Implemented automatic PID switching and channel detection

This commit is contained in:
Klaus Schmidinger 2004-01-04 12:30:00 +01:00
parent 3a1058fe1f
commit 8976ebcec5
31 changed files with 1069 additions and 300 deletions

View File

@ -216,6 +216,8 @@ Andreas Schultz <aschultz@warp10.net>
for implementing the TerrestrialDeliverySystemDescriptor in libdtv
for fixing setting the locking pid after a timed wait
for changing thread handling to make it work with NPTL ("Native Posix Thread Library")
for his 'autopid' patch which was helpful when implementing automatic
channel data gathering
Aaron Holtzman
for writing 'ac3dec'

25
HISTORY
View File

@ -2470,7 +2470,7 @@ Video Disk Recorder Revision History
- Final release of version 1.2.6.
2003-12-23: Version 1.3.0
2004-01-04: Version 1.3.0
- Changed thread handling to make it work with NPTL ("Native Posix Thread Library").
Thanks to Jon Burgess, Andreas Schultz, Werner Fink and Stefan Huelswitt.
@ -2482,6 +2482,7 @@ Video Disk Recorder Revision History
instead of explicit 'dsyslog()' calls inside their Action() function in order
to support logging the thread ids.
- Added "Slovak Link" and "Czech Link" to 'ca.conf' (thanks to Emil Petersky).
However, 'ca.conf' is now pretty much obsolete due to the automatic CA handling.
- Mutexes are now created with PTHREAD_MUTEX_ERRORCHECK_NP, which makes the
'lockingTid' stuff obsolete (thanks to Stefan Huelswitt).
- Changed font handling to allow language specific character sets.
@ -2499,7 +2500,7 @@ Video Disk Recorder Revision History
shortened to 'description'.
- Replaced 'libdtv' with 'libsi' (thanks to Marcel Wiesweg), which is thread
safe and can be used by multiple section filters simultaneously.
- Added 'cRWlock' to 'thread.[hc]'. Note that all plugin Makefiles need to
- Added 'cRwLock' to 'thread.[hc]'. Note that all plugin Makefiles need to
define _GNU_SOURCE for this to work (see the example plugin Makefiles and
'newplugin').
- Fixed a problem with crc32 in SI handling on 64bit systems (thanks to Pedro
@ -2512,3 +2513,23 @@ Video Disk Recorder Revision History
sections, depending on where they are found in the PMT (thanks to Hans-Peter
Raschke for reporting this one). This should make SkyCrypt CAMs work.
- Now using the 'version number' of EPG events to avoid unnecessary work.
- Channel data is now automatically derived from the DVB data stream (inspired
by the 'autopid' patch from Andreas Schultz).
- The current channel is now automatically re-tuned if the PIDs or other settings
change. If a recording is going on on a channel that has a change in its
settings, the recording will be stopped and immediately restarted to use the
new channel settings.
- EPG events now use the complete channel ID with NID, TID and SID.
- Channel names in 'channels.conf' can now have a short form, as provided
by some tv stations (see man vdr(5)). Currently channels that provide short
names in addition to long ones are listed in the OSD as "short,long name",
as in "RTL,RTL Television". The short names will be used explicitly later.
- The Ca parameter in 'channels.conf' has been extended and now contains all the
CA system ids for the given channel. When switching to a channel VDR now tests
for a device that provides one of these CA system ids. The devices automatically
get their supported ids from the CI handler.
- The values in 'ca.conf' are currently without any real meaning. Whether or not
a channel with conditional access can be received is now determined automatically
by evaluating its CA descriptors and comparing them to the CA system ids
provided by the installed CAM. Only the special values 1-16 are used to assign
a channel to a particular device.

View File

@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
# $Id: Makefile 1.61 2003/12/21 14:45:27 kls Exp $
# $Id: Makefile 1.62 2003/12/25 13:38:56 kls Exp $
.DELETE_ON_ERROR:
@ -36,7 +36,7 @@ SILIB = $(LSIDIR)/libsi.a
OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbosd.o\
dvbplayer.o dvbspu.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
lirc.o menu.o menuitems.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\
receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sections.o sources.o\
receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o sources.o\
spu.o status.o svdrp.o thread.o timers.o tools.o transfer.o vdr.o videodir.o
FIXFONT_ISO8859_1 = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1

View File

@ -12,3 +12,7 @@ VDR Plugin 'sky' Revision History
2003-05-09: Version 0.1.1
- Changed Start() to Initialize().
2004-01-04: Version 0.2.0
- Implemented automatic PID switching and channel detection

View File

@ -3,7 +3,7 @@
*
* See the README file for copyright information and how to reach the author.
*
* $Id: sky.c 1.3 2003/05/09 15:27:16 kls Exp $
* $Id: sky.c 1.4 2004/01/04 12:30:00 kls Exp $
*/
#include <sys/socket.h>
@ -14,7 +14,7 @@
#include <vdr/plugin.h>
#include <vdr/sources.h>
static const char *VERSION = "0.1.1";
static const char *VERSION = "0.2.0";
static const char *DESCRIPTION = "Sky Digibox interface";
// --- cDigiboxDevice --------------------------------------------------------
@ -37,6 +37,7 @@ public:
cDigiboxDevice(void);
virtual ~cDigiboxDevice();
virtual bool ProvidesSource(int Source) const;
virtual bool ProvidesTransponder(const cChannel *Channel) const;
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsSetChannel = NULL) const;
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
};
@ -137,13 +138,18 @@ bool cDigiboxDevice::ProvidesSource(int Source) const
return source == Source;
}
bool cDigiboxDevice::ProvidesTransponder(const cChannel *Channel) const
{
return false; // can't provide any actual transponder
}
bool cDigiboxDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
{
bool result = false;
bool hasPriority = Priority < 0 || Priority > this->Priority();
bool needsDetachReceivers = true;
if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel->Ca())) {
if (ProvidesSource(Channel->Source()) && Channel->Ca() == 0x30) {//XXX
if (Receiving()) {
if (digiboxChannelNumber == Channel->Frequency()) {
needsDetachReceivers = false;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: channels.c 1.16 2003/10/17 15:42:40 kls Exp $
* $Id: channels.c 1.17 2004/01/04 12:28:49 kls Exp $
*/
#include "channels.h"
@ -159,6 +159,7 @@ char *cChannel::buffer = NULL;
cChannel::cChannel(void)
{
memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__);
strcpy(name, "Pro7");
frequency = 12480;
source = cSource::FromString("S19.2E");
@ -170,7 +171,7 @@ cChannel::cChannel(void)
dpid1 = 257;
dpid2 = 0;
tpid = 32;
ca = 0;
caids[0] = 0;
nid = 0;
tid = 0;
sid = 888;
@ -186,6 +187,28 @@ cChannel::cChannel(void)
transmission = TRANSMISSION_MODE_AUTO;
guard = GUARD_INTERVAL_AUTO;
hierarchy = HIERARCHY_AUTO;
modification = CHANNELMOD_NONE;
}
cChannel::cChannel(const cChannel *Channel)
{
*this = *Channel;
*name = 0;
vpid = 0;
ppid = 0;
apid1 = 0;
apid2 = 0;
dpid1 = 0;
dpid2 = 0;
tpid = 0;
caids[0] = 0;
nid = 0;
tid = 0;
sid = 0;
rid = 0;
number = 0;
groupSep = false;
modification = CHANNELMOD_NONE;
}
cChannel& cChannel::operator= (const cChannel &Channel)
@ -194,16 +217,117 @@ cChannel& cChannel::operator= (const cChannel &Channel)
return *this;
}
static int MHz(int frequency)
int cChannel::Transponder(void) const
{
while (frequency > 20000)
frequency /= 1000;
return frequency;
int tf = frequency;
while (tf > 20000)
tf /= 1000;
return tf;
}
tChannelID cChannel::GetChannelID(void) const
{
return tChannelID(source, nid, nid ? tid : MHz(frequency), sid, rid);
return tChannelID(source, nid, nid ? tid : Transponder(), sid, rid);
}
int cChannel::Modification(int Mask)
{
int Result = modification & Mask;
modification = CHANNELMOD_NONE;
return Result;
}
void cChannel::SetId(int Nid, int Tid, int Sid, int Rid, bool Log)
{
if (nid != Nid || tid != Tid || sid != Sid || rid != Rid) {
if (Log)
dsyslog("changing id of channel %d from %d-%d-%d-%d to %d-%d-%d-%d", Number(), nid, tid, sid, rid, Nid, Tid, Sid, Rid);
nid = Nid;
tid = Tid;
sid = Sid;
rid = Rid;
modification |= CHANNELMOD_ID;
Channels.SetModified();
}
}
void cChannel::SetName(const char *Name, bool Log)
{
if (!isempty(Name) && strcmp(name, Name) != 0) {
if (Log)
dsyslog("changing name of channel %d from '%s' to '%s'", Number(), name, Name);
strn0cpy(name, Name, MaxChannelName);
modification |= CHANNELMOD_NAME;
Channels.SetModified();
}
}
void cChannel::SetPids(int Vpid, int Ppid, int Apid1, int Apid2, int Dpid1, int Dpid2, int Tpid)
{
//XXX if (vpid != Vpid || ppid != Ppid || apid1 != Apid1 || apid2 != Apid2 || dpid1 != Dpid1 || dpid2 != Dpid2 || tpid != Tpid) {
if (vpid != Vpid || ppid != Ppid || apid1 != Apid1 || (Apid2 && apid2 != Apid2) || dpid1 != Dpid1 || dpid2 != Dpid2 || tpid != Tpid) {
dsyslog("changing pids of channel %d from %d+%d:%d,%d;%d,%d:%d to %d+%d:%d,%d;%d,%d:%d", Number(), vpid, ppid, apid1, apid2, dpid1, dpid2, tpid, Vpid, Ppid, Apid1, Apid2, Dpid1, Dpid2, Tpid);
vpid = Vpid;
ppid = Ppid;
apid1 = Apid1;
if (Apid2)//XXX should we actually react here?
apid2 = Apid2;
dpid1 = Dpid1;
dpid2 = Dpid2;
tpid = Tpid;
modification |= CHANNELMOD_PIDS;
Channels.SetModified();
}
}
void cChannel::SetCaIds(const int *CaIds)
{
if (caids[0] && caids[0] <= 0x00FF)
return; // special values will not be overwritten
bool modified = false;
for (int i = 0; i < MAXCAIDS; i++) {
if (caids[i] != CaIds[i]) {
modified = true;
break;
}
if (!caids[i] || !CaIds[i])
break;
}
if (modified) {
char OldCaIdsBuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia
char NewCaIdsBuf[MAXCAIDS * 5 + 10];
char *qo = OldCaIdsBuf;
char *qn = NewCaIdsBuf;
int i;
for (i = 0; i < MAXCAIDS; i++) {
if (i == 0 || caids[i])
qo += snprintf(qo, sizeof(OldCaIdsBuf), "%s%X", i > 0 ? "," : "", caids[i]);
if (!caids[i])
break;
}
for (i = 0; i < MAXCAIDS; i++) {
if (i == 0 || CaIds[i])
qn += snprintf(qn, sizeof(NewCaIdsBuf), "%s%X", i > 0 ? "," : "", CaIds[i]);
caids[i] = CaIds[i];
if (!CaIds[i])
break;
}
caids[i] = 0;
*qo = *qn = 0;
dsyslog("changing caids of channel %d from %s to %s", Number(), OldCaIdsBuf, NewCaIdsBuf);
modification |= CHANNELMOD_CA;
Channels.SetModified();
}
}
void cChannel::SetCaDescriptors(int Level)
{
if (Level > 0) {
modification |= CHANNELMOD_CA;
Channels.SetModified();
if (Level > 1)
dsyslog("changing ca descriptors of channel %d", Number());
}
}
static int PrintParameter(char *p, char Name, int Value)
@ -290,10 +414,10 @@ const char *cChannel::ToText(cChannel *Channel)
char vpidbuf[32];
char *q = vpidbuf;
q += snprintf(q, sizeof(vpidbuf), "%d", Channel->vpid);
if (Channel->ppid)
if (Channel->ppid && Channel->ppid != Channel->vpid)
q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "+%d", Channel->ppid);
*q = 0;
char apidbuf[32];
char apidbuf[MAXAPIDS * 2 * 6 + 10]; // 2: Apids and Dpids, 6: 5 digits plus delimiting ',' or ';', 10: paranoia
q = apidbuf;
q += snprintf(q, sizeof(apidbuf), "%d", Channel->apid1);
if (Channel->apid2)
@ -303,7 +427,16 @@ const char *cChannel::ToText(cChannel *Channel)
if (Channel->dpid2)
q += snprintf(q, sizeof(apidbuf) - (q - apidbuf), ",%d", Channel->dpid2);
*q = 0;
asprintf(&buffer, "%s:%d:%s:%s:%d:%s:%s:%d:%d:%d:%d:%d:%d\n", s, Channel->frequency, Channel->ParametersToString(), cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, Channel->tpid, Channel->ca, Channel->sid, Channel->nid, Channel->tid, Channel->rid);
char caidbuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia
q = caidbuf;
for (int i = 0; i < MAXCAIDS; i++) {
if (i == 0 || Channel->caids[i])
q += snprintf(q, sizeof(caidbuf), "%s%X", i > 0 ? "," : "", Channel->caids[i]);
if (!Channel->caids[i])
break;
}
*q = 0;
asprintf(&buffer, "%s:%d:%s:%s:%d:%s:%s:%d:%s:%d:%d:%d:%d\n", s, Channel->frequency, Channel->ParametersToString(), cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, Channel->tpid, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid);
}
return buffer;
}
@ -336,12 +469,16 @@ bool cChannel::Parse(const char *s, bool AllowNonUniqueID)
char *parambuf = NULL;
char *vpidbuf = NULL;
char *apidbuf = NULL;
int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%d :%d :%d :%d :%d :%d ", &namebuf, &frequency, &parambuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpid, &ca, &sid, &nid, &tid, &rid);
char *caidbuf = NULL;
int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%d :%a[^:]:%d :%d :%d :%d ", &namebuf, &frequency, &parambuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpid, &caidbuf, &sid, &nid, &tid, &rid);
if (fields >= 9) {
if (fields == 9) {
// allow reading of old format
sid = ca;
ca = tpid;
sid = atoi(caidbuf);
delete caidbuf;
caidbuf = NULL;
caids[0] = tpid;
caids[1] = 0;
tpid = 0;
}
vpid = ppid = 0;
@ -350,24 +487,46 @@ bool cChannel::Parse(const char *s, bool AllowNonUniqueID)
ok = false;
if (parambuf && sourcebuf && vpidbuf && apidbuf) {
ok = StringToParameters(parambuf) && (source = cSource::FromString(sourcebuf)) >= 0;
char *p = strchr(vpidbuf, '+');
if (p)
*p++ = 0;
sscanf(vpidbuf, "%d", &vpid);
if (p)
sscanf(p, "%d", &ppid);
else
ppid = vpid;
p = strchr(apidbuf, ';');
if (p)
*p++ = 0;
sscanf(apidbuf, "%d ,%d ", &apid1, &apid2);
if (p)
sscanf(p, "%d ,%d ", &dpid1, &dpid2);
if (caidbuf) {
char *p = caidbuf;
char *q;
int NumCaIds = 0;
while ((q = strtok(p, ",")) != NULL) {
if (NumCaIds < MAXCAIDS) {
caids[NumCaIds++] = strtol(q, NULL, 16) & 0xFFFF;
if (NumCaIds == 1 && caids[0] <= 0x00FF)
break;
}
else
esyslog("ERROR: too many CA ids!"); // no need to set ok to 'false'
p = NULL;
}
caids[NumCaIds] = 0;
}
}
strn0cpy(name, namebuf, MaxChannelName);
free(parambuf);
free(sourcebuf);
free(vpidbuf);
free(apidbuf);
free(caidbuf);
free(namebuf);
if (!GetChannelID().Valid()) {
esyslog("ERROR: channel data results in invalid ID!");
@ -394,6 +553,12 @@ bool cChannel::Save(FILE *f)
cChannels Channels;
cChannels::cChannels(void)
{
maxNumber = 0;
modified = false;
}
bool cChannels::Load(const char *FileName, bool AllowComments, bool MustExist)
{
if (cConfig<cChannel>::Load(FileName, AllowComments, MustExist)) {
@ -457,10 +622,10 @@ cChannel *cChannels::GetByNumber(int Number, int SkipGap)
return NULL;
}
cChannel *cChannels::GetByServiceID(int Source, unsigned short ServiceID)
cChannel *cChannels::GetByServiceID(int Source, int Transponder, unsigned short ServiceID)
{
for (cChannel *channel = First(); channel; channel = Next(channel)) {
if (!channel->GroupSep() && channel->Source() == Source && channel->Sid() == ServiceID)
if (!channel->GroupSep() && channel->Source() == Source && ISTRANSPONDER(channel->Transponder(), Transponder) && channel->Sid() == ServiceID)
return channel;
}
return NULL;
@ -497,3 +662,31 @@ bool cChannels::SwitchTo(int Number)
cChannel *channel = GetByNumber(Number);
return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true);
}
void cChannels::SetModified(void)
{
modified = true;
}
bool cChannels::Modified(void)
{
bool Result = modified;
modified = false;
return Result;
}
cChannel *cChannels::NewChannel(int Source, int Transponder, const char *Name, int Nid, int Tid, int Sid, int Rid)
{
dsyslog("creating new channel '%s' on %s transponder %d with id %d-%d-%d-%d", Name, cSource::ToString(Source), Transponder, Nid, Tid, Sid, Rid);
for (cChannel *channel = First(); channel; channel = Next(channel)) {
if (!channel->GroupSep() && channel->Source() == Source && ISTRANSPONDER(channel->Transponder(), Transponder)) {
cChannel *NewChannel = new cChannel(channel);
Add(NewChannel);
ReNumber();
NewChannel->SetId(Nid, Tid, Sid, Rid, false);
NewChannel->SetName(Name, false);
return NewChannel;
}
}
return NULL;
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: channels.h 1.9 2003/10/26 13:32:00 kls Exp $
* $Id: channels.h 1.10 2004/01/04 12:26:37 kls Exp $
*/
#ifndef __CHANNELS_H
@ -12,10 +12,22 @@
#include "config.h"
#include "sources.h"
#include "thread.h"
#include "tools.h"
#define ISTRANSPONDER(f1, f2) (abs((f1) - (f2)) < 4) //XXX
#define CHANNELMOD_NONE 0x00
#define CHANNELMOD_ALL 0xFF
#define CHANNELMOD_NAME 0x01
#define CHANNELMOD_PIDS 0x02
#define CHANNELMOD_ID 0x04
#define CHANNELMOD_CA 0x10
#define CHANNELMOD_RETUNE (CHANNELMOD_PIDS | CHANNELMOD_CA)
#define MAXAPIDS 2
#define MAXCAIDS 8
struct tChannelParameterMap {
int userValue;
int driverValue;
@ -46,7 +58,7 @@ public:
tChannelID(void) { source = nid = tid = sid = rid = 0; }
tChannelID(int Source, int Nid, int Tid, int Sid, int Rid = 0) { source = Source; nid = Nid; tid = Tid; sid = Sid; rid = Rid; }
bool operator== (const tChannelID &arg) const;
bool Valid(void) { return tid && sid; } // nid and rid are optional and source may be 0
bool Valid(void) { return tid && sid; } // nid and rid are optional and source may be 0//XXX source may not be 0???
tChannelID &ClrRid(void) { rid = 0; return *this; }
static tChannelID FromString(const char *s);
const char *ToString(void);
@ -58,7 +70,7 @@ class cChannel : public cListObject {
private:
static char *buffer;
static const char *ToText(cChannel *Channel);
enum { MaxChannelName = 32 }; // 31 chars + terminating 0!
enum { MaxChannelName = 64 }; // 63 chars + terminating 0!
int __BeginData__;
char name[MaxChannelName];
int frequency; // MHz
@ -69,7 +81,7 @@ private:
int apid1, apid2;
int dpid1, dpid2;
int tpid;
int ca;
int caids[MAXCAIDS + 1]; // list is zero-terminated
int nid;
int tid;
int sid;
@ -86,16 +98,19 @@ private:
int guard;
int hierarchy;
int __EndData__;
int modification;
const char *ParametersToString(void);
bool StringToParameters(const char *s);
public:
cChannel(void);
cChannel(const cChannel *Channel);
cChannel& operator= (const cChannel &Channel);
const char *ToText(void);
bool Parse(const char *s, bool AllowNonUniqueID = false);
bool Save(FILE *f);
const char *Name(void) const { return name; }
int Frequency(void) const { return frequency; }
int Frequency(void) const { return frequency; } ///< Returns the actual frequency, as given in 'channels.conf'
int Transponder(void) const; ///< Returns the transponder frequency in MHz
int Source(void) const { return source; }
int Srate(void) const { return srate; }
int Vpid(void) const { return vpid; }
@ -105,8 +120,11 @@ public:
int Dpid1(void) const { return dpid1; }
int Dpid2(void) const { return dpid2; }
int Tpid(void) const { return tpid; }
int Ca(void) const { return ca; }
int Ca(int Index = 0) const { return Index < MAXCAIDS ? caids[Index] : 0; }
int Nid(void) const { return nid; }
int Tid(void) const { return tid; }
int Sid(void) const { return sid; }
int Rid(void) const { return rid; }
int Number(void) const { return number; }
void SetNumber(int Number) { number = Number; }
bool GroupSep(void) const { return groupSep; }
@ -123,24 +141,38 @@ public:
bool IsSat(void) const { return (source & cSource::st_Mask) == cSource::stSat; }
bool IsTerr(void) const { return (source & cSource::st_Mask) == cSource::stTerr; }
tChannelID GetChannelID(void) const;
int Modification(int Mask = CHANNELMOD_ALL);
void SetId(int Nid, int Tid, int Sid, int Rid = 0, bool Log = true);
void SetName(const char *Name, bool Log = true);
void SetPids(int Vpid, int Ppid, int Apid1, int Apid2, int Dpid1, int Dpid2, int Tpid);
void SetCaIds(const int *CaIds); // list must be zero-terminated
void SetCaDescriptors(int Level);
};
class cChannels : public cConfig<cChannel> {
protected:
class cChannels : public cRwLock, public cConfig<cChannel> {
private:
int maxNumber;
bool modified;
int beingEdited;
public:
cChannels(void) { maxNumber = 0; }
cChannels(void);
virtual bool Load(const char *FileName, bool AllowComments = false, bool MustExist = false);
int GetNextGroup(int Idx); // Get next channel group
int GetPrevGroup(int Idx); // Get previous channel group
int GetNextNormal(int Idx); // Get next normal channel (not group)
void ReNumber(void); // Recalculate 'number' based on channel type
cChannel *GetByNumber(int Number, int SkipGap = 0);
cChannel *GetByServiceID(int Source, unsigned short ServiceID);
cChannel *GetByServiceID(int Source, int Transponder, unsigned short ServiceID);
cChannel *GetByChannelID(tChannelID ChannelID, bool TryWithoutRid = false);
int BeingEdited(void) { return beingEdited; }
void IncBeingEdited(void) { beingEdited++; }
void DecBeingEdited(void) { beingEdited--; }
bool HasUniqueChannelID(cChannel *NewChannel, cChannel *OldChannel = NULL);
bool SwitchTo(int Number);
int MaxNumber(void) { return maxNumber; }
void SetModified(void);
bool Modified(void);
cChannel *NewChannel(int Source, int Transponder, const char *Name, int Nid, int Tid, int Sid, int Rid = 0);
};
extern cChannels Channels;

23
ci.c
View File

@ -4,13 +4,9 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: ci.c 1.20 2003/12/24 10:23:24 kls Exp $
* $Id: ci.c 1.21 2004/01/02 15:07:36 kls Exp $
*/
/* XXX TODO
- update CA descriptors in case they change
XXX*/
#include "ci.h"
#include <asm/unaligned.h>
#include <ctype.h>
@ -1570,6 +1566,23 @@ const unsigned short *cCiHandler::GetCaSystemIds(int Slot)
return cas ? cas->GetCaSystemIds() : NULL;
}
bool cCiHandler::ProvidesCa(const unsigned short *CaSystemIds)
{
cMutexLock MutexLock(&mutex);
for (int Slot = 0; Slot < numSlots; Slot++) {
cCiConditionalAccessSupport *cas = (cCiConditionalAccessSupport *)GetSessionByResourceId(RI_CONDITIONAL_ACCESS_SUPPORT, Slot);
if (cas) {
for (const unsigned short *ids = cas->GetCaSystemIds(); ids && *ids; ids++) {
for (const unsigned short *id = CaSystemIds; *id; id++) {
if (*id == *ids)
return true;
}
}
}
}
return false;
}
bool cCiHandler::SetCaPmt(cCiCaPmt &CaPmt, int Slot)
{
cMutexLock MutexLock(&mutex);

3
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 1.11 2003/12/24 10:05:46 kls Exp $
* $Id: ci.h 1.12 2003/12/31 13:49:49 kls Exp $
*/
#ifndef __CI_H
@ -111,6 +111,7 @@ public:
cCiMenu *GetMenu(void);
cCiEnquiry *GetEnquiry(void);
const unsigned short *GetCaSystemIds(int Slot);
bool ProvidesCa(const unsigned short *CaSystemIds); //XXX Slot???
bool SetCaPmt(cCiCaPmt &CaPmt, int Slot);
bool Reset(int Slot);
};

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 1.177 2003/10/18 11:14:33 kls Exp $
* $Id: config.h 1.178 2003/12/27 13:57:56 kls Exp $
*/
#ifndef __CONFIG_H
@ -87,7 +87,7 @@ public:
cConfig(void) { fileName = NULL; }
virtual ~cConfig() { free(fileName); }
const char *FileName(void) { return fileName; }
bool Load(const char *FileName = NULL, bool AllowComments = false, bool MustExist = false)
virtual bool Load(const char *FileName = NULL, bool AllowComments = false, bool MustExist = false)
{
Clear();
if (FileName) {

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 1.50 2003/12/22 10:53:45 kls Exp $
* $Id: device.c 1.51 2004/01/04 11:30:05 kls Exp $
*/
#include "device.h"
@ -47,6 +47,7 @@ cDevice::cDevice(void)
sectionHandler = NULL;
eitFilter = NULL;
patFilter = NULL;
sdtFilter = NULL;
ciHandler = NULL;
player = NULL;
@ -68,8 +69,9 @@ cDevice::~cDevice()
for (int i = 0; i < MAXRECEIVERS; i++)
Detach(receiver[i]);
delete ciHandler;
delete eitFilter;
delete sdtFilter;
delete patFilter;
delete eitFilter;
delete sectionHandler;
}
@ -157,7 +159,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDe
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basicly able to do the job
if (device[i]->Receiving() && !ndr)
pri = 0; // receiving and allows additional receivers
else if (d && !device[i]->Receiving() && device[i]->ProvidesCa(Channel->Ca()) < d->ProvidesCa(Channel->Ca()))
else if (d && !device[i]->Receiving() && device[i]->ProvidesCa(Channel) < d->ProvidesCa(Channel))
pri = 1; // free and fewer Ca's
else if (!device[i]->Receiving() && !device[i]->IsPrimaryDevice())
pri = 2; // free and not the primary device
@ -165,7 +167,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool *NeedsDe
pri = 3; // free
else if (d && device[i]->Priority() < d->Priority())
pri = 4; // receiving but priority is lower
else if (d && device[i]->Priority() == d->Priority() && device[i]->ProvidesCa(Channel->Ca()) < d->ProvidesCa(Channel->Ca()))
else if (d && device[i]->Priority() == d->Priority() && device[i]->ProvidesCa(Channel) < d->ProvidesCa(Channel))
pri = 5; // receiving with same priority but fewer Ca's
else
pri = 6; // all others
@ -325,6 +327,7 @@ void cDevice::StartSectionHandler(void)
sectionHandler = new cSectionHandler(this);
AttachFilter(eitFilter = new cEitFilter);
AttachFilter(patFilter = new cPatFilter);
AttachFilter(sdtFilter = new cSdtFilter(patFilter));
sectionHandler->SetStatus(true);
}
}
@ -349,6 +352,11 @@ bool cDevice::ProvidesSource(int Source) const
return false;
}
bool cDevice::ProvidesTransponder(const cChannel *Channel) const
{
return false;
}
bool cDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
{
return false;
@ -431,6 +439,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
Result = scrNotAvailable;
}
else {
Channels.Lock(false);
cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
// Stop section handling:
if (sectionHandler) {
@ -440,12 +449,13 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
if (SetChannelDevice(Channel, LiveView)) {
// Start section handling:
if (sectionHandler) {
sectionHandler->SetSource(Channel->Source(), Channel->Frequency());
sectionHandler->SetSource(Channel->Source(), Channel->Transponder());
sectionHandler->SetStatus(true);
}
}
else
Result = scrFailed;
Channels.Unlock();
}
if (Result == scrOk) {
@ -462,6 +472,11 @@ bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
return false;
}
bool cDevice::HasLock(void)
{
return true;
}
bool cDevice::HasProgramme(void)
{
return Replaying() || pidHandles[ptAudio].pid || pidHandles[ptVideo].pid;
@ -651,6 +666,7 @@ int cDevice::Priority(void) const
int cDevice::CanShift(int Ca, int Priority, int UsedCards) const
{
return -1;//XXX+ too complex with multiple recordings per device
/*XXX
// Test whether a receiver on this device can be shifted to another one
// in order to perform a new receiving with the given Ca and Priority on this device:
int ShiftLevel = -1; // default means this device can't be shifted
@ -681,25 +697,17 @@ int cDevice::CanShift(int Ca, int Priority, int UsedCards) const
else if (Priority > this->Priority())
ShiftLevel = 0; // no shifting necessary, this device can do the job
return ShiftLevel;
XXX*/
}
int cDevice::ProvidesCa(int Ca) const
int cDevice::ProvidesCa(const cChannel *Channel) const
{
int Ca = Channel->Ca();
if (Ca == CardIndex() + 1)
return 1; // exactly _this_ card was requested
if (Ca && Ca <= MAXDEVICES)
return 0; // a specific card was requested, but not _this_ one
int result = Ca ? 0 : 1; // by default every card can provide FTA
int others = Ca ? 1 : 0;
for (int i = 0; i < MAXCACAPS; i++) {
if (caCaps[i]) {
if (caCaps[i] == Ca)
result = 1;
else
others++;
}
}
return result ? result + others : 0;
return !Ca; // by default every card can provide FTA
}
bool cDevice::Receiving(bool CheckAny) const

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.h 1.36 2003/12/22 10:52:39 kls Exp $
* $Id: device.h 1.37 2004/01/04 11:52:00 kls Exp $
*/
#ifndef __DEVICE_H
@ -14,6 +14,7 @@
#include "eit.h"
#include "filter.h"
#include "pat.h"
#include "sdt.h"
#include "sections.h"
#include "thread.h"
#include "tools.h"
@ -131,7 +132,8 @@ public:
///< Returns the card index of this device (0 ... MAXDEVICES - 1).
int DeviceNumber(void) const;
///< Returns the number of this device (0 ... MAXDEVICES - 1).
int ProvidesCa(int Ca) const;
virtual int ProvidesCa(const cChannel *Channel) const;//XXX PLUGINS.html!!!
//XXX describe changed functionality!!!
///< Checks whether this device provides the given value in its
///< caCaps. Returns 0 if the value is not provided, 1 if only this
///< value is provided, and > 1 if this and other values are provided.
@ -161,6 +163,8 @@ protected:
public:
virtual bool ProvidesSource(int Source) const;
///< Returns true if this device can provide the given source.
virtual bool ProvidesTransponder(const cChannel *Channel) const;
///< XXX -> PLUGINS.html!
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;
///< Returns true if this device can provide the given channel.
///< In case the device has cReceivers attached to it or it is the primary
@ -192,6 +196,10 @@ protected:
public:
static int CurrentChannel(void) { return primaryDevice ? currentChannel : 0; }
///< Returns the number of the current channel on the primary device.
virtual bool HasLock(void);//XXX PLUGINS.html
///< Returns true if the device has a lock on the requested transponder.
///< Default is true, a specific device implementation may return false
///< to indicate that it is not ready yet.
virtual bool HasProgramme(void);
///< Returns true if the device is currently showing any programme to
///< the user, either through replaying or live.
@ -232,6 +240,7 @@ private:
cSectionHandler *sectionHandler;
cEitFilter *eitFilter;
cPatFilter *patFilter;
cSdtFilter *sdtFilter;
protected:
void StartSectionHandler(void);
///< A derived device that provides section data must call

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.c 1.75 2003/12/24 09:57:29 kls Exp $
* $Id: dvbdevice.c 1.76 2004/01/04 12:28:00 kls Exp $
*/
#include "dvbdevice.h"
@ -86,7 +86,7 @@ public:
virtual ~cDvbTuner();
bool IsTunedTo(const cChannel *Channel) const;
void Set(const cChannel *Channel, bool Tune, bool UseCa);
bool Locked(void) { return tunerStatus == tsLocked; }
bool Locked(void) { return tunerStatus >= tsLocked; }
};
cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_type_t FrontendType, cCiHandler *CiHandler)
@ -114,19 +114,18 @@ cDvbTuner::~cDvbTuner()
bool cDvbTuner::IsTunedTo(const cChannel *Channel) const
{
return tunerStatus != tsIdle && channel.Source() == Channel->Source() && channel.Frequency() == Channel->Frequency();
return tunerStatus != tsIdle && channel.Source() == Channel->Source() && channel.Transponder() == Channel->Transponder();
}
void cDvbTuner::Set(const cChannel *Channel, bool Tune, bool UseCa)
{
cMutexLock MutexLock(&mutex);
bool CaChange = !(Channel->GetChannelID() == channel.GetChannelID());
if (Tune)
tunerStatus = tsSet;
else if (tunerStatus == tsCam && CaChange)
else if (tunerStatus == tsCam)
tunerStatus = tsTuned;
useCa = UseCa;
if (Channel->Ca() && CaChange)
if (Channel->Ca() && tunerStatus != tsCam)
startTime = time(NULL);
channel = *Channel;
newSet.Broadcast();
@ -268,29 +267,27 @@ void cDvbTuner::Action(void)
continue;
}
}
if (tunerStatus >= tsLocked) {
if (ciHandler) {
if (ciHandler->Process() && useCa) {
if (tunerStatus != tsCam) {//XXX TODO update in case the CA descriptors have changed
for (int Slot = 0; Slot < ciHandler->NumSlots(); Slot++) {
cCiCaPmt CaPmt(channel.Source(), channel.Frequency(), channel.Sid(), ciHandler->GetCaSystemIds(Slot));
if (CaPmt.Valid()) {
CaPmt.AddPid(channel.Vpid(), 2);
CaPmt.AddPid(channel.Apid1(), 4);
CaPmt.AddPid(channel.Apid2(), 4);
CaPmt.AddPid(channel.Dpid1(), 0);
if (ciHandler->SetCaPmt(CaPmt, Slot)) {
tunerStatus = tsCam;
startTime = 0;
}
}
}
if (ciHandler) {
if (ciHandler->Process() && useCa) {
if (tunerStatus == tsLocked) {
for (int Slot = 0; Slot < ciHandler->NumSlots(); Slot++) {
cCiCaPmt CaPmt(channel.Source(), channel.Frequency(), channel.Sid(), ciHandler->GetCaSystemIds(Slot));
if (CaPmt.Valid()) {
CaPmt.AddPid(channel.Vpid(), 2);
CaPmt.AddPid(channel.Apid1(), 4);
CaPmt.AddPid(channel.Apid2(), 4);
CaPmt.AddPid(channel.Dpid1(), 0);
if (ciHandler->SetCaPmt(CaPmt, Slot)) {
tunerStatus = tsCam;
startTime = 0;
}
}
}
else
tunerStatus = tsLocked;
}
}
}
}
else if (tunerStatus > tsLocked)
tunerStatus = tsLocked;
}
// in the beginning we loop more often to let the CAM connection start up fast
newSet.TimedWait(mutex, (ciHandler && (time(NULL) - startTime < 20)) ? 100 : 1000);
@ -436,6 +433,17 @@ bool cDvbDevice::HasDecoder(void) const
return fd_video >= 0 && fd_audio >= 0;
}
int cDvbDevice::ProvidesCa(const cChannel *Channel) const
{
if (Channel->Ca() >= 0x0100 && ciHandler) {
unsigned short ids[MAXCAIDS + 1];
for (int i = 0; i <= MAXCAIDS; i++) // '<=' copies the terminating 0!
ids[i] = Channel->Ca(i);
return ciHandler->ProvidesCa(ids);
}
return cDevice::ProvidesCa(Channel);
}
cOsdBase *cDvbDevice::NewOsd(int x, int y)
{
return new cDvbOsd(x, y);
@ -661,13 +669,18 @@ bool cDvbDevice::ProvidesSource(int Source) const
return true;
}
bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const
{
return ProvidesSource(Channel->Source()) && ((Channel->Source() & cSource::st_Mask) != cSource::stSat || Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization()));
}
bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
{
bool result = false;
bool hasPriority = Priority < 0 || Priority > this->Priority();
bool needsDetachReceivers = false;
if (ProvidesSource(Channel->Source()) && ProvidesCa(Channel->Ca())) {
if ((Channel->Vpid() || Channel->Apid1()) && ProvidesSource(Channel->Source()) && ProvidesCa(Channel)) {
result = hasPriority;
if (Priority >= 0 && Receiving()) {
if (dvbTuner->IsTunedTo(Channel)) {
@ -736,15 +749,14 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
if (TurnOffLivePIDs)
TurnOffLiveMode();
dvbTuner->Set(Channel, DoTune, !EITScanner.UsesDevice(this)); //XXX 1.3: this is an ugly hack - find a cleaner solution
dvbTuner->Set(Channel, DoTune, !EITScanner.UsesDevice(this)); //XXX 1.3: this is an ugly hack - find a cleaner solution//XXX
// PID settings:
if (TurnOnLivePIDs) {
aPid1 = Channel->Apid1();
aPid2 = Channel->Apid2();
int pPid = Channel->Ppid() ? Channel->Ppid() : Channel->Vpid();
if (!(AddPid(pPid, ptPcr) && AddPid(Channel->Apid1(), ptAudio) && AddPid(Channel->Vpid(), ptVideo))) {//XXX+ dolby dpid1!!! (if audio plugins are attached)
if (!(AddPid(Channel->Ppid(), ptPcr) && AddPid(Channel->Apid1(), ptAudio) && AddPid(Channel->Vpid(), ptVideo))) {//XXX+ dolby dpid1!!! (if audio plugins are attached)
esyslog("ERROR: failed to set PIDs for channel %d on device %d", Channel->Number(), CardIndex() + 1);
return false;
}
@ -758,6 +770,11 @@ bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
return true;
}
bool cDvbDevice::HasLock(void)
{
return dvbTuner->Locked();
}
void cDvbDevice::SetVolumeDevice(int Volume)
{
if (HasDecoder()) {

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.h 1.25 2003/12/21 14:04:00 kls Exp $
* $Id: dvbdevice.h 1.26 2004/01/03 10:21:50 kls Exp $
*/
#ifndef __DVBDEVICE_H
@ -44,6 +44,7 @@ protected:
public:
cDvbDevice(int n);
virtual ~cDvbDevice();
virtual int ProvidesCa(const cChannel *Channel) const;
virtual bool HasDecoder(void) const;
// OSD facilities
@ -61,9 +62,12 @@ private:
void TurnOffLiveMode(void);
public:
virtual bool ProvidesSource(int Source) const;
virtual bool ProvidesTransponder(const cChannel *Channel) const;
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;
protected:
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
public:
virtual bool HasLock(void);
// PID handle facilities

10
eit.c
View File

@ -8,7 +8,7 @@
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
* Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>.
*
* $Id: eit.c 1.83 2003/12/25 12:48:47 kls Exp $
* $Id: eit.c 1.84 2004/01/02 22:27:29 kls Exp $
*/
#include "eit.h"
@ -29,12 +29,10 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)
if (!CheckCRCAndParse())
return;
//XXX TODO use complete channel ID
cChannel *channel = Channels.GetByServiceID(Source, getServiceId());
tChannelID channelID(Source, getOriginalNetworkId(), getTransportStreamId(), getServiceId());
cChannel *channel = Channels.GetByChannelID(channelID, true);
if (!channel)
return; // only collect data for known channels
tChannelID channelID = channel->GetChannelID();
channelID.ClrRid();
cEvent *rEvent = NULL;
@ -82,7 +80,7 @@ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data)
// Unfortunately some stations (like, e.g. "Premiere") broadcast their EPG data on several transponders (like
// the actual Premiere transponder and the Sat.1/Pro7 transponder), but use different version numbers on
// each of them :-( So if one DVB card is tuned to the Premiere transponder, while an other one is tuned
// to the Sat.1/Pro7 transponder, events will keep toggling because ot the bogus version numbers.
// to the Sat.1/Pro7 transponder, events will keep toggling because of the bogus version numbers.
if (Tid == pEvent->TableID() && pEvent->Version() == getVersionNumber())
continue;
}

144
eitscan.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: eitscan.c 1.14 2003/09/06 13:06:13 kls Exp $
* $Id: eitscan.c 1.15 2004/01/04 12:28:00 kls Exp $
*/
#include "eitscan.h"
@ -12,6 +12,68 @@
#include "channels.h"
#include "dvbdevice.h"
// --- cScanData -------------------------------------------------------------
class cScanData : public cListObject {
private:
int source;
int transponder;
public:
cScanData(int Source, int Transponder);
virtual bool operator< (const cListObject &ListObject);
int Source(void) { return source; }
int Transponder(void) { return transponder; }
cChannel *GetChannel(void);
};
cScanData::cScanData(int Source, int Transponder)
{
source = Source;
transponder = Transponder;
}
bool cScanData::operator< (const cListObject &ListObject)
{
cScanData *sd = (cScanData *)&ListObject;
return source < sd->source || source == sd->source && transponder < sd->transponder;
}
//XXX this might be done differently later...
cChannel *cScanData::GetChannel(void)
{
for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
if (!Channel->GroupSep() && Channel->Source() == source && ISTRANSPONDER(Channel->Transponder(), transponder))
return Channel;
}
return NULL;
}
// --- cScanList -------------------------------------------------------------
class cScanList : public cList<cScanData> {
public:
cScanList(void);
void AddTransponder(const cChannel *Channel);
};
cScanList::cScanList(void)
{
for (cChannel *ch = Channels.First(); ch; ch = Channels.Next(ch))
AddTransponder(ch);
Sort();
}
void cScanList::AddTransponder(const cChannel *Channel)
{
for (cScanData *sd = First(); sd; sd = Next(sd)) {
if (sd->Source() == Channel->Source() && sd->Transponder() == Channel->Transponder())
return;
}
Add(new cScanData(Channel->Source(), Channel->Transponder()));
}
// --- cEITScanner -----------------------------------------------------------
cEITScanner EITScanner;
cEITScanner::cEITScanner(void)
@ -20,24 +82,12 @@ cEITScanner::cEITScanner(void)
currentDevice = NULL;
currentChannel = 0;
memset(lastChannel, 0, sizeof(lastChannel));
numTransponders = 0;
transponders = NULL;
scanList = NULL;
}
cEITScanner::~cEITScanner()
{
free(transponders);
}
bool cEITScanner::TransponderScanned(cChannel *Channel)
{
for (int i = 0; i < numTransponders; i++) {
if (transponders[i] == Channel->Frequency())
return true;
}
transponders = (int *)realloc(transponders, ++numTransponders * sizeof(int));
transponders[numTransponders - 1] = Channel->Frequency();
return false;
delete scanList;
}
void cEITScanner::Activity(void)
@ -54,37 +104,51 @@ void cEITScanner::Process(void)
if (Setup.EPGScanTimeout && Channels.MaxNumber() > 1) {
time_t now = time(NULL);
if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) {
for (int i = 0; i < cDevice::NumDevices(); i++) {
cDevice *Device = cDevice::GetDevice(i);
if (Device && Device->CardIndex() < MAXDVBDEVICES) {
if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
if (!(Device->Receiving(true) || Device->Replaying())) {
for (;;) {
cChannel *Channel = Channels.GetByNumber(lastChannel[Device->DeviceNumber()] + 1, 1);
if (Channel) {
lastChannel[Device->DeviceNumber()] = Channel->Number();
if (Channel->Sid() && Device->ProvidesChannel(Channel) && !TransponderScanned(Channel)) {
if (Device == cDevice::PrimaryDevice() && !currentChannel) {
currentChannel = Device->CurrentChannel();
if (Channels.Lock(false, 10)) {
if (!scanList)
scanList = new cScanList();
for (bool AnyDeviceSwitched = false; !AnyDeviceSwitched; ) {
cScanData *ScanData = NULL;
for (int i = 0; i < cDevice::NumDevices(); i++) {
cDevice *Device = cDevice::GetDevice(i);
if (Device) {
if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
if (!(Device->Receiving(true) || Device->Replaying())) {
if (!ScanData)
ScanData = scanList->First();
if (ScanData) {
cChannel *Channel = ScanData->GetChannel();
//XXX if (Device->ProvidesTransponder(Channel)) {
if ((!Channel->Ca() || Channel->Ca() == Device->DeviceNumber() + 1 || Channel->Ca() >= 0x0100) && Device->ProvidesTransponder(Channel)) { //XXX temporary for the 'sky' plugin
if (Device == cDevice::PrimaryDevice() && !currentChannel)
currentChannel = Device->CurrentChannel();
currentDevice = Device;//XXX see also dvbdevice.c!!!
Device->SwitchChannel(Channel, false);
currentDevice = NULL;
scanList->Del(ScanData);
ScanData = NULL;
AnyDeviceSwitched = true;
}
currentDevice = Device;
Device->SwitchChannel(Channel, false);
currentDevice = NULL;
break;
}
}
else {
if (lastChannel[Device->DeviceNumber()])
numTransponders = 0;
lastChannel[Device->DeviceNumber()] = 0;
break;
else
break;
}
}
}
}
}
if (ScanData && !AnyDeviceSwitched) {
scanList->Del(ScanData);
ScanData = NULL;
}
if (!scanList->Count()) {
delete scanList;
scanList = NULL;
break;
}
}
}
lastScan = time(NULL);
Channels.Unlock();
lastScan = time(NULL);
}
}
}
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: eitscan.h 1.4 2003/09/06 13:05:51 kls Exp $
* $Id: eitscan.h 1.5 2004/01/03 13:08:39 kls Exp $
*/
#ifndef __EITSCAN_H
@ -13,6 +13,8 @@
#include <time.h>
#include "config.h"
class cScanList;
class cEITScanner {
private:
enum { ActivityTimeout = 60,
@ -22,8 +24,7 @@ private:
cDevice *currentDevice;
int currentChannel;
int lastChannel[MAXDEVICES];
int numTransponders, *transponders;
bool TransponderScanned(cChannel *Channel);
cScanList *scanList;
public:
cEITScanner(void);
~cEITScanner();

4
epg.h
View File

@ -7,7 +7,7 @@
* Original version (as used in VDR before 1.3.0) written by
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
*
* $Id: epg.h 1.2 2003/12/24 13:20:35 kls Exp $
* $Id: epg.h 1.3 2004/01/03 17:00:25 kls Exp $
*/
#ifndef __EPG_H
@ -110,7 +110,7 @@ class cSchedules : public cList<cSchedule> {
friend class cSchedule;
friend class cSchedulesLock;
private:
cRWlock rwlock;
cRwLock rwlock;
static cSchedules schedules;
static const char *epgDataFileName;
static time_t lastCleanup;

65
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.c 1.275 2003/12/22 10:05:14 kls Exp $
* $Id: menu.c 1.276 2004/01/04 11:12:43 kls Exp $
*/
#include "menu.h"
@ -581,7 +581,7 @@ void cMenuEditChannel::Setup(void)
Add(new cMenuEditIntItem( tr("Dpid1"), &data.dpid1, 0, 0x1FFF));
Add(new cMenuEditIntItem( tr("Dpid2"), &data.dpid2, 0, 0x1FFF));
Add(new cMenuEditIntItem( tr("Tpid"), &data.tpid, 0, 0x1FFF));
Add(new cMenuEditCaItem( tr("CA"), &data.ca, true));
Add(new cMenuEditCaItem( tr("CA"), &data.caids[0], true));//XXX
Add(new cMenuEditIntItem( tr("Sid"), &data.sid, 0));
/* XXX not yet used
Add(new cMenuEditIntItem( tr("Nid"), &data.nid, 0));
@ -615,7 +615,6 @@ eOSState cMenuEditChannel::ProcessKey(eKeys Key)
if (channel) {
*channel = data;
isyslog("edited channel %d %s", channel->Number(), data.ToText());
Timers.Save();
state = osBack;
}
else {
@ -626,7 +625,7 @@ eOSState cMenuEditChannel::ProcessKey(eKeys Key)
isyslog("added channel %d %s", channel->Number(), data.ToText());
state = osUser1;
}
Channels.Save();
Channels.SetModified();
}
else {
Interface->Error(tr("Channel settings are not unique!"));
@ -682,6 +681,7 @@ protected:
virtual void Move(int From, int To);
public:
cMenuChannels(void);
~cMenuChannels();
virtual eOSState ProcessKey(eKeys Key);
};
@ -693,6 +693,12 @@ cMenuChannels::cMenuChannels(void)
Add(new cMenuChannelItem(channel), channel->Number() == cDevice::CurrentChannel());
}
SetHelp(tr("Edit"), tr("New"), tr("Delete"), tr("Mark"));
Channels.IncBeingEdited();
}
cMenuChannels::~cMenuChannels()
{
Channels.DecBeingEdited();
}
cChannel *cMenuChannels::GetChannel(int Index)
@ -704,11 +710,10 @@ cChannel *cMenuChannels::GetChannel(int Index)
void cMenuChannels::Propagate(void)
{
Channels.ReNumber();
Channels.Save();
for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
ci->Set();
Timers.Save(); // channel numbering has changed!
Display();
Channels.SetModified();
}
eOSState cMenuChannels::Switch(void)
@ -1380,22 +1385,24 @@ void cMenuSchedule::PrepareSchedule(cChannel *Channel)
free(buffer);
if (schedules) {
const cSchedule *Schedule = schedules->GetSchedule(Channel->GetChannelID());
int num = Schedule->NumEvents();
const cEvent **pArray = MALLOC(const cEvent *, num);
if (pArray) {
time_t now = time(NULL);
int numreal = 0;
for (int a = 0; a < num; a++) {
const cEvent *Event = Schedule->GetEventNumber(a);
if (Event->StartTime() + Event->Duration() > now)
pArray[numreal++] = Event;
}
qsort(pArray, numreal, sizeof(cEvent *), CompareEventTime);
for (int a = 0; a < numreal; a++)
Add(new cMenuScheduleItem(pArray[a]));
free(pArray);
if (Schedule) {
int num = Schedule->NumEvents();
const cEvent **pArray = MALLOC(const cEvent *, num);
if (pArray) {
time_t now = time(NULL);
int numreal = 0;
for (int a = 0; a < num; a++) {
const cEvent *Event = Schedule->GetEventNumber(a);
if (Event->StartTime() + Event->Duration() > now)
pArray[numreal++] = Event;
}
qsort(pArray, numreal, sizeof(cEvent *), CompareEventTime);
for (int a = 0; a < numreal; a++)
Add(new cMenuScheduleItem(pArray[a]));
free(pArray);
}
}
}
}
@ -3219,6 +3226,20 @@ void cRecordControls::Process(time_t t)
}
}
void cRecordControls::ChannelDataModified(cChannel *Channel)
{
for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i]) {
if (RecordControls[i]->Timer() && RecordControls[i]->Timer()->Channel() == Channel) {
isyslog("stopping recording due to modification of channel %d", Channel->Number());
RecordControls[i]->Stop(true);
// This will restart the recording, maybe even from a different
// device in case conditional access has changed.
}
}
}
}
bool cRecordControls::Active(void)
{
for (int i = 0; i < MAXRECORDCONTROLS; i++) {

3
menu.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.h 1.58 2003/12/21 15:27:07 kls Exp $
* $Id: menu.h 1.59 2004/01/04 11:01:13 kls Exp $
*/
#ifndef __MENU_H
@ -143,6 +143,7 @@ public:
static const char *GetInstantId(const char *LastInstantId);
static cRecordControl *GetRecordControl(const char *FileName);
static void Process(time_t t);
static void ChannelDataModified(cChannel *Channel);
static bool Active(void);
static void Shutdown(void);
};

350
pat.c
View File

@ -4,45 +4,39 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: pat.c 1.2 2003/12/24 10:23:33 kls Exp $
* $Id: pat.c 1.3 2004/01/04 12:27:06 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 {
friend class cCaDescriptors;
private:
int source;
int transponder;
int serviceId;
int caSystem;
int providerId;
int caPid;
bool stream;
int length;
uchar *data;
public:
cCaDescriptor(int Source, int Transponder, int ServiceId, int CaSystem, int ProviderId, int CaPid, bool Stream, int Length, const uchar *Data);
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 Source, int Transponder, int ServiceId, int CaSystem, int ProviderId, int CaPid, bool Stream, int Length, const uchar *Data)
cCaDescriptor::cCaDescriptor(int CaSystem, int CaPid, bool Stream, int Length, const uchar *Data)
{
source = Source;
transponder = Transponder;
serviceId = ServiceId;
caSystem = CaSystem;
providerId = ProviderId;
caPid = CaPid;
stream = Stream;
length = Length + 6;
data = MALLOC(uchar, length);
@ -54,15 +48,6 @@ cCaDescriptor::cCaDescriptor(int Source, int Transponder, int ServiceId, int CaS
data[5] = CaPid & 0xFF;
if (Length)
memcpy(&data[6], Data, Length);
//#define DEBUG_CA_DESCRIPTORS 1
#ifdef DEBUG_CA_DESCRIPTORS
char buffer[1024];
char *q = buffer;
q += sprintf(q, "CAM: %04X %5d %5d %04X %6X %04X %d -", source, transponder, serviceId, caSystem, providerId, caPid, stream);
for (int i = 0; i < length; i++)
q += sprintf(q, " %02X", data[i]);
dsyslog(buffer);
#endif
}
cCaDescriptor::~cCaDescriptor()
@ -70,75 +55,121 @@ cCaDescriptor::~cCaDescriptor()
free(data);
}
// --- cCaDescriptors --------------------------------------------------------
class cCaDescriptors : public cList<cCaDescriptor> {
private:
cMutex mutex;
public:
void NewCaDescriptor(int Source, int Transponder, int ServiceId, SI::CaDescriptor *d, bool Stream);
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);
};
void cCaDescriptors::NewCaDescriptor(int Source, int Transponder, int ServiceId, SI::CaDescriptor *d, bool Stream)
bool cCaDescriptor::operator== (const cCaDescriptor &arg) const
{
// The code for determining the ProviderID was taken from 'libdtv'
// written by Rolf Hakenes <hakenes@hippomi.de>.
const uchar *Data = d->privateData.getData();
int Length = d->privateData.getLength();
int ProviderID = 0;
switch (d->getCaType() >> 8) {
case 0x01: // SECA
ProviderID = (Data[0] << 8) | Data[1];
break;
case 0x05: // Viaccess ? (France Telecom)
for (int i = 0; i < Length; i++) {
if (Data[i] == 0x14 && Data[i + 1] == 0x03) {
ProviderID = (Data[i + 2] << 16) |
(Data[i + 3] << 8) |
(Data[i + 4] & 0xf0);
break;
}
}
break;
}
cMutexLock MutexLock(&mutex);
for (cCaDescriptor *ca = First(); ca; ca = Next(ca)) {
if (ca->source == Source && ca->transponder == Transponder && ca->serviceId == ServiceId && ca->caSystem == d->getCaType() && ca->providerId == ProviderID && ca->caPid == d->getCaPid())
return;
}
Add(new cCaDescriptor(Source, Transponder, ServiceId, d->getCaType(), ProviderID, d->getCaPid(), Stream, Length, Data));
//XXX update???
return length == arg.length && memcmp(data, arg.data, length) == 0;
}
int cCaDescriptors::GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag)
// --- 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) {
cMutexLock MutexLock(&mutex);
int length = 0;
int IsStream = -1;
for (cCaDescriptor *d = First(); d; d = Next(d)) {
if (d->source == Source && d->transponder == Transponder && d->serviceId == ServiceId) {
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;
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();
}
} while (*++caids);
}
else
return -1;
}
} while (*++caids);
}
StreamFlag = IsStream == 1;
return length;
@ -146,11 +177,52 @@ int cCaDescriptors::GetCaDescriptors(int Source, int Transponder, int ServiceId,
return -1;
}
cCaDescriptors CaDescriptors;
// --- 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);
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 CaDescriptors.GetCaDescriptors(Source, Transponder, ServiceId, CaSystemIds, BufSize, Data, StreamFlag);
return CaDescriptorHandler.GetCaDescriptors(Source, Transponder, ServiceId, CaSystemIds, BufSize, Data, StreamFlag);
}
// --- cPatFilter ------------------------------------------------------------
@ -160,6 +232,7 @@ cPatFilter::cPatFilter(void)
pmtIndex = 0;
pmtPid = 0;
lastPmtScan = 0;
numPmtEntries = 0;
Set(0x00, 0x00); // PAT
}
@ -169,6 +242,28 @@ void cPatFilter::SetStatus(bool On)
pmtIndex = 0;
pmtPid = 0;
lastPmtScan = 0;
numPmtEntries = 0;
}
void cPatFilter::Trigger(void)
{
numPmtEntries = 0;
}
bool cPatFilter::PmtVersionChanged(int PmtPid, int Version)
{
Version <<= 16;
for (int i = 0; i < numPmtEntries; i++) {
if ((pmtVersion[i] & 0x0000FFFF) == PmtPid) {
bool Changed = (pmtVersion[i] & 0x00FF0000) != Version;
if (Changed)
pmtVersion[i] = PmtPid | Version;
return Changed;
}
}
if (numPmtEntries < MAXPMTENTRIES)
pmtVersion[numPmtEntries++] = PmtPid | Version;
return true;
}
void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
@ -206,21 +301,78 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
SI::PMT pmt(Data, false);
if (!pmt.CheckCRCAndParse())
return;
SI::CaDescriptor *d;
// Scan the common loop:
for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)pmt.commonDescriptors.getNext(it, SI::CaDescriptorTag)); ) {
CaDescriptors.NewCaDescriptor(Source(), Transponder(), pmt.getServiceId(), d, false);
delete d;
}
// Scan the stream-specific loop:
SI::PMT::Stream stream;
for (SI::Loop::Iterator it; pmt.streamLoop.hasNext(it); ) {
stream = pmt.streamLoop.getNext(it);
for (SI::Loop::Iterator it; (d = (SI::CaDescriptor*)stream.streamDescriptors.getNext(it, SI::CaDescriptorTag)); ) {
CaDescriptors.NewCaDescriptor(Source(), Transponder(), pmt.getServiceId(), d, true);
delete d;
}
}
if (!PmtVersionChanged(pmtPid, 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] = { 0 };
int Dpids[MAXAPIDS] = { 0 };
int Tpid = 0;
int NumApids = 0;
int NumDpids = 0;
for (SI::Loop::Iterator it; pmt.streamLoop.hasNext(it); ) {
stream = pmt.streamLoop.getNext(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();
}
break;
case 5: // STREAMTYPE_13818_PRIVATE
case 6: // STREAMTYPE_13818_PES_PRIVATE
//XXX case 8: // STREAMTYPE_13818_DSMCC
{
SI::Descriptor *d;
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
switch (d->getDescriptorTag()) {
case SI::AC3DescriptorTag:
if (NumDpids < MAXAPIDS)
Dpids[NumDpids++] = stream.getPid();
break;
case SI::TeletextDescriptorTag:
Tpid = stream.getPid();
break;
default: ;
}
delete d;
}
}
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;
}
}
Channel->SetPids(Vpid, Ppid, Apids[0], Apids[1], Dpids[0], Dpids[1], Tpid);
Channel->SetCaIds(CaDescriptors->CaIds());
Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors));
}
lastPmtScan = 0; // this triggers the next scan
Channels.Unlock();
}
}

