/* * channels.c: Channel handling * * See the main source file 'vdr.c' for copyright information and * how to reach the author. * * $Id: channels.c 1.21 2004/01/25 16:02:13 kls Exp $ */ #include "channels.h" #include #include // 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; apids[0] = 0; dpids[0] = 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(); } } static bool IntArraysDiffer(const int *a, const int *b, const char na[][4] = NULL, const char nb[][4] = NULL) { int i = 0; while (a[i] && b[i]) { if (a[i] != b[i] || na && nb && strcmp(na[i], nb[i]) != 0) return true; i++; } return a[i] != b[i] || a[i] && na && nb && strcmp(na[i], nb[i]) != 0; } static int IntArrayToString(char *s, const int *a, int Base = 10, const char n[][4] = NULL) { char *q = s; int i = 0; while (a[i] || i == 0) { q += sprintf(q, Base == 16 ? "%s%X" : "%s%d", i ? "," : "", a[i]); if (a[i] && n && *n[i]) q += sprintf(q, "=%s", n[i]); i++; } *q = 0; return q - s; } void cChannel::SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][4], int *Dpids, char DLangs[][4], int Tpid) { bool modified = vpid != Vpid || ppid != Ppid || tpid != Tpid; if (!modified) modified = IntArraysDiffer(apids, Apids, alangs, ALangs) || IntArraysDiffer(dpids, Dpids, dlangs, DLangs); if (modified) { char OldApidsBuf[MAXAPIDS * 2 * 10 + 10]; // 2: Apids and Dpids, 10: 5 digits plus delimiting ',' or ';' plus optional '=cod', +10: paranoia char NewApidsBuf[MAXAPIDS * 2 * 10 + 10]; char *q = OldApidsBuf; q += IntArrayToString(q, apids, 10, alangs); if (dpids[0]) { *q++ = ';'; q += IntArrayToString(q, dpids, 10, dlangs); } *q = 0; q = NewApidsBuf; q += IntArrayToString(q, Apids, 10, ALangs); if (Dpids[0]) { *q++ = ';'; q += IntArrayToString(q, Dpids, 10, DLangs); } *q = 0; dsyslog("changing pids of channel %d from %d+%d:%s:%d to %d+%d:%s:%d", Number(), vpid, ppid, OldApidsBuf, tpid, Vpid, Ppid, NewApidsBuf, Tpid); vpid = Vpid; ppid = Ppid; for (int i = 0; i <= MAXAPIDS; i++) { // <= to copy the terminating 0 apids[i] = Apids[i]; strn0cpy(alangs[i], ALangs[i], 4); dpids[i] = Dpids[i]; strn0cpy(dlangs[i], DLangs[i], 4); } 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 if (IntArraysDiffer(caids, CaIds)) { char OldCaIdsBuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia char NewCaIdsBuf[MAXCAIDS * 5 + 10]; IntArrayToString(OldCaIdsBuf, caids, 16); IntArrayToString(NewCaIdsBuf, CaIds, 16); dsyslog("changing caids of channel %d from %s to %s", Number(), OldCaIdsBuf, NewCaIdsBuf); for (int i = 0; i <= MAXCAIDS && CaIds[i]; i++) // <= to copy the terminating 0 caids[i] = CaIds[i]; 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 * 10 + 10]; // 2: Apids and Dpids, 10: 5 digits plus delimiting ',' or ';' plus optional '=cod', +10: paranoia q = apidbuf; q += IntArrayToString(q, Channel->apids, 10, Channel->alangs); if (Channel->dpids[0]) { *q++ = ';'; q += IntArrayToString(q, Channel->dpids, 10, Channel->dlangs); } *q = 0; char caidbuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia q = caidbuf; q += IntArrayToString(q, Channel->caids, 16); *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; apids[0] = 0; dpids[0] = 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; char *dpidbuf = strchr(apidbuf, ';'); if (dpidbuf) *dpidbuf++ = 0; p = apidbuf; char *q; int NumApids = 0; while ((q = strtok(p, ",")) != NULL) { if (NumApids < MAXAPIDS) { char *l = strchr(q, '='); if (l) { *l++ = 0; strn0cpy(alangs[NumApids], l, 4); } else *alangs[NumApids] = 0; apids[NumApids++] = strtol(q, NULL, 10); } else esyslog("ERROR: too many APIDs!"); // no need to set ok to 'false' p = NULL; } apids[NumApids] = 0; if (dpidbuf) { char *p = dpidbuf; char *q; int NumDpids = 0; while ((q = strtok(p, ",")) != NULL) { if (NumDpids < MAXAPIDS) { char *l = strchr(q, '='); if (l) { *l++ = 0; strn0cpy(dlangs[NumDpids], l, 4); } else *dlangs[NumDpids] = 0; dpids[NumDpids++] = strtol(q, NULL, 10); } else esyslog("ERROR: too many DPIDs!"); // no need to set ok to 'false' p = NULL; } dpids[NumDpids] = 0; } 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::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; }