diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 1ae39e7d..d2f715dd 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -216,6 +216,8 @@ Andreas Schultz 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' diff --git a/HISTORY b/HISTORY index ad2701bd..fe1e166a 100644 --- a/HISTORY +++ b/HISTORY @@ -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. diff --git a/Makefile b/Makefile index 5b3e743f..cc3a3a95 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/PLUGINS/src/sky/HISTORY b/PLUGINS/src/sky/HISTORY index d41904e6..d3da71ec 100644 --- a/PLUGINS/src/sky/HISTORY +++ b/PLUGINS/src/sky/HISTORY @@ -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 diff --git a/PLUGINS/src/sky/sky.c b/PLUGINS/src/sky/sky.c index ae591378..0f2da6e9 100644 --- a/PLUGINS/src/sky/sky.c +++ b/PLUGINS/src/sky/sky.c @@ -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 @@ -14,7 +14,7 @@ #include #include -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; diff --git a/channels.c b/channels.c index 69ff3c91..9a7965f5 100644 --- a/channels.c +++ b/channels.c @@ -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, ¶mbuf, &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, ¶mbuf, &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::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; +} diff --git a/channels.h b/channels.h index 6fa1758c..c3d9ad4e 100644 --- a/channels.h +++ b/channels.h @@ -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 { -protected: +class cChannels : public cRwLock, public cConfig { +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; diff --git a/ci.c b/ci.c index a6f60562..760052de 100644 --- a/ci.c +++ b/ci.c @@ -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 #include @@ -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); diff --git a/ci.h b/ci.h index 673bad21..71eccd8d 100644 --- a/ci.h +++ b/ci.h @@ -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); }; diff --git a/config.h b/config.h index 9125d2c8..08368e4c 100644 --- a/config.h +++ b/config.h @@ -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) { diff --git a/device.c b/device.c index 20cbe5c5..8f73a142 100644 --- a/device.c +++ b/device.c @@ -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 diff --git a/device.h b/device.h index 33b4ad80..196ccfe9 100644 --- a/device.h +++ b/device.h @@ -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 diff --git a/dvbdevice.c b/dvbdevice.c index 76c3340b..15c6786d 100644 --- a/dvbdevice.c +++ b/dvbdevice.c @@ -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()) { diff --git a/dvbdevice.h b/dvbdevice.h index f6f6f484..f64e6dda 100644 --- a/dvbdevice.h +++ b/dvbdevice.h @@ -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 diff --git a/eit.c b/eit.c index f1071826..45a4d800 100644 --- a/eit.c +++ b/eit.c @@ -8,7 +8,7 @@ * Robert Schneider and Rolf Hakenes . * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg . * - * $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; } diff --git a/eitscan.c b/eitscan.c index a12bb44d..d4d1e8f3 100644 --- a/eitscan.c +++ b/eitscan.c @@ -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 { +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); + } } } } diff --git a/eitscan.h b/eitscan.h index 420f47d0..fff2dee8 100644 --- a/eitscan.h +++ b/eitscan.h @@ -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 #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(); diff --git a/epg.h b/epg.h index 84667848..19332a59 100644 --- a/epg.h +++ b/epg.h @@ -7,7 +7,7 @@ * Original version (as used in VDR before 1.3.0) written by * Robert Schneider and Rolf Hakenes . * - * $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 { friend class cSchedule; friend class cSchedulesLock; private: - cRWlock rwlock; + cRwLock rwlock; static cSchedules schedules; static const char *epgDataFileName; static time_t lastCleanup; diff --git a/menu.c b/menu.c index 16f2893a..5e628bbb 100644 --- a/menu.c +++ b/menu.c @@ -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++) { diff --git a/menu.h b/menu.h index ddab9863..041d6f7c 100644 --- a/menu.h +++ b/menu.h @@ -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); }; diff --git a/pat.c b/pat.c index 07980147..3fa6848c 100644 --- a/pat.c +++ b/pat.c @@ -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 +#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 { -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 . - - 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 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 { +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(); } } diff --git a/pat.h b/pat.h index df30c104..dd1a1fe8 100644 --- a/pat.h +++ b/pat.h @@ -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); diff --git a/sdt.c b/sdt.c new file mode 100644 index 00000000..a4e91b80 --- /dev/null +++ b/sdt.c @@ -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); +} diff --git a/sdt.h b/sdt.h new file mode 100644 index 00000000..8d75f3c7 --- /dev/null +++ b/sdt.h @@ -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 diff --git a/sections.c b/sections.c index 4a2f6d5f..6b4d4907 100644 --- a/sections.c +++ b/sections.c @@ -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) { diff --git a/svdrp.c b/svdrp.c index 009f2f01..83847a0e 100644 --- a/svdrp.c +++ b/svdrp.c @@ -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()); } diff --git a/thread.c b/thread.c index 31e14a2b..974362c9 100644 --- a/thread.c +++ b/thread.c @@ -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); } diff --git a/thread.h b/thread.h index 124a930e..1804a5a6 100644 --- a/thread.h +++ b/thread.h @@ -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); }; diff --git a/timers.c b/timers.c index 36e249c3..1db88a0f 100644 --- a/timers.c +++ b/timers.c @@ -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; diff --git a/vdr.5 b/vdr.5 index 67bdc37b..761c400d 100644 --- a/vdr.5 +++ b/vdr.5 @@ -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. diff --git a/vdr.c b/vdr.c index 4adb1868..fc12f005 100644 --- a/vdr.c +++ b/vdr.c @@ -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 @@ -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)