9
pat.h
View File

@ -4,25 +4,30 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: pat.h 1.2 2003/12/24 10:08:22 kls Exp $
* $Id: pat.h 1.3 2004/01/03 13:47:54 kls Exp $
*/
#ifndef __PAT_H
#define __PAT_H
#include "filter.h"
#include "thread.h"
#define MAXPMTENTRIES 64
class cPatFilter : public cFilter {
private:
time_t lastPmtScan;
int pmtIndex;
int pmtPid;
int pmtVersion[MAXPMTENTRIES];
int numPmtEntries;
bool PmtVersionChanged(int PmtPid, int Version);
protected:
virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
public:
cPatFilter(void);
virtual void SetStatus(bool On);
void Trigger(void);
};
int GetCaDescriptors(int Source, int Transponder, int ServiceId, const unsigned short *CaSystemIds, int BufSize, uchar *Data, bool &StreamFlag);

146
sdt.c Normal file
View File

@ -0,0 +1,146 @@
/*
* sdt.c: SDT section filter
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: sdt.c 1.1 2004/01/04 11:54:42 kls Exp $
*/
#include "sdt.h"
#include "channels.h"
#include "libsi/section.h"
#include "libsi/descriptor.h"
// --- cSDT ------------------------------------------------------------------
class cSDT : public SI::SDT {
public:
cSDT(int Source, int Transponder, uchar &lastSdtVersion, cPatFilter *PatFilter, const u_char *Data);
};
cSDT::cSDT(int Source, int Transponder, uchar &lastSdtVersion, cPatFilter *PatFilter, const u_char *Data)
:SI::SDT(Data, false)
{
if (!CheckCRCAndParse())
return;
if (getVersionNumber() == lastSdtVersion)
return;
if (!Channels.Lock(true, 10))
return;
lastSdtVersion = getVersionNumber();
SI::SDT::Service SiSdtService;
for (SI::Loop::Iterator it; serviceLoop.hasNext(it); ) {
SiSdtService = serviceLoop.getNext(it);
cChannel *Channel = Channels.GetByChannelID(tChannelID(Source, getOriginalNetworkId(), getTransportStreamId(), SiSdtService.getServiceId()));
if (!Channel)
Channel = Channels.GetByChannelID(tChannelID(Source, 0, Transponder, SiSdtService.getServiceId()));
SI::Descriptor *d;
for (SI::Loop::Iterator it2; (d = SiSdtService.serviceDescriptors.getNext(it2)); ) {
switch (d->getDescriptorTag()) {
case SI::ServiceDescriptorTag: {
SI::ServiceDescriptor *sd = (SI::ServiceDescriptor *)d;
switch (sd->getServiceType()) {
case 0x01: // digital television service
//XXX TODO case 0x02: // digital radio sound service
//XXX TODO case 0x04: // NVOD reference service
//XXX TODO case 0x05: // NVOD time-shifted service
{
char buffer[1024];
char *p = sd->serviceName.getText(buffer);
char NameBuf[1024];
char ShortNameBuf[1024];
char *pn = NameBuf;
char *ps = ShortNameBuf;
int IsShortName = 0;
while (*p) {
if ((uchar)*p == 0x86)
IsShortName++;
else if ((uchar)*p == 0x87)
IsShortName--;
else {
*pn++ = *p;
if (IsShortName)
*ps++ = *p;
}
p++;
}
*pn = *ps = 0;
pn = NameBuf;
if (*NameBuf && *ShortNameBuf) {
*ps++ = ',';
strcpy(ps, NameBuf);
pn = ShortNameBuf;
}
if (Channel) {
Channel->SetId(getOriginalNetworkId(), getTransportStreamId(), SiSdtService.getServiceId());
Channel->SetName(pn);
// Using SiSdtService.getFreeCaMode() is no good, because some
// tv stations set this flag even for non-encrypted channels :-(
// The special value 0xFFFF was supposed to mean "unknown encryption"
// and would have been overwritten with real CA values later:
// Channel->SetCa(SiSdtService.getFreeCaMode() ? 0xFFFF : 0);
}
else if (*pn) {
Channel = Channels.NewChannel(Source, Transponder, pn, getOriginalNetworkId(), getTransportStreamId(), SiSdtService.getServiceId());
PatFilter->Trigger();
}
}
}
}
break;
// Using the CaIdentifierDescriptor is no good, because some tv stations
// just don't use it. The actual CA values are collected in pat.c:
/*
case SI::CaIdentifierDescriptorTag: {
SI::CaIdentifierDescriptor *cid = (SI::CaIdentifierDescriptor *)d;
if (Channel) {
for (SI::Loop::Iterator it; cid->identifiers.hasNext(it); )
Channel->SetCa(cid->identifiers.getNext(it));
}
}
break;
*/
case SI::NVODReferenceDescriptorTag: {
SI::NVODReferenceDescriptor *nrd = (SI::NVODReferenceDescriptor *)d;
for (SI::Loop::Iterator it; nrd->serviceLoop.hasNext(it); ) {
SI::NVODReferenceDescriptor::Service Service = nrd->serviceLoop.getNext(it);
//printf(" %04X-%04X-%04X\n", Service.getOriginalNetworkId(), Service.getTransportStream(), Service.getServiceId());//XXX TODO
}
}
break;
default: ;
}
delete d;
}
}
Channels.Unlock();
}
// --- cSdtFilter ------------------------------------------------------------
cSdtFilter::cSdtFilter(cPatFilter *PatFilter)
{
lastSdtVersion = 0xFF;
patFilter = PatFilter;
Set(0x11, 0x42); // SDT
}
void cSdtFilter::SetStatus(bool On)
{
cFilter::SetStatus(On);
lastSdtVersion = 0xFF;
}
void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
{
if (Source() && Transponder())
cSDT SDT(Source(), Transponder(), lastSdtVersion, patFilter, Data);
}

