mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
- Fixed resetting the file size when regenerating the index file. - The new function cDevice::PatPmtParser() can be used in derived devices to access the PAT/PMT of the currently replayed material. - Updated the Italian OSD texts (thanks to Diego Pierotto). - The PCR pid in generated PMTs is now set to 0x1FFF ("no PCR pid") in cPatPmtGenerator::GeneratePmt(), because VDR doesn't record the PCR pid. - Updated the Estonian OSD texts (thanks to Arthur Konovalov). - The 'sky' plugin is no longer part of the VDR source. - Improved SPU handling on devices with limited OSD capabilities (thanks to Matthieu Castet). - Several code modifications to avoid compiler warnings (thanks to Winfried Köhler). - Added stream type 11172 AUDIO to cPatPmtParser::ParsePmt() (thanks to Johann Friedrichs). - Removed debug output of '-' from cTransfer::Receive(). - Added defines for large files to the 'newplugin' script (reported by Udo Richter). - Removed the workaround for short channel names of "Kabel Deutschland", because apparently they now have their data according to the DVB standard (thanks to Johann Friedrichs). - Some fixes to dvbspu.[hc] (thanks to Johann Friedrichs). - Fixed a busy loop when moving editing marks (thanks to Johann Friedrichs). - Updated sources.conf (thanks to Derek Kelly). - Modified cCharSetConv so that it can be used to convert from "whatever VDR uses" to a given code (thanks to Joachim Wilke). - Channel names containing commas are now handled correctly in channels.conf. If a channel's short name contains a comma, it is replaced with a '.'. - cDevice now logs the device number when a new device is created. - Fixed handling STREAMTYPE_11172_AUDIO in cPatPmtParser::ParsePmt(). - cParsePatPmt now has functions to retrieve the audio, dolby and subtitle pids. - cPatFilter::Process() now only stores CA descriptors for video and audio pids (thanks to Francesco Saverio Schiavarelli for reporting a problem with channels that have some encrypted components that VDR doesn't use). - cDevice::AddPid() now stores the stream type of the given pid (thanks to Andreas Regel). - Added cFont::FontName() and cFont::Size() (thanks to Andreas Regel). - cPatPmtParser now also stores the audio stream types. - The support for full featured DVB cards of the TT/FuSi design has been moved into the new plugin 'dvbsddevice'. On systems that use such a card as their primary device, this plugin now needs to be loaded when running VDR in order to view live or recorded video. If the plugin is not loaded, the card will be treated like a budget DVB card, and there will be no OSD or viewing capability. - Fixed handling the "CA PMT" generation (revised a change not mentioned in version 1.7.9's changes, which caused a malfunction with Conax and Viaccess CAMs). - Fixed stopping subtitle display when switching the primary device (thanks to Anssi Hannula). IMPORTANT NOTE TO PLUGIN AUTHORS: a plugin that implements a derived cDevice class that can replay video must now call the MakePrimaryDevice() function of its base class. - Fixed compiler warnings "format not a string literal and no format arguments" in some syslog calls (thanks to Rolf Ahrenberg). - The new command line options --edit and --genindex can be used to edit a recording or generate its index without actually starting the entire VDR (based on a patch from Helmut Auer). - Improved the description of the transponder parameters in vdr.5 (thanks to Winfried Köhler). - Avoiding setting the video stream type to 2 if the vpid is 0 (problem reported by Arthur Konovalov). - Implemented handling the "Content Descriptor" (based on a patch from Rolf Ahrenberg). The 'classic', 'sttng' and 'curses' skins display the textual representation of the content descriptors as "genre". The epg.data file stores the genre using the tag character 'G'. - Implemented handling the "Parental Rating Descriptor" (based on a patch from Rolf Ahrenberg). The 'classic', 'sttng' and 'curses' skins display the parental rating (if given) in their event displays. The epg.data file stores the parental rating using the tag character 'R'. IMPORTANT NOTE: if VDR doesn't display a parental rating, this does not necessarily mean that the given programme is suitable for all audiences! - Rearranged cEvent members to minimize memory waste. - After a CLRE command, no further EPG processing is now done for 10 seconds, so that data sent with subsequent PUTE commands doesn't interfere with data from the broadcasters (suggested by Helmut Auer). - Added support for DVB cards with multiple fontends. Note that this only works for DVB cards where each frontend can be used independently of all the others on the same adapter. - Fixed plugin arguments corruption with glibc 2.11 on x86_64 (thanks to Anssi Hannula).
1179 lines
37 KiB
C
1179 lines
37 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 2.12 2010/01/02 17:38:40 kls Exp $
|
|
*/
|
|
|
|
#include "channels.h"
|
|
#include <linux/dvb/frontend.h>
|
|
#include <ctype.h>
|
|
#include "device.h"
|
|
#include "epg.h"
|
|
#include "timers.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, trNOOP("off") },
|
|
{ 1, INVERSION_ON, trNOOP("on") },
|
|
{ 999, INVERSION_AUTO, trNOOP("auto") },
|
|
{ -1, 0, NULL }
|
|
};
|
|
|
|
const tChannelParameterMap BandwidthValues[] = {
|
|
{ 6, 6000000, "6 MHz" },
|
|
{ 7, 7000000, "7 MHz" },
|
|
{ 8, 8000000, "8 MHz" },
|
|
{ -1, 0, NULL }
|
|
};
|
|
|
|
const tChannelParameterMap CoderateValues[] = {
|
|
{ 0, FEC_NONE, trNOOP("none") },
|
|
{ 12, FEC_1_2, "1/2" },
|
|
{ 23, FEC_2_3, "2/3" },
|
|
{ 34, FEC_3_4, "3/4" },
|
|
{ 35, FEC_3_5, "3/5" },
|
|
{ 45, FEC_4_5, "4/5" },
|
|
{ 56, FEC_5_6, "5/6" },
|
|
{ 67, FEC_6_7, "6/7" },
|
|
{ 78, FEC_7_8, "7/8" },
|
|
{ 89, FEC_8_9, "8/9" },
|
|
{ 910, FEC_9_10, "9/10" },
|
|
{ 999, FEC_AUTO, trNOOP("auto") },
|
|
{ -1, 0, NULL }
|
|
};
|
|
|
|
const tChannelParameterMap ModulationValues[] = {
|
|
{ 16, QAM_16, "QAM16" },
|
|
{ 32, QAM_32, "QAM32" },
|
|
{ 64, QAM_64, "QAM64" },
|
|
{ 128, QAM_128, "QAM128" },
|
|
{ 256, QAM_256, "QAM256" },
|
|
{ 2, QPSK, "QPSK" },
|
|
{ 5, PSK_8, "8PSK" },
|
|
{ 6, APSK_16, "16APSK" },
|
|
{ 10, VSB_8, "VSB8" },
|
|
{ 11, VSB_16, "VSB16" },
|
|
{ 998, QAM_AUTO, "QAMAUTO" },
|
|
{ -1, 0, NULL }
|
|
};
|
|
|
|
const tChannelParameterMap SystemValues[] = {
|
|
{ 0, SYS_DVBS, "DVB-S" },
|
|
{ 1, SYS_DVBS2, "DVB-S2" },
|
|
{ -1, 0, NULL }
|
|
};
|
|
|
|
const tChannelParameterMap TransmissionValues[] = {
|
|
{ 2, TRANSMISSION_MODE_2K, "2K" },
|
|
{ 8, TRANSMISSION_MODE_8K, "8K" },
|
|
{ 999, TRANSMISSION_MODE_AUTO, trNOOP("auto") },
|
|
{ -1, 0, NULL }
|
|
};
|
|
|
|
const tChannelParameterMap GuardValues[] = {
|
|
{ 4, GUARD_INTERVAL_1_4, "1/4" },
|
|
{ 8, GUARD_INTERVAL_1_8, "1/8" },
|
|
{ 16, GUARD_INTERVAL_1_16, "1/16" },
|
|
{ 32, GUARD_INTERVAL_1_32, "1/32" },
|
|
{ 999, GUARD_INTERVAL_AUTO, trNOOP("auto") },
|
|
{ -1, 0, NULL }
|
|
};
|
|
|
|
const tChannelParameterMap HierarchyValues[] = {
|
|
{ 0, HIERARCHY_NONE, trNOOP("none") },
|
|
{ 1, HIERARCHY_1, "1" },
|
|
{ 2, HIERARCHY_2, "2" },
|
|
{ 4, HIERARCHY_4, "4" },
|
|
{ 999, HIERARCHY_AUTO, trNOOP("auto") },
|
|
{ -1, 0, NULL }
|
|
};
|
|
|
|
const tChannelParameterMap RollOffValues[] = {
|
|
{ 0, ROLLOFF_AUTO, trNOOP("auto") },
|
|
{ 20, ROLLOFF_20, "0.20" },
|
|
{ 25, ROLLOFF_25, "0.25" },
|
|
{ 35, ROLLOFF_35, "0.35" },
|
|
{ -1, 0, NULL }
|
|
};
|
|
|
|
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, const char **String)
|
|
{
|
|
int n = DriverIndex(Value, Map);
|
|
if (n >= 0) {
|
|
if (String)
|
|
*String = tr(Map[n].userString);
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
cString tChannelID::ToString(void) const
|
|
{
|
|
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;
|
|
}
|
|
|
|
tChannelID &tChannelID::ClrPolarization(void)
|
|
{
|
|
while (tid > 100000)
|
|
tid -= 100000;
|
|
return *this;
|
|
}
|
|
|
|
// --- cChannel --------------------------------------------------------------
|
|
|
|
cChannel::cChannel(void)
|
|
{
|
|
name = strdup("");
|
|
shortName = strdup("");
|
|
provider = strdup("");
|
|
portalName = strdup("");
|
|
memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__);
|
|
inversion = INVERSION_AUTO;
|
|
bandwidth = 8000000;
|
|
coderateH = FEC_AUTO;
|
|
coderateL = FEC_AUTO;
|
|
modulation = QPSK;
|
|
system = SYS_DVBS;
|
|
transmission = TRANSMISSION_MODE_AUTO;
|
|
guard = GUARD_INTERVAL_AUTO;
|
|
hierarchy = HIERARCHY_AUTO;
|
|
rollOff = ROLLOFF_AUTO;
|
|
modification = CHANNELMOD_NONE;
|
|
schedule = NULL;
|
|
linkChannels = NULL;
|
|
refChannel = NULL;
|
|
}
|
|
|
|
cChannel::cChannel(const cChannel &Channel)
|
|
{
|
|
name = NULL;
|
|
shortName = NULL;
|
|
provider = NULL;
|
|
portalName = NULL;
|
|
schedule = NULL;
|
|
linkChannels = NULL;
|
|
refChannel = NULL;
|
|
*this = Channel;
|
|
}
|
|
|
|
cChannel::~cChannel()
|
|
{
|
|
delete linkChannels;
|
|
linkChannels = NULL; // more than one channel can link to this one, so we need the following loop
|
|
for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
|
|
if (Channel->linkChannels) {
|
|
for (cLinkChannel *lc = Channel->linkChannels->First(); lc; lc = Channel->linkChannels->Next(lc)) {
|
|
if (lc->Channel() == this) {
|
|
Channel->linkChannels->Del(lc);
|
|
break;
|
|
}
|
|
}
|
|
if (Channel->linkChannels->Count() == 0) {
|
|
delete Channel->linkChannels;
|
|
Channel->linkChannels = NULL;
|
|
}
|
|
}
|
|
}
|
|
free(name);
|
|
free(shortName);
|
|
free(provider);
|
|
free(portalName);
|
|
}
|
|
|
|
cChannel& cChannel::operator= (const cChannel &Channel)
|
|
{
|
|
name = strcpyrealloc(name, Channel.name);
|
|
shortName = strcpyrealloc(shortName, Channel.shortName);
|
|
provider = strcpyrealloc(provider, Channel.provider);
|
|
portalName = strcpyrealloc(portalName, Channel.portalName);
|
|
memcpy(&__BeginData__, &Channel.__BeginData__, (char *)&Channel.__EndData__ - (char *)&Channel.__BeginData__);
|
|
return *this;
|
|
}
|
|
|
|
int cChannel::Transponder(int Frequency, char Polarization)
|
|
{
|
|
// some satellites have transponders at the same frequency, just with different polarization:
|
|
switch (tolower(Polarization)) {
|
|
case 'h': Frequency += 100000; break;
|
|
case 'v': Frequency += 200000; break;
|
|
case 'l': Frequency += 300000; break;
|
|
case 'r': Frequency += 400000; break;
|
|
default: esyslog("ERROR: invalid value for Polarization '%c'", Polarization);
|
|
}
|
|
return Frequency;
|
|
}
|
|
|
|
int cChannel::Transponder(void) const
|
|
{
|
|
int tf = frequency;
|
|
while (tf > 20000)
|
|
tf /= 1000;
|
|
if (IsSat())
|
|
tf = Transponder(tf, polarization);
|
|
return tf;
|
|
}
|
|
|
|
bool cChannel::HasTimer(void) const
|
|
{
|
|
for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) {
|
|
if (Timer->Channel() == this)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int cChannel::Modification(int Mask)
|
|
{
|
|
int Result = modification & Mask;
|
|
modification = CHANNELMOD_NONE;
|
|
return Result;
|
|
}
|
|
|
|
void cChannel::CopyTransponderData(const cChannel *Channel)
|
|
{
|
|
if (Channel) {
|
|
frequency = Channel->frequency;
|
|
source = Channel->source;
|
|
srate = Channel->srate;
|
|
polarization = Channel->polarization;
|
|
inversion = Channel->inversion;
|
|
bandwidth = Channel->bandwidth;
|
|
coderateH = Channel->coderateH;
|
|
coderateL = Channel->coderateL;
|
|
modulation = Channel->modulation;
|
|
system = Channel->system;
|
|
transmission = Channel->transmission;
|
|
guard = Channel->guard;
|
|
hierarchy = Channel->hierarchy;
|
|
rollOff = Channel->rollOff;
|
|
}
|
|
}
|
|
|
|
bool cChannel::SetSatTransponderData(int Source, int Frequency, char Polarization, int Srate, int CoderateH, int Modulation, int System, int RollOff)
|
|
{
|
|
// 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;
|
|
// Sometimes the symbol rate is off by one
|
|
if (abs(srate - Srate) <= 1)
|
|
Srate = srate;
|
|
|
|
if (source != Source || frequency != Frequency || polarization != Polarization || srate != Srate || coderateH != CoderateH || modulation != Modulation || system != System || rollOff != RollOff) {
|
|
cString OldTransponderData = TransponderDataToString();
|
|
source = Source;
|
|
frequency = Frequency;
|
|
polarization = Polarization;
|
|
srate = Srate;
|
|
coderateH = CoderateH;
|
|
modulation = Modulation;
|
|
system = System;
|
|
rollOff = RollOff;
|
|
schedule = NULL;
|
|
if (Number()) {
|
|
dsyslog("changing transponder data of channel %d from %s to %s", Number(), *OldTransponderData, *TransponderDataToString());
|
|
modification |= CHANNELMOD_TRANSP;
|
|
Channels.SetModified();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool cChannel::SetCableTransponderData(int Source, int Frequency, int Modulation, int Srate, int CoderateH)
|
|
{
|
|
if (source != Source || frequency != Frequency || modulation != Modulation || srate != Srate || coderateH != CoderateH) {
|
|
cString OldTransponderData = TransponderDataToString();
|
|
source = Source;
|
|
frequency = Frequency;
|
|
modulation = Modulation;
|
|
srate = Srate;
|
|
coderateH = CoderateH;
|
|
schedule = NULL;
|
|
if (Number()) {
|
|
dsyslog("changing transponder data of channel %d from %s to %s", Number(), *OldTransponderData, *TransponderDataToString());
|
|
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)
|
|
{
|
|
if (source != Source || frequency != Frequency || bandwidth != Bandwidth || modulation != Modulation || hierarchy != Hierarchy || coderateH != CoderateH || coderateL != CoderateL || guard != Guard || transmission != Transmission) {
|
|
cString OldTransponderData = TransponderDataToString();
|
|
source = Source;
|
|
frequency = Frequency;
|
|
bandwidth = Bandwidth;
|
|
modulation = Modulation;
|
|
hierarchy = Hierarchy;
|
|
coderateH = CoderateH;
|
|
coderateL = CoderateL;
|
|
guard = Guard;
|
|
transmission = Transmission;
|
|
schedule = NULL;
|
|
if (Number()) {
|
|
dsyslog("changing transponder data of channel %d from %s to %s", Number(), *OldTransponderData, *TransponderDataToString());
|
|
modification |= CHANNELMOD_TRANSP;
|
|
Channels.SetModified();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void cChannel::SetId(int Nid, int Tid, int Sid, int Rid)
|
|
{
|
|
if (nid != Nid || tid != Tid || sid != Sid || rid != Rid) {
|
|
if (Number()) {
|
|
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);
|
|
modification |= CHANNELMOD_ID;
|
|
Channels.SetModified();
|
|
Channels.UnhashChannel(this);
|
|
}
|
|
nid = Nid;
|
|
tid = Tid;
|
|
sid = Sid;
|
|
rid = Rid;
|
|
if (Number())
|
|
Channels.HashChannel(this);
|
|
schedule = NULL;
|
|
}
|
|
}
|
|
|
|
void cChannel::SetName(const char *Name, const char *ShortName, const char *Provider)
|
|
{
|
|
if (!isempty(Name)) {
|
|
bool nn = strcmp(name, Name) != 0;
|
|
bool ns = strcmp(shortName, ShortName) != 0;
|
|
bool np = strcmp(provider, Provider) != 0;
|
|
if (nn || ns || np) {
|
|
if (Number()) {
|
|
dsyslog("changing name of channel %d from '%s,%s;%s' to '%s,%s;%s'", Number(), name, shortName, provider, Name, ShortName, Provider);
|
|
modification |= CHANNELMOD_NAME;
|
|
Channels.SetModified();
|
|
}
|
|
if (nn)
|
|
name = strcpyrealloc(name, Name);
|
|
if (ns)
|
|
shortName = strcpyrealloc(shortName, ShortName);
|
|
if (np)
|
|
provider = strcpyrealloc(provider, Provider);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cChannel::SetPortalName(const char *PortalName)
|
|
{
|
|
if (!isempty(PortalName) && strcmp(portalName, PortalName) != 0) {
|
|
if (Number()) {
|
|
dsyslog("changing portal name of channel %d from '%s' to '%s'", Number(), portalName, PortalName);
|
|
modification |= CHANNELMOD_NAME;
|
|
Channels.SetModified();
|
|
}
|
|
portalName = strcpyrealloc(portalName, PortalName);
|
|
}
|
|
}
|
|
|
|
#define STRDIFF 0x01
|
|
#define VALDIFF 0x02
|
|
|
|
static int IntArraysDiffer(const int *a, const int *b, const char na[][MAXLANGCODE2] = NULL, const char nb[][MAXLANGCODE2] = NULL)
|
|
{
|
|
int result = 0;
|
|
for (int i = 0; a[i] || b[i]; i++) {
|
|
if (a[i] && na && nb && strcmp(na[i], nb[i]) != 0)
|
|
result |= STRDIFF;
|
|
if (a[i] != b[i])
|
|
result |= VALDIFF;
|
|
if (!a[i] || !b[i])
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int IntArrayToString(char *s, const int *a, int Base = 10, const char n[][MAXLANGCODE2] = 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]);
|
|
if (!a[i])
|
|
break;
|
|
i++;
|
|
}
|
|
*q = 0;
|
|
return q - s;
|
|
}
|
|
|
|
void cChannel::SetPids(int Vpid, int Ppid, int Vtype, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid)
|
|
{
|
|
int mod = CHANNELMOD_NONE;
|
|
if (vpid != Vpid || ppid != Ppid || vtype != Vtype || tpid != Tpid)
|
|
mod |= CHANNELMOD_PIDS;
|
|
int m = IntArraysDiffer(apids, Apids, alangs, ALangs) | IntArraysDiffer(dpids, Dpids, dlangs, DLangs) | IntArraysDiffer(spids, Spids, slangs, SLangs);
|
|
if (m & STRDIFF)
|
|
mod |= CHANNELMOD_LANGS;
|
|
if (m & VALDIFF)
|
|
mod |= CHANNELMOD_PIDS;
|
|
if (mod) {
|
|
const int BufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod', +10: paranoia
|
|
char OldApidsBuf[BufferSize];
|
|
char NewApidsBuf[BufferSize];
|
|
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;
|
|
const int SBufferSize = MAXSPIDS * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod', +10: paranoia
|
|
char OldSpidsBuf[SBufferSize];
|
|
char NewSpidsBuf[SBufferSize];
|
|
q = OldSpidsBuf;
|
|
q += IntArrayToString(q, spids, 10, slangs);
|
|
*q = 0;
|
|
q = NewSpidsBuf;
|
|
q += IntArrayToString(q, Spids, 10, SLangs);
|
|
*q = 0;
|
|
if (Number())
|
|
dsyslog("changing pids of channel %d from %d+%d=%d:%s:%s:%d to %d+%d=%d:%s:%s:%d", Number(), vpid, ppid, vtype, OldApidsBuf, OldSpidsBuf, tpid, Vpid, Ppid, Vtype, NewApidsBuf, NewSpidsBuf, Tpid);
|
|
vpid = Vpid;
|
|
ppid = Ppid;
|
|
vtype = Vtype;
|
|
for (int i = 0; i < MAXAPIDS; i++) {
|
|
apids[i] = Apids[i];
|
|
strn0cpy(alangs[i], ALangs[i], MAXLANGCODE2);
|
|
}
|
|
apids[MAXAPIDS] = 0;
|
|
for (int i = 0; i < MAXDPIDS; i++) {
|
|
dpids[i] = Dpids[i];
|
|
strn0cpy(dlangs[i], DLangs[i], MAXLANGCODE2);
|
|
}
|
|
dpids[MAXDPIDS] = 0;
|
|
for (int i = 0; i < MAXSPIDS; i++) {
|
|
spids[i] = Spids[i];
|
|
strn0cpy(slangs[i], SLangs[i], MAXLANGCODE2);
|
|
}
|
|
spids[MAXSPIDS] = 0;
|
|
tpid = Tpid;
|
|
modification |= mod;
|
|
Channels.SetModified();
|
|
}
|
|
}
|
|
|
|
void cChannel::SetSubtitlingDescriptors(uchar *SubtitlingTypes, uint16_t *CompositionPageIds, uint16_t *AncillaryPageIds)
|
|
{
|
|
if (SubtitlingTypes) {
|
|
for (int i = 0; i < MAXSPIDS; i++)
|
|
subtitlingTypes[i] = SubtitlingTypes[i];
|
|
}
|
|
if (CompositionPageIds) {
|
|
for (int i = 0; i < MAXSPIDS; i++)
|
|
compositionPageIds[i] = CompositionPageIds[i];
|
|
}
|
|
if (AncillaryPageIds) {
|
|
for (int i = 0; i < MAXSPIDS; i++)
|
|
ancillaryPageIds[i] = AncillaryPageIds[i];
|
|
}
|
|
}
|
|
|
|
void cChannel::SetCaIds(const int *CaIds)
|
|
{
|
|
if (caids[0] && caids[0] <= CA_USER_MAX)
|
|
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);
|
|
if (Number())
|
|
dsyslog("changing caids of channel %d from %s to %s", Number(), OldCaIdsBuf, NewCaIdsBuf);
|
|
for (int i = 0; i <= MAXCAIDS; i++) { // <= to copy the terminating 0
|
|
caids[i] = CaIds[i];
|
|
if (!CaIds[i])
|
|
break;
|
|
}
|
|
modification |= CHANNELMOD_CA;
|
|
Channels.SetModified();
|
|
}
|
|
}
|
|
|
|
void cChannel::SetCaDescriptors(int Level)
|
|
{
|
|
if (Level > 0) {
|
|
modification |= CHANNELMOD_CA;
|
|
Channels.SetModified();
|
|
if (Number() && Level > 1)
|
|
dsyslog("changing ca descriptors of channel %d", Number());
|
|
}
|
|
}
|
|
|
|
void cChannel::SetLinkChannels(cLinkChannels *LinkChannels)
|
|
{
|
|
if (!linkChannels && !LinkChannels)
|
|
return;
|
|
if (linkChannels && LinkChannels) {
|
|
cLinkChannel *lca = linkChannels->First();
|
|
cLinkChannel *lcb = LinkChannels->First();
|
|
while (lca && lcb) {
|
|
if (lca->Channel() != lcb->Channel()) {
|
|
lca = NULL;
|
|
break;
|
|
}
|
|
lca = linkChannels->Next(lca);
|
|
lcb = LinkChannels->Next(lcb);
|
|
}
|
|
if (!lca && !lcb) {
|
|
delete LinkChannels;
|
|
return; // linkage has not changed
|
|
}
|
|
}
|
|
char buffer[((linkChannels ? linkChannels->Count() : 0) + (LinkChannels ? LinkChannels->Count() : 0)) * 6 + 256]; // 6: 5 digit channel number plus blank, 256: other texts (see below) plus reserve
|
|
char *q = buffer;
|
|
q += sprintf(q, "linking channel %d from", Number());
|
|
if (linkChannels) {
|
|
for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) {
|
|
lc->Channel()->SetRefChannel(NULL);
|
|
q += sprintf(q, " %d", lc->Channel()->Number());
|
|
}
|
|
delete linkChannels;
|
|
}
|
|
else
|
|
q += sprintf(q, " none");
|
|
q += sprintf(q, " to");
|
|
linkChannels = LinkChannels;
|
|
if (linkChannels) {
|
|
for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) {
|
|
lc->Channel()->SetRefChannel(this);
|
|
q += sprintf(q, " %d", lc->Channel()->Number());
|
|
//dsyslog("link %4d -> %4d: %s", Number(), lc->Channel()->Number(), lc->Channel()->Name());
|
|
}
|
|
}
|
|
else
|
|
q += sprintf(q, " none");
|
|
if (Number())
|
|
dsyslog("%s", buffer);
|
|
}
|
|
|
|
void cChannel::SetRefChannel(cChannel *RefChannel)
|
|
{
|
|
refChannel = RefChannel;
|
|
}
|
|
|
|
static int PrintParameter(char *p, char Name, int Value)
|
|
{
|
|
return Value >= 0 && Value != 999 ? sprintf(p, "%c%d", Name, Value) : 0;
|
|
}
|
|
|
|
cString cChannel::TransponderDataToString(void) const
|
|
{
|
|
if (cSource::IsTerr(source))
|
|
return cString::sprintf("%d:%s:%s", frequency, *ParametersToString(), *cSource::ToString(source));
|
|
return cString::sprintf("%d:%s:%s:%d", frequency, *ParametersToString(), *cSource::ToString(source), srate);
|
|
}
|
|
|
|
cString cChannel::ParametersToString(void) const
|
|
{
|
|
char type = **cSource::ToString(source);
|
|
if (isdigit(type))
|
|
type = 'S';
|
|
#define ST(s) if (strchr(s, type))
|
|
char buffer[64];
|
|
char *q = buffer;
|
|
*q = 0;
|
|
ST(" S ") q += sprintf(q, "%c", polarization);
|
|
ST(" T") q += PrintParameter(q, 'B', MapToUser(bandwidth, BandwidthValues));
|
|
ST("CST") q += PrintParameter(q, 'C', MapToUser(coderateH, CoderateValues));
|
|
ST(" T") q += PrintParameter(q, 'D', MapToUser(coderateL, CoderateValues));
|
|
ST(" T") q += PrintParameter(q, 'G', MapToUser(guard, GuardValues));
|
|
ST("CST") q += PrintParameter(q, 'I', MapToUser(inversion, InversionValues));
|
|
ST("CST") q += PrintParameter(q, 'M', MapToUser(modulation, ModulationValues));
|
|
ST(" S ") q += PrintParameter(q, 'O', MapToUser(rollOff, RollOffValues));
|
|
ST(" S ") q += PrintParameter(q, 'S', MapToUser(system, SystemValues));
|
|
ST(" T") q += PrintParameter(q, 'T', MapToUser(transmission, TransmissionValues));
|
|
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: invalid value for parameter '%c'", *(s - 1));
|
|
return NULL;
|
|
}
|
|
|
|
static const char *SkipDigits(const char *s)
|
|
{
|
|
while (*++s && isdigit(*s))
|
|
;
|
|
return s;
|
|
}
|
|
|
|
bool cChannel::StringToParameters(const char *s)
|
|
{
|
|
while (s && *s) {
|
|
switch (toupper(*s)) {
|
|
case 'A': s = SkipDigits(s); break; // for compatibility with the "multiproto" approach - may be removed in future versions
|
|
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 'O': s = ParseParameter(s, rollOff, RollOffValues); break;
|
|
case 'P': s = SkipDigits(s); break; // for compatibility with the "multiproto" approach - may be removed in future versions
|
|
case 'R': polarization = *s++; break;
|
|
case 'S': s = ParseParameter(s, system, SystemValues); break;
|
|
case 'T': s = ParseParameter(s, transmission, TransmissionValues); break;
|
|
case 'V': polarization = *s++; break;
|
|
case 'Y': s = ParseParameter(s, hierarchy, HierarchyValues); break;
|
|
case 'Z': s = SkipDigits(s); break; // for compatibility with the original DVB-S2 patch - may be removed in future versions
|
|
default: esyslog("ERROR: unknown parameter key '%c'", *s);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
cString cChannel::ToText(const cChannel *Channel)
|
|
{
|
|
char FullName[strlen(Channel->name) + 1 + strlen(Channel->shortName) + 1 + strlen(Channel->provider) + 1 + 10]; // +10: paranoia
|
|
char *q = FullName;
|
|
q += sprintf(q, "%s", Channel->name);
|
|
if (!isempty(Channel->shortName))
|
|
q += sprintf(q, ",%s", Channel->shortName);
|
|
else if (strchr(Channel->name, ','))
|
|
q += sprintf(q, ",");
|
|
if (!isempty(Channel->provider))
|
|
q += sprintf(q, ";%s", Channel->provider);
|
|
*q = 0;
|
|
strreplace(FullName, ':', '|');
|
|
cString buffer;
|
|
if (Channel->groupSep) {
|
|
if (Channel->number)
|
|
buffer = cString::sprintf(":@%d %s\n", Channel->number, FullName);
|
|
else
|
|
buffer = cString::sprintf(":%s\n", FullName);
|
|
}
|
|
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);
|
|
if (Channel->vpid && Channel->vtype)
|
|
q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "=%d", Channel->vtype);
|
|
*q = 0;
|
|
const int BufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod', +10: paranoia
|
|
char apidbuf[BufferSize];
|
|
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;
|
|
buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%d:%s:%d:%d:%d:%d\n", FullName, 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;
|
|
}
|
|
|
|
cString cChannel::ToText(void) const
|
|
{
|
|
return ToText(this);
|
|
}
|
|
|
|
bool cChannel::Parse(const char *s)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
name = strcpyrealloc(name, skipspace(s));
|
|
strreplace(name, '|', ':');
|
|
}
|
|
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;
|
|
vtype = 0;
|
|
apids[0] = 0;
|
|
dpids[0] = 0;
|
|
ok = false;
|
|
if (parambuf && sourcebuf && vpidbuf && apidbuf) {
|
|
ok = StringToParameters(parambuf) && (source = cSource::FromString(sourcebuf)) >= 0;
|
|
|
|
char *p;
|
|
if ((p = strchr(vpidbuf, '=')) != NULL) {
|
|
*p++ = 0;
|
|
if (sscanf(p, "%d", &vtype) != 1)
|
|
return false;
|
|
}
|
|
if ((p = strchr(vpidbuf, '+')) != NULL) {
|
|
*p++ = 0;
|
|
if (sscanf(p, "%d", &ppid) != 1)
|
|
return false;
|
|
}
|
|
if (sscanf(vpidbuf, "%d", &vpid) != 1)
|
|
return false;
|
|
if (!ppid)
|
|
ppid = vpid;
|
|
if (vpid && !vtype)
|
|
vtype = 2; // default is MPEG-2
|
|
|
|
char *dpidbuf = strchr(apidbuf, ';');
|
|
if (dpidbuf)
|
|
*dpidbuf++ = 0;
|
|
p = apidbuf;
|
|
char *q;
|
|
int NumApids = 0;
|
|
char *strtok_next;
|
|
while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
|
|
if (NumApids < MAXAPIDS) {
|
|
char *l = strchr(q, '=');
|
|
if (l) {
|
|
*l++ = 0;
|
|
strn0cpy(alangs[NumApids], l, MAXLANGCODE2);
|
|
}
|
|
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;
|
|
char *strtok_next;
|
|
while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
|
|
if (NumDpids < MAXDPIDS) {
|
|
char *l = strchr(q, '=');
|
|
if (l) {
|
|
*l++ = 0;
|
|
strn0cpy(dlangs[NumDpids], l, MAXLANGCODE2);
|
|
}
|
|
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;
|
|
char *strtok_next;
|
|
while ((q = strtok_r(p, ",", &strtok_next)) != NULL) {
|
|
if (NumCaIds < MAXCAIDS) {
|
|
caids[NumCaIds++] = strtol(q, NULL, 16) & 0xFFFF;
|
|
if (NumCaIds == 1 && caids[0] <= CA_USER_MAX)
|
|
break;
|
|
}
|
|
else
|
|
esyslog("ERROR: too many CA ids!"); // no need to set ok to 'false'
|
|
p = NULL;
|
|
}
|
|
caids[NumCaIds] = 0;
|
|
}
|
|
}
|
|
strreplace(namebuf, '|', ':');
|
|
|
|
char *p = strchr(namebuf, ';');
|
|
if (p) {
|
|
*p++ = 0;
|
|
provider = strcpyrealloc(provider, p);
|
|
}
|
|
p = strrchr(namebuf, ','); // long name might contain a ',', so search for the rightmost one
|
|
if (p) {
|
|
*p++ = 0;
|
|
shortName = strcpyrealloc(shortName, p);
|
|
}
|
|
name = strcpyrealloc(name, namebuf);
|
|
|
|
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;
|
|
}
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool cChannel::Save(FILE *f)
|
|
{
|
|
return fprintf(f, "%s", *ToText()) > 0;
|
|
}
|
|
|
|
// --- cChannelSorter --------------------------------------------------------
|
|
|
|
class cChannelSorter : public cListObject {
|
|
public:
|
|
cChannel *channel;
|
|
tChannelID channelID;
|
|
cChannelSorter(cChannel *Channel) {
|
|
channel = Channel;
|
|
channelID = channel->GetChannelID();
|
|
}
|
|
virtual int Compare(const cListObject &ListObject) const {
|
|
cChannelSorter *cs = (cChannelSorter *)&ListObject;
|
|
return memcmp(&channelID, &cs->channelID, sizeof(channelID));
|
|
}
|
|
};
|
|
|
|
// --- cChannels -------------------------------------------------------------
|
|
|
|
cChannels Channels;
|
|
|
|
cChannels::cChannels(void)
|
|
{
|
|
maxNumber = 0;
|
|
modified = CHANNELSMOD_NONE;
|
|
}
|
|
|
|
void cChannels::DeleteDuplicateChannels(void)
|
|
{
|
|
cList<cChannelSorter> ChannelSorter;
|
|
for (cChannel *channel = First(); channel; channel = Next(channel)) {
|
|
if (!channel->GroupSep())
|
|
ChannelSorter.Add(new cChannelSorter(channel));
|
|
}
|
|
ChannelSorter.Sort();
|
|
cChannelSorter *cs = ChannelSorter.First();
|
|
while (cs) {
|
|
cChannelSorter *next = ChannelSorter.Next(cs);
|
|
if (next && cs->channelID == next->channelID) {
|
|
dsyslog("deleting duplicate channel %s", *next->channel->ToText());
|
|
Del(next->channel);
|
|
}
|
|
cs = next;
|
|
}
|
|
}
|
|
|
|
bool cChannels::Load(const char *FileName, bool AllowComments, bool MustExist)
|
|
{
|
|
if (cConfig<cChannel>::Load(FileName, AllowComments, MustExist)) {
|
|
DeleteDuplicateChannels();
|
|
ReNumber();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void cChannels::HashChannel(cChannel *Channel)
|
|
{
|
|
channelsHashSid.Add(Channel, Channel->Sid());
|
|
}
|
|
|
|
void cChannels::UnhashChannel(cChannel *Channel)
|
|
{
|
|
channelsHashSid.Del(Channel, Channel->Sid());
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int cChannels::GetPrevNormal(int Idx)
|
|
{
|
|
cChannel *channel = Get(--Idx);
|
|
while (channel && channel->GroupSep())
|
|
channel = Get(--Idx);
|
|
return channel ? Idx : -1;
|
|
}
|
|
|
|
void cChannels::ReNumber(void)
|
|
{
|
|
channelsHashSid.Clear();
|
|
maxNumber = 0;
|
|
int Number = 1;
|
|
for (cChannel *channel = First(); channel; channel = Next(channel)) {
|
|
if (channel->GroupSep()) {
|
|
if (channel->Number() > Number)
|
|
Number = channel->Number();
|
|
}
|
|
else {
|
|
HashChannel(channel);
|
|
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)
|
|
{
|
|
cList<cHashObject> *list = channelsHashSid.GetList(ServiceID);
|
|
if (list) {
|
|
for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
|
|
cChannel *channel = (cChannel *)hobj->Object();
|
|
if (channel->Sid() == ServiceID && channel->Source() == Source && ISTRANSPONDER(channel->Transponder(), Transponder))
|
|
return channel;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
cChannel *cChannels::GetByChannelID(tChannelID ChannelID, bool TryWithoutRid, bool TryWithoutPolarization)
|
|
{
|
|
int sid = ChannelID.Sid();
|
|
cList<cHashObject> *list = channelsHashSid.GetList(sid);
|
|
if (list) {
|
|
for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
|
|
cChannel *channel = (cChannel *)hobj->Object();
|
|
if (channel->Sid() == sid && channel->GetChannelID() == ChannelID)
|
|
return channel;
|
|
}
|
|
if (TryWithoutRid) {
|
|
ChannelID.ClrRid();
|
|
for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
|
|
cChannel *channel = (cChannel *)hobj->Object();
|
|
if (channel->Sid() == sid && channel->GetChannelID().ClrRid() == ChannelID)
|
|
return channel;
|
|
}
|
|
}
|
|
if (TryWithoutPolarization) {
|
|
ChannelID.ClrPolarization();
|
|
for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) {
|
|
cChannel *channel = (cChannel *)hobj->Object();
|
|
if (channel->Sid() == sid && channel->GetChannelID().ClrPolarization() == ChannelID)
|
|
return channel;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
cChannel *cChannels::GetByTransponderID(tChannelID ChannelID)
|
|
{
|
|
int source = ChannelID.Source();
|
|
int nid = ChannelID.Nid();
|
|
int tid = ChannelID.Tid();
|
|
for (cChannel *channel = First(); channel; channel = Next(channel)) {
|
|
if (channel->Tid() == tid && channel->Nid() == nid && channel->Source() == source)
|
|
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(bool ByUser)
|
|
{
|
|
modified = ByUser ? CHANNELSMOD_USER : !modified ? CHANNELSMOD_AUTO : modified;
|
|
}
|
|
|
|
int cChannels::Modified(void)
|
|
{
|
|
int Result = modified;
|
|
modified = CHANNELSMOD_NONE;
|
|
return Result;
|
|
}
|
|
|
|
cChannel *cChannels::NewChannel(const cChannel *Transponder, const char *Name, const char *ShortName, const char *Provider, int Nid, int Tid, int Sid, int Rid)
|
|
{
|
|
if (Transponder) {
|
|
dsyslog("creating new channel '%s,%s;%s' on %s transponder %d with id %d-%d-%d-%d", Name, ShortName, Provider, *cSource::ToString(Transponder->Source()), Transponder->Transponder(), Nid, Tid, Sid, Rid);
|
|
cChannel *NewChannel = new cChannel;
|
|
NewChannel->CopyTransponderData(Transponder);
|
|
NewChannel->SetId(Nid, Tid, Sid, Rid);
|
|
NewChannel->SetName(Name, ShortName, Provider);
|
|
Add(NewChannel);
|
|
ReNumber();
|
|
return NewChannel;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
cString ChannelString(const cChannel *Channel, int Number)
|
|
{
|
|
char buffer[256];
|
|
if (Channel) {
|
|
if (Channel->GroupSep())
|
|
snprintf(buffer, sizeof(buffer), "%s", Channel->Name());
|
|
else
|
|
snprintf(buffer, sizeof(buffer), "%d%s %s", Channel->Number(), Number ? "-" : "", Channel->Name());
|
|
}
|
|
else if (Number)
|
|
snprintf(buffer, sizeof(buffer), "%d-", Number);
|
|
else
|
|
snprintf(buffer, sizeof(buffer), "%s", tr("*** Invalid Channel ***"));
|
|
return buffer;
|
|
}
|