mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Implemented automatic PID switching and channel detection
This commit is contained in:
parent
3a1058fe1f
commit
8976ebcec5
@ -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
25
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.
|
||||
|
4
Makefile
4
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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
223
channels.c
223
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<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;
|
||||
}
|
||||
|
52
channels.h
52
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<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
23
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 <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
3
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);
|
||||
};
|
||||
|
4
config.h
4
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) {
|
||||
|
42
device.c
42
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
|
||||
|
13
device.h
13
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
|
||||
|
77
dvbdevice.c
77
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()) {
|
||||
|
@ -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
10
eit.c
@ -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
144
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<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
4
epg.h
@ -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
65
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++) {
|
||||
|
3
menu.h
3
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);
|
||||
};
|
||||
|
350
pat.c
350
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 <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
9
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);
|
||||
|
146
sdt.c
Normal file
146
sdt.c
Normal 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
27
sdt.h
Normal 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
|
13
sections.c
13
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) {
|
||||
|
9
svdrp.c
9
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());
|
||||
}
|
||||
|
12
thread.c
12
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);
|
||||
}
|
||||
|
8
thread.h
8
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);
|
||||
};
|
||||
|
8
timers.c
8
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;
|
||||
|
43
vdr.5
43
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.
|
||||
|
21
vdr.c
21
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 <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)
|
||||
|
Loading…
Reference in New Issue
Block a user