27
sdt.h Normal file
View File

@ -0,0 +1,27 @@
/*
* sdt.h: SDT section filter
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: sdt.h 1.1 2004/01/03 13:49:55 kls Exp $
*/
#ifndef __SDT_H
#define __SDT_H
#include "filter.h"
#include "pat.h"
class cSdtFilter : public cFilter {
private:
uchar lastSdtVersion;
cPatFilter *patFilter;
protected:
virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
public:
cSdtFilter(cPatFilter *PatFilter);
virtual void SetStatus(bool On);
};
#endif //__SDT_H

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: sections.c 1.1 2003/12/22 11:17:38 kls Exp $
* $Id: sections.c 1.2 2004/01/03 12:54:01 kls Exp $
*/
#include "sections.h"
@ -108,23 +108,25 @@ void cSectionHandler::Detach(cFilter *Filter)
void cSectionHandler::SetSource(int Source, int Transponder)
{
Lock();
source = Source;
transponder = Transponder;
Unlock();
}
void cSectionHandler::SetStatus(bool On)
{
Lock();
if (on != On) {
Lock();
statusCount++;
for (cFilter *fi = filters.First(); fi; fi = filters.Next(fi)) {
fi->SetStatus(false);
if (On)
fi->SetStatus(true);
}
Unlock();
on = On;
}
Unlock();
}
void cSectionHandler::Action(void)
@ -144,6 +146,9 @@ void cSectionHandler::Action(void)
Unlock();
if (poll(pfd, NumFilters, 1000) != 0) {
bool DeviceHasLock = device->HasLock();
if (!DeviceHasLock)
usleep(100000);
for (int i = 0; i < NumFilters; i++) {
if (pfd[i].revents & POLLIN) {
cFilterHandle *fh = NULL;
@ -158,6 +163,8 @@ void cSectionHandler::Action(void)
// Read section data:
unsigned char buf[4096]; // max. allowed size for any EIT section
int r = safe_read(fh->handle, buf, sizeof(buf));
if (!DeviceHasLock)
continue; // we do the read anyway, to flush any data that might have come from a different transponder
if (r > 3) { // minimum number of bytes necessary to get section length
int len = (((buf[1] & 0x0F) << 8) | (buf[2] & 0xFF)) + 3;
if (len == r) {

View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection.
*
* $Id: svdrp.c 1.56 2003/12/21 13:37:10 kls Exp $
* $Id: svdrp.c 1.57 2003/12/28 10:09:30 kls Exp $
*/
#include "svdrp.h"
@ -476,7 +476,7 @@ void cSVDRP::CmdDELC(const char *Option)
}
Channels.Del(channel);
Channels.ReNumber();
Channels.Save();
Channels.SetModified();
isyslog("channel %s deleted", Option);
Reply(250, "Channel \"%s\" deleted", Option);
}
@ -810,9 +810,8 @@ void cSVDRP::CmdMODC(const char *Option)
if (Channels.HasUniqueChannelID(&ch, channel)) {
*channel = ch;
Channels.ReNumber();
Channels.Save();
Channels.SetModified();
isyslog("modifed channel %d %s", channel->Number(), channel->ToText());
Timers.Save();
Reply(250, "%d %s", channel->Number(), channel->ToText());
}
else
@ -886,7 +885,7 @@ void cSVDRP::CmdNEWC(const char *Option)
*channel = ch;
Channels.Add(channel);
Channels.ReNumber();
Channels.Save();
Channels.SetModified();
isyslog("new channel %d %s", channel->Number(), channel->ToText());
Reply(250, "%d %s", channel->Number(), channel->ToText());
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: thread.c 1.29 2003/12/21 15:17:24 kls Exp $
* $Id: thread.c 1.30 2004/01/03 16:59:33 kls Exp $
*/
#include "thread.h"
@ -80,20 +80,20 @@ void cCondVar::Signal(void)
}
*/
// --- cRWlock ---------------------------------------------------------------
// --- cRwLock ---------------------------------------------------------------
cRWlock::cRWlock(bool PreferWriter)
cRwLock::cRwLock(bool PreferWriter)
{
pthread_rwlockattr_t attr = { PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP };
pthread_rwlock_init(&rwlock, &attr);
}
cRWlock::~cRWlock()
cRwLock::~cRwLock()
{
pthread_rwlock_destroy(&rwlock);
}
bool cRWlock::Lock(bool Write, int TimeoutMs)
bool cRwLock::Lock(bool Write, int TimeoutMs)
{
int Result = 0;
struct timespec abstime;
@ -108,7 +108,7 @@ bool cRWlock::Lock(bool Write, int TimeoutMs)
return Result == 0;
}
void cRWlock::Unlock(void)
void cRwLock::Unlock(void)
{
pthread_rwlock_unlock(&rwlock);
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: thread.h 1.19 2003/12/21 15:44:31 kls Exp $
* $Id: thread.h 1.20 2004/01/03 16:58:50 kls Exp $
*/
#ifndef __THREAD_H
@ -28,12 +28,12 @@ public:
//void Signal(void);
};
class cRWlock {
class cRwLock {
private:
pthread_rwlock_t rwlock;
public:
cRWlock(bool PreferWriter = false);
~cRWlock();
cRwLock(bool PreferWriter = false);
~cRwLock();
bool Lock(bool Write, int TimeoutMs = 0);
void Unlock(void);
};

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: timers.c 1.7 2003/12/13 13:06:29 kls Exp $
* $Id: timers.c 1.8 2003/12/27 13:10:04 kls Exp $
*/
#include "timers.h"
@ -216,8 +216,10 @@ bool cTimer::Parse(const char *s)
strn0cpy(file, filebuffer, MaxFileName);
strreplace(file, '|', ':');
strreplace(summary, '|', '\n');
tChannelID cid = tChannelID::FromString(channelbuffer);
channel = cid.Valid() ? Channels.GetByChannelID(cid, true) : Channels.GetByNumber(atoi(channelbuffer));
if (isnumber(channelbuffer))
channel = Channels.GetByNumber(atoi(channelbuffer));
else
channel = Channels.GetByChannelID(tChannelID::FromString(channelbuffer), true);
if (!channel) {
esyslog("ERROR: channel %s not defined", channelbuffer);
result = false;

43
vdr.5
View File

@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the
.\" vdr distribution.
.\"
.\" $Id: vdr.5 1.20 2003/05/29 11:58:57 kls Exp $
.\" $Id: vdr.5 1.21 2004/01/02 15:24:21 kls Exp $
.\"
.TH vdr 5 "1 Jun 2003" "1.2.0" "Video Disk Recorder Files"
.SH NAME
@ -45,7 +45,7 @@ Such a delimiter will not appear in the Channels menu.
A \fBchannel definition\fR is a line with channel data, where the fields
are separated by ':' characters. Example:
\fBRTL:12188:h:S19.2E:27500:163:104:105:0:12003:0:0:0\fR
\fBRTL,RTL Television:12188:h:S19.2E:27500:163:104:105:0:12003:1:1089:0\fR
The line number of a channel definition (not counting group separators,
and based on a possible previous '@...' parameter)
@ -57,6 +57,13 @@ to right):
.B Name
The channel's name (if the name originally contains a ':' character
it has to be replaced by '|').
Some tv stations provide a way of deriving a "short name" from the
channel name, which can be used in situations where there is not
much space for displaying a long name. If a short name is available
for this channel, it preceeds the full name and is delimited by a comma,
as in
\fBRTL,RTL Television:...\fR
.TP
.B Frequency
The transponder frequency (as an integer). For DVB-S this value is in MHz. For DVB-C
@ -119,23 +126,33 @@ the audio PIDs, separated by a semicolon, as in
The teletext PID.
.TP
.B Conditional access
An integer defining how this channel can be accessed:
A hexadecimal integer defining how this channel can be accessed:
.TS
tab (@);
l l.
\fB0\fR@Free To Air
\fB1...4\fR@explicitly requires the DVB card with the given number
\fB>=100\fR@requires a specific decryption method defined in \fIca.conf\fR
\fB0000\fR@Free To Air
\fB0001...000F\fR@explicitly requires the device with the given number
\fB0010...00FF\fR@reserved for user defined assignments defined in \fIca.conf\fR
\fB0100...FFFF\fR@specific decryption methods as broadcast in the data stream\fR
.TE
Values in the range 0001...00FF will not be overwritten, all other values
will be automatically replaced by the actual CA system identifiers received
from the data stream. If there is more than one CA system id broadcast, they
will be separated by commas, as in
.B ...:1702,1722,1801:...
The values are in hex because that's the way they are defined in the "ETR 162"
document. Leading zeros may be omitted.
.TP
.B SID
The Service ID of this channel.
.TP
.B NID
The Network ID of this channel (for future use, currently always 0).
The Network ID of this channel.
.TP
.B TID
The Transport stream ID of this channel (for future use, currently always 0).
The Transport stream ID of this channel.
.TP
.B RID
The Radio ID of this channel (typically 0, may be used to distinguish channels where
@ -144,12 +161,12 @@ NID, TID and SID are all equal).
A particular channel can be uniquely identified by its \fBchannel\ ID\fR,
which is a string that looks like this:
\fBS19.2E-0-12188-12003-0\fR
\fBS19.2E-1-1089-12003-0\fR
The components of this string are the \fBSource\fR (S19.2E), \fBFrequency\fR
(12188, MHz) and \fBSID\fR (12003) as defined above. The parts that are currently
\fB0\fR are reserved for future use (the last part can be omitted if it is \fB0\fR,
so the above example could also be written as \fBS19.2E-0-12188-12003\fR).
The components of this string are the \fBSource\fR (S19.2E), \fBNID\fR
(1), \fBTID\fR (1089), \fBSID\fR (12003) and \fBRID\fR (0) as defined above.
The last part can be omitted if it is \fB0\fR,
so the above example could also be written as S19.2E-1-1089-12003).
.br
The \fBchannel\ ID\fR is used in the \fItimers.conf\fR and \fIepg.data\fR
files to properly identify the channels.

21
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/vdr
*
* $Id: vdr.c 1.171 2003/12/22 13:29:24 kls Exp $
* $Id: vdr.c 1.172 2004/01/04 11:12:05 kls Exp $
*/
#include <getopt.h>
@ -511,6 +511,25 @@ int main(int argc, char *argv[])
dsyslog("max. latency time %d seconds", MaxLatencyTime);
}
}
// Handle channel modifications:
if (!Channels.BeingEdited() && Channels.Modified()) {
if (Channels.Lock(false, 100)) {
Channels.Save(); //XXX only after user changes???
Timers.Save();
for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
if (Channel && Channel->Modification(CHANNELMOD_RETUNE)) {
cRecordControls::ChannelDataModified(Channel);
if (Channel->Number() == cDevice::CurrentChannel()) {
if (!cDevice::PrimaryDevice()->Replaying()) {
isyslog("retuning due to modification of channel %d", Channel->Number());
Channels.SwitchTo(Channel->Number());
}
}
}
}
Channels.Unlock();
}
}
// Channel display:
if (!EITScanner.Active() && cDevice::CurrentChannel() != LastChannel) {
if (!Menu)