mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
- Fixed a lockup in the EPG scanner when no non-primary device was available (thanks to Martin Holst for reporting this one). - Fixed a compiler warning about virtual cConfig::Load() functions (thanks to Lauri Tischler for reporting this one). - Fixed a warning about character comparison in libsi/si.c (thanks to Lauri Tischler for reporting this one). - The new parameter "Update channels" in the "Setup/DVB" menu can be used to control if and how channels will be automatically updated (see MANUAL). This has already been part of the 'autopid' patch by Andreas Schultz and has now been adopted. - Fixed a crash in case there is no DVB hardware present (thanks to Sascha Volkenandt for reporting this one). - Changed calculation of channel ids to make it work for tv stations that use the undefined NID value 0 (thanks to Teemu Rantanen for reporting this one). - Enhanced the SDT filter to handle multi part sections. - Added support for selecting preferred EPG languages (based upon a patch by Teemu Rantanen). - Fixed a 'const' in libsi/si.h (thanks to Marcel Wiesweg). - Fixed the 'su' call in 'runvdr' to make it work on systems that require the user name to appear before the command option (thanks to Robert Huitl). - Fixed testing for matching section filters in case they are turned off (thanks to Marcel Wiesweg). - In case of incomplete sections an error message is now logged only every 10 seconds. - Fixed a possible NULL pointer access in cEITScanner::Process() (thanks to Andreas Kool). - The actual transponder data is now taken from the NIT and existing channels are adjusted if necessary. If the NIT contains new transponders, they are scanned for channels during the next EPG scan. Note that only the satellite branches are tested, cable and terrestrial need to be tested by somebody who actually has such equipment.
734 lines
22 KiB
C
734 lines
22 KiB
C
/*
|
|
* channels.c: Channel handling
|
|
*
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
* how to reach the author.
|
|
*
|
|
* $Id: channels.c 1.19 2004/01/11 15:52:32 kls Exp $
|
|
*/
|
|
|
|
#include "channels.h"
|
|
#include <linux/dvb/frontend.h>
|
|
#include <ctype.h>
|
|
|
|
// IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
|
|
// format characters in order to allow any number of blanks after a numeric
|
|
// value!
|
|
|
|
// -- Channel Parameter Maps -------------------------------------------------
|
|
|
|
const tChannelParameterMap InversionValues[] = {
|
|
{ 0, INVERSION_OFF },
|
|
{ 1, INVERSION_ON },
|
|
{ 999, INVERSION_AUTO },
|
|
{ -1 }
|
|
};
|
|
|
|
const tChannelParameterMap BandwidthValues[] = {
|
|
{ 6, BANDWIDTH_6_MHZ },
|
|
{ 7, BANDWIDTH_7_MHZ },
|
|
{ 8, BANDWIDTH_8_MHZ },
|
|
{ 999, BANDWIDTH_AUTO },
|
|
{ -1 }
|
|
};
|
|
|
|
const tChannelParameterMap CoderateValues[] = {
|
|
{ 0, FEC_NONE },
|
|
{ 12, FEC_1_2 },
|
|
{ 23, FEC_2_3 },
|
|
{ 34, FEC_3_4 },
|
|
{ 45, FEC_4_5 },
|
|
{ 56, FEC_5_6 },
|
|
{ 67, FEC_6_7 },
|
|
{ 78, FEC_7_8 },
|
|
{ 89, FEC_8_9 },
|
|
{ 999, FEC_AUTO },
|
|
{ -1 }
|
|
};
|
|
|
|
const tChannelParameterMap ModulationValues[] = {
|
|
{ 0, QPSK },
|
|
{ 16, QAM_16 },
|
|
{ 32, QAM_32 },
|
|
{ 64, QAM_64 },
|
|
{ 128, QAM_128 },
|
|
{ 256, QAM_256 },
|
|
{ 999, QAM_AUTO },
|
|
{ -1 }
|
|
};
|
|
|
|
const tChannelParameterMap TransmissionValues[] = {
|
|
{ 2, TRANSMISSION_MODE_2K },
|
|
{ 8, TRANSMISSION_MODE_8K },
|
|
{ 999, TRANSMISSION_MODE_AUTO },
|
|
{ -1 }
|
|
};
|
|
|
|
const tChannelParameterMap GuardValues[] = {
|
|
{ 4, GUARD_INTERVAL_1_4 },
|
|
{ 8, GUARD_INTERVAL_1_8 },
|
|
{ 16, GUARD_INTERVAL_1_16 },
|
|
{ 32, GUARD_INTERVAL_1_32 },
|
|
{ 999, GUARD_INTERVAL_AUTO },
|
|
{ -1 }
|
|
};
|
|
|
|
const tChannelParameterMap HierarchyValues[] = {
|
|
{ 0, HIERARCHY_NONE },
|
|
{ 1, HIERARCHY_1 },
|
|
{ 2, HIERARCHY_2 },
|
|
{ 4, HIERARCHY_4 },
|
|
{ 999, HIERARCHY_AUTO },
|
|
{ -1 }
|
|
};
|
|
|
|
int UserIndex(int Value, const tChannelParameterMap *Map)
|
|
{
|
|
const tChannelParameterMap *map = Map;
|
|
while (map && map->userValue != -1) {
|
|
if (map->userValue == Value)
|
|
return map - Map;
|
|
map++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int DriverIndex(int Value, const tChannelParameterMap *Map)
|
|
{
|
|
const tChannelParameterMap *map = Map;
|
|
while (map && map->userValue != -1) {
|
|
if (map->driverValue == Value)
|
|
return map - Map;
|
|
map++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int MapToUser(int Value, const tChannelParameterMap *Map)
|
|
{
|
|
int n = DriverIndex(Value, Map);
|
|
if (n >= 0)
|
|
return Map[n].userValue;
|
|
return -1;
|
|
}
|
|
|
|
int MapToDriver(int Value, const tChannelParameterMap *Map)
|
|
{
|
|
int n = UserIndex(Value, Map);
|
|
if (n >= 0)
|
|
return Map[n].driverValue;
|
|
return -1;
|
|
}
|
|
|
|
// -- tChannelID -------------------------------------------------------------
|
|
|
|
const tChannelID tChannelID::InvalidID;
|
|
|
|
bool tChannelID::operator== (const tChannelID &arg) const
|
|
{
|
|
return source == arg.source && nid == arg.nid && tid == arg.tid && sid == arg.sid && rid == arg.rid;
|
|
}
|
|
|
|
tChannelID tChannelID::FromString(const char *s)
|
|
{
|
|
char *sourcebuf = NULL;
|
|
int nid;
|
|
int tid;
|
|
int sid;
|
|
int rid = 0;
|
|
int fields = sscanf(s, "%a[^-]-%d-%d-%d-%d", &sourcebuf, &nid, &tid, &sid, &rid);
|
|
if (fields == 4 || fields == 5) {
|
|
int source = cSource::FromString(sourcebuf);
|
|
free(sourcebuf);
|
|
if (source >= 0)
|
|
return tChannelID(source, nid, tid, sid, rid);
|
|
}
|
|
return tChannelID::InvalidID;
|
|
}
|
|
|
|
const char *tChannelID::ToString(void)
|
|
{
|
|
static char buffer[256];
|
|
snprintf(buffer, sizeof(buffer), rid ? "%s-%d-%d-%d-%d" : "%s-%d-%d-%d", cSource::ToString(source), nid, tid, sid, rid);
|
|
return buffer;
|
|
}
|
|
|
|
// -- cChannel ---------------------------------------------------------------
|
|
|
|
char *cChannel::buffer = NULL;
|
|
|
|
cChannel::cChannel(void)
|
|
{
|
|
memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__);
|
|
inversion = INVERSION_AUTO;
|
|
bandwidth = BANDWIDTH_AUTO;
|
|
coderateH = FEC_AUTO;
|
|
coderateL = FEC_AUTO;
|
|
modulation = QAM_AUTO;
|
|
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)
|
|
{
|
|
memcpy(&__BeginData__, &Channel.__BeginData__, (char *)&Channel.__EndData__ - (char *)&Channel.__BeginData__);
|
|
return *this;
|
|
}
|
|
|
|
int cChannel::Transponder(void) const
|
|
{
|
|
int tf = frequency;
|
|
while (tf > 20000)
|
|
tf /= 1000;
|
|
return tf;
|
|
}
|
|
|
|
tChannelID cChannel::GetChannelID(void) const
|
|
{
|
|
return tChannelID(source, nid, (nid || tid) ? tid : Transponder(), sid, rid);
|
|
}
|
|
|
|
int cChannel::Modification(int Mask)
|
|
{
|
|
int Result = modification & Mask;
|
|
modification = CHANNELMOD_NONE;
|
|
return Result;
|
|
}
|
|
|
|
bool cChannel::SetSatTransponderData(int Source, int Frequency, char Polarization, int Srate, int CoderateH, bool Log)
|
|
{
|
|
// Workarounds for broadcaster stupidity:
|
|
// Some providers broadcast the transponder frequency of their channels with two different
|
|
// values (like 12551 and 12552), so we need to allow for a little tolerance here
|
|
if (abs(frequency - Frequency) <= 1)
|
|
Frequency = frequency;
|
|
// Sometimes the transponder frequency is set to 0, which is just wrong
|
|
if (Frequency == 0)
|
|
return false;
|
|
|
|
if (source != Source || frequency != Frequency || polarization != Polarization || srate != Srate || coderateH != CoderateH) {
|
|
if (Log)
|
|
dsyslog("changing transponder data of channel %d from %s:%d:%c:%d:%d to %s:%d:%c:%d:%d", Number(), cSource::ToString(source), frequency, polarization, srate, coderateH, cSource::ToString(Source), Frequency, Polarization, Srate, CoderateH);
|
|
source = Source;
|
|
frequency = Frequency;
|
|
polarization = Polarization;
|
|
srate = Srate;
|
|
coderateH = CoderateH;
|
|
modulation = QPSK;
|
|
modification |= CHANNELMOD_TRANSP;
|
|
Channels.SetModified();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cChannel::SetCableTransponderData(int Source, int Frequency, int Modulation, int Srate, int CoderateH, bool Log)
|
|
{
|
|
if (source != Source || frequency != Frequency || modulation != Modulation || srate != Srate || coderateH != CoderateH) {
|
|
if (Log)
|
|
dsyslog("changing transponder data of channel %d from %s:%d:%d:%d:%d to %s:%d:%d:%d:%d", Number(), cSource::ToString(source), frequency, modulation, srate, coderateH, cSource::ToString(Source), Frequency, Modulation, Srate, CoderateH);
|
|
source = Source;
|
|
frequency = Frequency;
|
|
modulation = Modulation;
|
|
srate = Srate;
|
|
coderateH = CoderateH;
|
|
modification |= CHANNELMOD_TRANSP;
|
|
Channels.SetModified();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cChannel::SetTerrTransponderData(int Source, int Frequency, int Bandwidth, int Modulation, int Hierarchy, int CoderateH, int CoderateL, int Guard, int Transmission, bool Log)
|
|
{
|
|
if (source != Source || frequency != Frequency || bandwidth != Bandwidth || modulation != Modulation || hierarchy != Hierarchy || coderateH != CoderateH || coderateL != CoderateL || guard != Guard || transmission != Transmission) {
|
|
if (Log)
|
|
dsyslog("changing transponder data of channel %d from %s:%d:%d:%d:%d:%d:%d:%d:%d to %s:%d:%d:%d:%d:%d:%d:%d:%d", Number(), cSource::ToString(source), frequency, bandwidth, modulation, hierarchy, coderateH, coderateL, guard, transmission, cSource::ToString(Source), Frequency, Bandwidth, Modulation, Hierarchy, CoderateH, CoderateL, Guard, Transmission);
|
|
source = Source;
|
|
frequency = Frequency;
|
|
bandwidth = Bandwidth;
|
|
modulation = Modulation;
|
|
hierarchy = Hierarchy;
|
|
coderateH = CoderateH;
|
|
coderateL = CoderateL;
|
|
guard = Guard;
|
|
transmission = Transmission;
|
|
modification |= CHANNELMOD_TRANSP;
|
|
Channels.SetModified();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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)
|
|
{
|
|
return Value >= 0 && Value != 999 ? sprintf(p, "%c%d", Name, Value) : 0;
|
|
}
|
|
|
|
const char *cChannel::ParametersToString(void)
|
|
{
|
|
char type = *cSource::ToString(source);
|
|
if (isdigit(type))
|
|
type = 'S';
|
|
#define ST(s) if (strchr(s, type))
|
|
static char buffer[64];
|
|
char *q = buffer;
|
|
*q = 0;
|
|
ST(" S ") q += sprintf(q, "%c", polarization);
|
|
ST("CST") q += PrintParameter(q, 'I', MapToUser(inversion, InversionValues));
|
|
ST("CST") q += PrintParameter(q, 'C', MapToUser(coderateH, CoderateValues));
|
|
ST(" T") q += PrintParameter(q, 'D', MapToUser(coderateL, CoderateValues));
|
|
ST("C T") q += PrintParameter(q, 'M', MapToUser(modulation, ModulationValues));
|
|
ST(" T") q += PrintParameter(q, 'B', MapToUser(bandwidth, BandwidthValues));
|
|
ST(" T") q += PrintParameter(q, 'T', MapToUser(transmission, TransmissionValues));
|
|
ST(" T") q += PrintParameter(q, 'G', MapToUser(guard, GuardValues));
|
|
ST(" T") q += PrintParameter(q, 'Y', MapToUser(hierarchy, HierarchyValues));
|
|
return buffer;
|
|
}
|
|
|
|
static const char *ParseParameter(const char *s, int &Value, const tChannelParameterMap *Map)
|
|
{
|
|
if (*++s) {
|
|
char *p = NULL;
|
|
errno = 0;
|
|
int n = strtol(s, &p, 10);
|
|
if (!errno && p != s) {
|
|
Value = MapToDriver(n, Map);
|
|
if (Value >= 0)
|
|
return p;
|
|
}
|
|
}
|
|
esyslog("ERROR: illegal value for parameter '%c'", *(s - 1));
|
|
return NULL;
|
|
}
|
|
|
|
bool cChannel::StringToParameters(const char *s)
|
|
{
|
|
while (s && *s) {
|
|
switch (toupper(*s)) {
|
|
case 'B': s = ParseParameter(s, bandwidth, BandwidthValues); break;
|
|
case 'C': s = ParseParameter(s, coderateH, CoderateValues); break;
|
|
case 'D': s = ParseParameter(s, coderateL, CoderateValues); break;
|
|
case 'G': s = ParseParameter(s, guard, GuardValues); break;
|
|
case 'H': polarization = *s++; break;
|
|
case 'I': s = ParseParameter(s, inversion, InversionValues); break;
|
|
case 'L': polarization = *s++; break;
|
|
case 'M': s = ParseParameter(s, modulation, ModulationValues); break;
|
|
case 'R': polarization = *s++; break;
|
|
case 'T': s = ParseParameter(s, transmission, TransmissionValues); break;
|
|
case 'V': polarization = *s++; break;
|
|
case 'Y': s = ParseParameter(s, hierarchy, HierarchyValues); break;
|
|
default: esyslog("ERROR: unknown parameter key '%c'", *s);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const char *cChannel::ToText(cChannel *Channel)
|
|
{
|
|
char buf[MaxChannelName * 2];
|
|
char *s = Channel->name;
|
|
if (strchr(s, ':')) {
|
|
s = strcpy(buf, s);
|
|
strreplace(s, ':', '|');
|
|
}
|
|
free(buffer);
|
|
if (Channel->groupSep) {
|
|
if (Channel->number)
|
|
asprintf(&buffer, ":@%d %s\n", Channel->number, s);
|
|
else
|
|
asprintf(&buffer, ":%s\n", s);
|
|
}
|
|
else {
|
|
char vpidbuf[32];
|
|
char *q = vpidbuf;
|
|
q += snprintf(q, sizeof(vpidbuf), "%d", Channel->vpid);
|
|
if (Channel->ppid && Channel->ppid != Channel->vpid)
|
|
q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "+%d", Channel->ppid);
|
|
*q = 0;
|
|
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)
|
|
q += snprintf(q, sizeof(apidbuf) - (q - apidbuf), ",%d", Channel->apid2);
|
|
if (Channel->dpid1 || Channel->dpid2)
|
|
q += snprintf(q, sizeof(apidbuf) - (q - apidbuf), ";%d", Channel->dpid1);
|
|
if (Channel->dpid2)
|
|
q += snprintf(q, sizeof(apidbuf) - (q - apidbuf), ",%d", Channel->dpid2);
|
|
*q = 0;
|
|
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;
|
|
}
|
|
|
|
const char *cChannel::ToText(void)
|
|
{
|
|
return ToText(this);
|
|
}
|
|
|
|
bool cChannel::Parse(const char *s, bool AllowNonUniqueID)
|
|
{
|
|
bool ok = true;
|
|
if (*s == ':') {
|
|
groupSep = true;
|
|
if (*++s == '@' && *++s) {
|
|
char *p = NULL;
|
|
errno = 0;
|
|
int n = strtol(s, &p, 10);
|
|
if (!errno && p != s && n > 0) {
|
|
number = n;
|
|
s = p;
|
|
}
|
|
}
|
|
strn0cpy(name, skipspace(s), MaxChannelName);
|
|
}
|
|
else {
|
|
groupSep = false;
|
|
char *namebuf = NULL;
|
|
char *sourcebuf = NULL;
|
|
char *parambuf = NULL;
|
|
char *vpidbuf = NULL;
|
|
char *apidbuf = NULL;
|
|
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 = atoi(caidbuf);
|
|
delete caidbuf;
|
|
caidbuf = NULL;
|
|
caids[0] = tpid;
|
|
caids[1] = 0;
|
|
tpid = 0;
|
|
}
|
|
vpid = ppid = 0;
|
|
apid1 = apid2 = 0;
|
|
dpid1 = dpid2 = 0;
|
|
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!");
|
|
return false;
|
|
}
|
|
if (!AllowNonUniqueID && Channels.GetByChannelID(GetChannelID())) {
|
|
esyslog("ERROR: channel data not unique!");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
strreplace(name, '|', ':');
|
|
return ok;
|
|
}
|
|
|
|
bool cChannel::Save(FILE *f)
|
|
{
|
|
return fprintf(f, ToText()) > 0;
|
|
}
|
|
|
|
// -- cChannels --------------------------------------------------------------
|
|
|
|
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)) {
|
|
ReNumber();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int cChannels::GetNextGroup(int Idx)
|
|
{
|
|
cChannel *channel = Get(++Idx);
|
|
while (channel && !(channel->GroupSep() && *channel->Name()))
|
|
channel = Get(++Idx);
|
|
return channel ? Idx : -1;
|
|
}
|
|
|
|
int cChannels::GetPrevGroup(int Idx)
|
|
{
|
|
cChannel *channel = Get(--Idx);
|
|
while (channel && !(channel->GroupSep() && *channel->Name()))
|
|
channel = Get(--Idx);
|
|
return channel ? Idx : -1;
|
|
}
|
|
|
|
int cChannels::GetNextNormal(int Idx)
|
|
{
|
|
cChannel *channel = Get(++Idx);
|
|
while (channel && channel->GroupSep())
|
|
channel = Get(++Idx);
|
|
return channel ? Idx : -1;
|
|
}
|
|
|
|
void cChannels::ReNumber( void )
|
|
{
|
|
int Number = 1;
|
|
for (cChannel *channel = First(); channel; channel = Next(channel)) {
|
|
if (channel->GroupSep()) {
|
|
if (channel->Number() > Number)
|
|
Number = channel->Number();
|
|
}
|
|
else {
|
|
maxNumber = Number;
|
|
channel->SetNumber(Number++);
|
|
}
|
|
}
|
|
}
|
|
|
|
cChannel *cChannels::GetByNumber(int Number, int SkipGap)
|
|
{
|
|
cChannel *previous = NULL;
|
|
for (cChannel *channel = First(); channel; channel = Next(channel)) {
|
|
if (!channel->GroupSep()) {
|
|
if (channel->Number() == Number)
|
|
return channel;
|
|
else if (SkipGap && channel->Number() > Number)
|
|
return SkipGap > 0 ? channel : previous;
|
|
previous = channel;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
cChannel *cChannels::GetByServiceID(int Source, int Transponder, unsigned short ServiceID)
|
|
{
|
|
for (cChannel *channel = First(); channel; channel = Next(channel)) {
|
|
if (!channel->GroupSep() && channel->Source() == Source && ISTRANSPONDER(channel->Transponder(), Transponder) && channel->Sid() == ServiceID)
|
|
return channel;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
cChannel *cChannels::GetByChannelID(tChannelID ChannelID, bool TryWithoutRid)
|
|
{
|
|
for (cChannel *channel = First(); channel; channel = Next(channel)) {
|
|
if (!channel->GroupSep() && channel->GetChannelID() == ChannelID)
|
|
return channel;
|
|
}
|
|
if (TryWithoutRid) {
|
|
ChannelID.ClrRid();
|
|
for (cChannel *channel = First(); channel; channel = Next(channel)) {
|
|
if (!channel->GroupSep() && channel->GetChannelID().ClrRid() == ChannelID)
|
|
return channel;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool cChannels::HasUniqueChannelID(cChannel *NewChannel, cChannel *OldChannel)
|
|
{
|
|
tChannelID NewChannelID = NewChannel->GetChannelID();
|
|
for (cChannel *channel = First(); channel; channel = Next(channel)) {
|
|
if (!channel->GroupSep() && channel != OldChannel && channel->GetChannelID() == NewChannelID)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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(const cChannel *Transponder, const char *Name, int Nid, int Tid, int Sid, int Rid)
|
|
{
|
|
if (Transponder) {
|
|
dsyslog("creating new channel '%s' on %s transponder %d with id %d-%d-%d-%d", Name, cSource::ToString(Transponder->Source()), Transponder->Transponder(), Nid, Tid, Sid, Rid);
|
|
cChannel *NewChannel = new cChannel(Transponder);
|
|
Add(NewChannel);
|
|
ReNumber();
|
|
NewChannel->SetId(Nid, Tid, Sid, Rid, false);
|
|
NewChannel->SetName(Name, false);
|
|
return NewChannel;
|
|
}
|
|
return NULL;
|
|
}
|