vdr
1.7.27
|
00001 /* 00002 * channels.c: Channel handling 00003 * 00004 * See the main source file 'vdr.c' for copyright information and 00005 * how to reach the author. 00006 * 00007 * $Id: channels.c 2.21 2012/03/11 13:29:06 kls Exp $ 00008 */ 00009 00010 #include "channels.h" 00011 #include <ctype.h> 00012 #include "device.h" 00013 #include "epg.h" 00014 #include "libsi/si.h" 00015 #include "timers.h" 00016 00017 // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d' 00018 // format characters in order to allow any number of blanks after a numeric 00019 // value! 00020 00021 // --- tChannelID ------------------------------------------------------------ 00022 00023 const tChannelID tChannelID::InvalidID; 00024 00025 tChannelID tChannelID::FromString(const char *s) 00026 { 00027 char *sourcebuf = NULL; 00028 int nid; 00029 int tid; 00030 int sid; 00031 int rid = 0; 00032 int fields = sscanf(s, "%a[^-]-%d-%d-%d-%d", &sourcebuf, &nid, &tid, &sid, &rid); 00033 if (fields == 4 || fields == 5) { 00034 int source = cSource::FromString(sourcebuf); 00035 free(sourcebuf); 00036 if (source >= 0) 00037 return tChannelID(source, nid, tid, sid, rid); 00038 } 00039 return tChannelID::InvalidID; 00040 } 00041 00042 cString tChannelID::ToString(void) const 00043 { 00044 char buffer[256]; 00045 snprintf(buffer, sizeof(buffer), rid ? "%s-%d-%d-%d-%d" : "%s-%d-%d-%d", *cSource::ToString(source), nid, tid, sid, rid); 00046 return buffer; 00047 } 00048 00049 tChannelID &tChannelID::ClrPolarization(void) 00050 { 00051 while (tid > 100000) 00052 tid -= 100000; 00053 return *this; 00054 } 00055 00056 // --- cChannel -------------------------------------------------------------- 00057 00058 cChannel::cChannel(void) 00059 { 00060 name = strdup(""); 00061 shortName = strdup(""); 00062 provider = strdup(""); 00063 portalName = strdup(""); 00064 memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__); 00065 parameters = ""; 00066 modification = CHANNELMOD_NONE; 00067 schedule = NULL; 00068 linkChannels = NULL; 00069 refChannel = NULL; 00070 } 00071 00072 cChannel::cChannel(const cChannel &Channel) 00073 { 00074 name = NULL; 00075 shortName = NULL; 00076 provider = NULL; 00077 portalName = NULL; 00078 schedule = NULL; 00079 linkChannels = NULL; 00080 refChannel = NULL; 00081 *this = Channel; 00082 } 00083 00084 cChannel::~cChannel() 00085 { 00086 delete linkChannels; 00087 linkChannels = NULL; // more than one channel can link to this one, so we need the following loop 00088 for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) { 00089 if (Channel->linkChannels) { 00090 for (cLinkChannel *lc = Channel->linkChannels->First(); lc; lc = Channel->linkChannels->Next(lc)) { 00091 if (lc->Channel() == this) { 00092 Channel->linkChannels->Del(lc); 00093 break; 00094 } 00095 } 00096 if (Channel->linkChannels->Count() == 0) { 00097 delete Channel->linkChannels; 00098 Channel->linkChannels = NULL; 00099 } 00100 } 00101 } 00102 free(name); 00103 free(shortName); 00104 free(provider); 00105 free(portalName); 00106 } 00107 00108 cChannel& cChannel::operator= (const cChannel &Channel) 00109 { 00110 name = strcpyrealloc(name, Channel.name); 00111 shortName = strcpyrealloc(shortName, Channel.shortName); 00112 provider = strcpyrealloc(provider, Channel.provider); 00113 portalName = strcpyrealloc(portalName, Channel.portalName); 00114 memcpy(&__BeginData__, &Channel.__BeginData__, (char *)&Channel.__EndData__ - (char *)&Channel.__BeginData__); 00115 parameters = Channel.parameters; 00116 return *this; 00117 } 00118 00119 int cChannel::Transponder(int Frequency, char Polarization) 00120 { 00121 // some satellites have transponders at the same frequency, just with different polarization: 00122 switch (toupper(Polarization)) { 00123 case 'H': Frequency += 100000; break; 00124 case 'V': Frequency += 200000; break; 00125 case 'L': Frequency += 300000; break; 00126 case 'R': Frequency += 400000; break; 00127 default: esyslog("ERROR: invalid value for Polarization '%c'", Polarization); 00128 } 00129 return Frequency; 00130 } 00131 00132 int cChannel::Transponder(void) const 00133 { 00134 int tf = frequency; 00135 while (tf > 20000) 00136 tf /= 1000; 00137 if (IsSat()) { 00138 const char *p = strpbrk(parameters, "HVLRhvlr"); // lowercase for backwards compatibility 00139 if (p) 00140 tf = Transponder(tf, *p); 00141 } 00142 return tf; 00143 } 00144 00145 bool cChannel::HasTimer(void) const 00146 { 00147 for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) { 00148 if (Timer->Channel() == this) 00149 return true; 00150 } 00151 return false; 00152 } 00153 00154 int cChannel::Modification(int Mask) 00155 { 00156 int Result = modification & Mask; 00157 modification = CHANNELMOD_NONE; 00158 return Result; 00159 } 00160 00161 void cChannel::CopyTransponderData(const cChannel *Channel) 00162 { 00163 if (Channel) { 00164 frequency = Channel->frequency; 00165 source = Channel->source; 00166 srate = Channel->srate; 00167 parameters = Channel->parameters; 00168 } 00169 } 00170 00171 bool cChannel::SetTransponderData(int Source, int Frequency, int Srate, const char *Parameters, bool Quiet) 00172 { 00173 if (strchr(Parameters, ':')) { 00174 esyslog("ERROR: parameter string '%s' contains ':'", Parameters); 00175 return false; 00176 } 00177 // Workarounds for broadcaster stupidity: 00178 // Some providers broadcast the transponder frequency of their channels with two different 00179 // values (like 12551 and 12552), so we need to allow for a little tolerance here 00180 if (abs(frequency - Frequency) <= 1) 00181 Frequency = frequency; 00182 // Sometimes the transponder frequency is set to 0, which is just wrong 00183 if (Frequency == 0) 00184 return false; 00185 // Sometimes the symbol rate is off by one 00186 if (abs(srate - Srate) <= 1) 00187 Srate = srate; 00188 00189 if (source != Source || frequency != Frequency || srate != Srate || strcmp(parameters, Parameters)) { 00190 cString OldTransponderData = TransponderDataToString(); 00191 source = Source; 00192 frequency = Frequency; 00193 srate = Srate; 00194 parameters = Parameters; 00195 schedule = NULL; 00196 if (Number() && !Quiet) { 00197 dsyslog("changing transponder data of channel %d from %s to %s", Number(), *OldTransponderData, *TransponderDataToString()); 00198 modification |= CHANNELMOD_TRANSP; 00199 Channels.SetModified(); 00200 } 00201 } 00202 return true; 00203 } 00204 00205 void cChannel::SetId(int Nid, int Tid, int Sid, int Rid) 00206 { 00207 if (nid != Nid || tid != Tid || sid != Sid || rid != Rid) { 00208 if (Number()) { 00209 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); 00210 modification |= CHANNELMOD_ID; 00211 Channels.SetModified(); 00212 Channels.UnhashChannel(this); 00213 } 00214 nid = Nid; 00215 tid = Tid; 00216 sid = Sid; 00217 rid = Rid; 00218 if (Number()) 00219 Channels.HashChannel(this); 00220 schedule = NULL; 00221 } 00222 } 00223 00224 void cChannel::SetName(const char *Name, const char *ShortName, const char *Provider) 00225 { 00226 if (!isempty(Name)) { 00227 bool nn = strcmp(name, Name) != 0; 00228 bool ns = strcmp(shortName, ShortName) != 0; 00229 bool np = strcmp(provider, Provider) != 0; 00230 if (nn || ns || np) { 00231 if (Number()) { 00232 dsyslog("changing name of channel %d from '%s,%s;%s' to '%s,%s;%s'", Number(), name, shortName, provider, Name, ShortName, Provider); 00233 modification |= CHANNELMOD_NAME; 00234 Channels.SetModified(); 00235 } 00236 if (nn) 00237 name = strcpyrealloc(name, Name); 00238 if (ns) 00239 shortName = strcpyrealloc(shortName, ShortName); 00240 if (np) 00241 provider = strcpyrealloc(provider, Provider); 00242 } 00243 } 00244 } 00245 00246 void cChannel::SetPortalName(const char *PortalName) 00247 { 00248 if (!isempty(PortalName) && strcmp(portalName, PortalName) != 0) { 00249 if (Number()) { 00250 dsyslog("changing portal name of channel %d from '%s' to '%s'", Number(), portalName, PortalName); 00251 modification |= CHANNELMOD_NAME; 00252 Channels.SetModified(); 00253 } 00254 portalName = strcpyrealloc(portalName, PortalName); 00255 } 00256 } 00257 00258 #define STRDIFF 0x01 00259 #define VALDIFF 0x02 00260 00261 static int IntArraysDiffer(const int *a, const int *b, const char na[][MAXLANGCODE2] = NULL, const char nb[][MAXLANGCODE2] = NULL) 00262 { 00263 int result = 0; 00264 for (int i = 0; a[i] || b[i]; i++) { 00265 if (!a[i] || !b[i]) { 00266 result |= VALDIFF; 00267 break; 00268 } 00269 if (na && nb && strcmp(na[i], nb[i]) != 0) 00270 result |= STRDIFF; 00271 if (a[i] != b[i]) 00272 result |= VALDIFF; 00273 } 00274 return result; 00275 } 00276 00277 static int IntArrayToString(char *s, const int *a, int Base = 10, const char n[][MAXLANGCODE2] = NULL, const int *t = NULL) 00278 { 00279 char *q = s; 00280 int i = 0; 00281 while (a[i] || i == 0) { 00282 q += sprintf(q, Base == 16 ? "%s%X" : "%s%d", i ? "," : "", a[i]); 00283 const char *Delim = "="; 00284 if (a[i]) { 00285 if (n && *n[i]) { 00286 q += sprintf(q, "%s%s", Delim, n[i]); 00287 Delim = ""; 00288 } 00289 if (t && t[i]) 00290 q += sprintf(q, "%s@%d", Delim, t[i]); 00291 } 00292 if (!a[i]) 00293 break; 00294 i++; 00295 } 00296 *q = 0; 00297 return q - s; 00298 } 00299 00300 void cChannel::SetPids(int Vpid, int Ppid, int Vtype, int *Apids, int *Atypes, char ALangs[][MAXLANGCODE2], int *Dpids, int *Dtypes, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid) 00301 { 00302 int mod = CHANNELMOD_NONE; 00303 if (vpid != Vpid || ppid != Ppid || vtype != Vtype || tpid != Tpid) 00304 mod |= CHANNELMOD_PIDS; 00305 int m = IntArraysDiffer(apids, Apids, alangs, ALangs) | IntArraysDiffer(atypes, Atypes) | IntArraysDiffer(dpids, Dpids, dlangs, DLangs) | IntArraysDiffer(dtypes, Dtypes) | IntArraysDiffer(spids, Spids, slangs, SLangs); 00306 if (m & STRDIFF) 00307 mod |= CHANNELMOD_LANGS; 00308 if (m & VALDIFF) 00309 mod |= CHANNELMOD_PIDS; 00310 if (mod) { 00311 const int BufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2 + 5) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod@type', +10: paranoia 00312 char OldApidsBuf[BufferSize]; 00313 char NewApidsBuf[BufferSize]; 00314 char *q = OldApidsBuf; 00315 q += IntArrayToString(q, apids, 10, alangs, atypes); 00316 if (dpids[0]) { 00317 *q++ = ';'; 00318 q += IntArrayToString(q, dpids, 10, dlangs, dtypes); 00319 } 00320 *q = 0; 00321 q = NewApidsBuf; 00322 q += IntArrayToString(q, Apids, 10, ALangs, Atypes); 00323 if (Dpids[0]) { 00324 *q++ = ';'; 00325 q += IntArrayToString(q, Dpids, 10, DLangs, Dtypes); 00326 } 00327 *q = 0; 00328 const int SBufferSize = MAXSPIDS * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod', +10: paranoia 00329 char OldSpidsBuf[SBufferSize]; 00330 char NewSpidsBuf[SBufferSize]; 00331 q = OldSpidsBuf; 00332 q += IntArrayToString(q, spids, 10, slangs); 00333 *q = 0; 00334 q = NewSpidsBuf; 00335 q += IntArrayToString(q, Spids, 10, SLangs); 00336 *q = 0; 00337 if (Number()) 00338 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); 00339 vpid = Vpid; 00340 ppid = Ppid; 00341 vtype = Vtype; 00342 for (int i = 0; i < MAXAPIDS; i++) { 00343 apids[i] = Apids[i]; 00344 atypes[i] = Atypes[i]; 00345 strn0cpy(alangs[i], ALangs[i], MAXLANGCODE2); 00346 } 00347 apids[MAXAPIDS] = 0; 00348 for (int i = 0; i < MAXDPIDS; i++) { 00349 dpids[i] = Dpids[i]; 00350 dtypes[i] = Dtypes[i]; 00351 strn0cpy(dlangs[i], DLangs[i], MAXLANGCODE2); 00352 } 00353 dpids[MAXDPIDS] = 0; 00354 for (int i = 0; i < MAXSPIDS; i++) { 00355 spids[i] = Spids[i]; 00356 strn0cpy(slangs[i], SLangs[i], MAXLANGCODE2); 00357 } 00358 spids[MAXSPIDS] = 0; 00359 tpid = Tpid; 00360 modification |= mod; 00361 Channels.SetModified(); 00362 } 00363 } 00364 00365 void cChannel::SetSubtitlingDescriptors(uchar *SubtitlingTypes, uint16_t *CompositionPageIds, uint16_t *AncillaryPageIds) 00366 { 00367 if (SubtitlingTypes) { 00368 for (int i = 0; i < MAXSPIDS; i++) 00369 subtitlingTypes[i] = SubtitlingTypes[i]; 00370 } 00371 if (CompositionPageIds) { 00372 for (int i = 0; i < MAXSPIDS; i++) 00373 compositionPageIds[i] = CompositionPageIds[i]; 00374 } 00375 if (AncillaryPageIds) { 00376 for (int i = 0; i < MAXSPIDS; i++) 00377 ancillaryPageIds[i] = AncillaryPageIds[i]; 00378 } 00379 } 00380 00381 void cChannel::SetTeletextSubtitlePages(tTeletextSubtitlePage pages[], int numberOfPages) 00382 { 00383 int mod = CHANNELMOD_NONE; 00384 if (totalTtxtSubtitlePages != (fixedTtxtSubtitlePages + numberOfPages)) 00385 mod |= CHANNELMOD_PIDS; 00386 totalTtxtSubtitlePages = fixedTtxtSubtitlePages; 00387 for (int i = 0; (i < numberOfPages) && (totalTtxtSubtitlePages < MAXTXTPAGES); i++) { 00388 if (teletextSubtitlePages[totalTtxtSubtitlePages].ttxtMagazine != pages[i].ttxtMagazine || 00389 teletextSubtitlePages[totalTtxtSubtitlePages].ttxtPage != pages[i].ttxtPage || 00390 teletextSubtitlePages[totalTtxtSubtitlePages].ttxtType != pages[i].ttxtType || 00391 strcmp(teletextSubtitlePages[totalTtxtSubtitlePages].ttxtLanguage, pages[i].ttxtLanguage)) { 00392 mod |= CHANNELMOD_PIDS; 00393 teletextSubtitlePages[totalTtxtSubtitlePages] = pages[i]; 00394 } 00395 totalTtxtSubtitlePages++; 00396 } 00397 modification |= mod; 00398 Channels.SetModified(); 00399 } 00400 00401 void cChannel::SetCaIds(const int *CaIds) 00402 { 00403 if (caids[0] && caids[0] <= CA_USER_MAX) 00404 return; // special values will not be overwritten 00405 if (IntArraysDiffer(caids, CaIds)) { 00406 char OldCaIdsBuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia 00407 char NewCaIdsBuf[MAXCAIDS * 5 + 10]; 00408 IntArrayToString(OldCaIdsBuf, caids, 16); 00409 IntArrayToString(NewCaIdsBuf, CaIds, 16); 00410 if (Number()) 00411 dsyslog("changing caids of channel %d from %s to %s", Number(), OldCaIdsBuf, NewCaIdsBuf); 00412 for (int i = 0; i <= MAXCAIDS; i++) { // <= to copy the terminating 0 00413 caids[i] = CaIds[i]; 00414 if (!CaIds[i]) 00415 break; 00416 } 00417 modification |= CHANNELMOD_CA; 00418 Channels.SetModified(); 00419 } 00420 } 00421 00422 void cChannel::SetCaDescriptors(int Level) 00423 { 00424 if (Level > 0) { 00425 modification |= CHANNELMOD_CA; 00426 Channels.SetModified(); 00427 if (Number() && Level > 1) 00428 dsyslog("changing ca descriptors of channel %d", Number()); 00429 } 00430 } 00431 00432 void cChannel::SetLinkChannels(cLinkChannels *LinkChannels) 00433 { 00434 if (!linkChannels && !LinkChannels) 00435 return; 00436 if (linkChannels && LinkChannels) { 00437 cLinkChannel *lca = linkChannels->First(); 00438 cLinkChannel *lcb = LinkChannels->First(); 00439 while (lca && lcb) { 00440 if (lca->Channel() != lcb->Channel()) { 00441 lca = NULL; 00442 break; 00443 } 00444 lca = linkChannels->Next(lca); 00445 lcb = LinkChannels->Next(lcb); 00446 } 00447 if (!lca && !lcb) { 00448 delete LinkChannels; 00449 return; // linkage has not changed 00450 } 00451 } 00452 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 00453 char *q = buffer; 00454 q += sprintf(q, "linking channel %d from", Number()); 00455 if (linkChannels) { 00456 for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) { 00457 lc->Channel()->SetRefChannel(NULL); 00458 q += sprintf(q, " %d", lc->Channel()->Number()); 00459 } 00460 delete linkChannels; 00461 } 00462 else 00463 q += sprintf(q, " none"); 00464 q += sprintf(q, " to"); 00465 linkChannels = LinkChannels; 00466 if (linkChannels) { 00467 for (cLinkChannel *lc = linkChannels->First(); lc; lc = linkChannels->Next(lc)) { 00468 lc->Channel()->SetRefChannel(this); 00469 q += sprintf(q, " %d", lc->Channel()->Number()); 00470 //dsyslog("link %4d -> %4d: %s", Number(), lc->Channel()->Number(), lc->Channel()->Name()); 00471 } 00472 } 00473 else 00474 q += sprintf(q, " none"); 00475 if (Number()) 00476 dsyslog("%s", buffer); 00477 } 00478 00479 void cChannel::SetRefChannel(cChannel *RefChannel) 00480 { 00481 refChannel = RefChannel; 00482 } 00483 00484 cString cChannel::TransponderDataToString(void) const 00485 { 00486 if (cSource::IsTerr(source)) 00487 return cString::sprintf("%d:%s:%s", frequency, *parameters, *cSource::ToString(source)); 00488 return cString::sprintf("%d:%s:%s:%d", frequency, *parameters, *cSource::ToString(source), srate); 00489 } 00490 00491 cString cChannel::ToText(const cChannel *Channel) 00492 { 00493 char FullName[strlen(Channel->name) + 1 + strlen(Channel->shortName) + 1 + strlen(Channel->provider) + 1 + 10]; // +10: paranoia 00494 char *q = FullName; 00495 q += sprintf(q, "%s", Channel->name); 00496 if (!isempty(Channel->shortName)) 00497 q += sprintf(q, ",%s", Channel->shortName); 00498 else if (strchr(Channel->name, ',')) 00499 q += sprintf(q, ","); 00500 if (!isempty(Channel->provider)) 00501 q += sprintf(q, ";%s", Channel->provider); 00502 *q = 0; 00503 strreplace(FullName, ':', '|'); 00504 cString buffer; 00505 if (Channel->groupSep) { 00506 if (Channel->number) 00507 buffer = cString::sprintf(":@%d %s\n", Channel->number, FullName); 00508 else 00509 buffer = cString::sprintf(":%s\n", FullName); 00510 } 00511 else { 00512 char vpidbuf[32]; 00513 char *q = vpidbuf; 00514 q += snprintf(q, sizeof(vpidbuf), "%d", Channel->vpid); 00515 if (Channel->ppid && Channel->ppid != Channel->vpid) 00516 q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "+%d", Channel->ppid); 00517 if (Channel->vpid && Channel->vtype) 00518 q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "=%d", Channel->vtype); 00519 *q = 0; 00520 const int ABufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2 + 5) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod@type', +10: paranoia 00521 char apidbuf[ABufferSize]; 00522 q = apidbuf; 00523 q += IntArrayToString(q, Channel->apids, 10, Channel->alangs, Channel->atypes); 00524 if (Channel->dpids[0]) { 00525 *q++ = ';'; 00526 q += IntArrayToString(q, Channel->dpids, 10, Channel->dlangs, Channel->dtypes); 00527 } 00528 *q = 0; 00529 const int TBufferSize = (MAXTXTPAGES * MAXSPIDS) * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod', +10: paranoia and tpid 00530 char tpidbuf[TBufferSize]; 00531 q = tpidbuf; 00532 q += snprintf(q, sizeof(tpidbuf), "%d", Channel->tpid); 00533 if (Channel->fixedTtxtSubtitlePages > 0) { 00534 *q++ = '+'; 00535 for (int i = 0; i < Channel->fixedTtxtSubtitlePages; ++i) { 00536 tTeletextSubtitlePage page = Channel->teletextSubtitlePages[i]; 00537 q += snprintf(q, sizeof(tpidbuf) - (q - tpidbuf), "%d=%s", page.PageNumber(), page.ttxtLanguage); 00538 } 00539 } 00540 if (Channel->spids[0]) { 00541 *q++ = ';'; 00542 q += IntArrayToString(q, Channel->spids, 10, Channel->slangs); 00543 } 00544 char caidbuf[MAXCAIDS * 5 + 10]; // 5: 4 digits plus delimiting ',', 10: paranoia 00545 q = caidbuf; 00546 q += IntArrayToString(q, Channel->caids, 16); 00547 *q = 0; 00548 buffer = cString::sprintf("%s:%d:%s:%s:%d:%s:%s:%s:%s:%d:%d:%d:%d\n", FullName, Channel->frequency, *Channel->parameters, *cSource::ToString(Channel->source), Channel->srate, vpidbuf, apidbuf, tpidbuf, caidbuf, Channel->sid, Channel->nid, Channel->tid, Channel->rid); 00549 } 00550 return buffer; 00551 } 00552 00553 cString cChannel::ToText(void) const 00554 { 00555 return ToText(this); 00556 } 00557 00558 bool cChannel::Parse(const char *s) 00559 { 00560 bool ok = true; 00561 if (*s == ':') { 00562 groupSep = true; 00563 if (*++s == '@' && *++s) { 00564 char *p = NULL; 00565 errno = 0; 00566 int n = strtol(s, &p, 10); 00567 if (!errno && p != s && n > 0) { 00568 number = n; 00569 s = p; 00570 } 00571 } 00572 name = strcpyrealloc(name, skipspace(s)); 00573 strreplace(name, '|', ':'); 00574 } 00575 else { 00576 groupSep = false; 00577 char *namebuf = NULL; 00578 char *sourcebuf = NULL; 00579 char *parambuf = NULL; 00580 char *vpidbuf = NULL; 00581 char *apidbuf = NULL; 00582 char *tpidbuf = NULL; 00583 char *caidbuf = NULL; 00584 int fields = sscanf(s, "%a[^:]:%d :%a[^:]:%a[^:] :%d :%a[^:]:%a[^:]:%a[^:]:%a[^:]:%d :%d :%d :%d ", &namebuf, &frequency, ¶mbuf, &sourcebuf, &srate, &vpidbuf, &apidbuf, &tpidbuf, &caidbuf, &sid, &nid, &tid, &rid); 00585 if (fields >= 9) { 00586 if (fields == 9) { 00587 // allow reading of old format 00588 sid = atoi(caidbuf); 00589 delete caidbuf; 00590 caidbuf = NULL; 00591 if (sscanf(tpidbuf, "%d", &tpid) != 1) 00592 return false; 00593 caids[0] = tpid; 00594 caids[1] = 0; 00595 tpid = 0; 00596 } 00597 vpid = ppid = 0; 00598 vtype = 0; 00599 apids[0] = 0; 00600 atypes[0] = 0; 00601 dpids[0] = 0; 00602 dtypes[0] = 0; 00603 spids[0] = 0; 00604 ok = false; 00605 if (parambuf && sourcebuf && vpidbuf && apidbuf) { 00606 parameters = parambuf; 00607 ok = (source = cSource::FromString(sourcebuf)) >= 0; 00608 00609 char *p; 00610 if ((p = strchr(vpidbuf, '=')) != NULL) { 00611 *p++ = 0; 00612 if (sscanf(p, "%d", &vtype) != 1) 00613 return false; 00614 } 00615 if ((p = strchr(vpidbuf, '+')) != NULL) { 00616 *p++ = 0; 00617 if (sscanf(p, "%d", &ppid) != 1) 00618 return false; 00619 } 00620 if (sscanf(vpidbuf, "%d", &vpid) != 1) 00621 return false; 00622 if (!ppid) 00623 ppid = vpid; 00624 if (vpid && !vtype) 00625 vtype = 2; // default is MPEG-2 00626 00627 char *dpidbuf = strchr(apidbuf, ';'); 00628 if (dpidbuf) 00629 *dpidbuf++ = 0; 00630 p = apidbuf; 00631 char *q; 00632 int NumApids = 0; 00633 char *strtok_next; 00634 while ((q = strtok_r(p, ",", &strtok_next)) != NULL) { 00635 if (NumApids < MAXAPIDS) { 00636 atypes[NumApids] = 4; // backwards compatibility 00637 char *l = strchr(q, '='); 00638 if (l) { 00639 *l++ = 0; 00640 char *t = strchr(l, '@'); 00641 if (t) { 00642 *t++ = 0; 00643 atypes[NumApids] = strtol(t, NULL, 10); 00644 } 00645 strn0cpy(alangs[NumApids], l, MAXLANGCODE2); 00646 } 00647 else 00648 *alangs[NumApids] = 0; 00649 if ((apids[NumApids] = strtol(q, NULL, 10)) != 0) 00650 NumApids++; 00651 } 00652 else 00653 esyslog("ERROR: too many APIDs!"); // no need to set ok to 'false' 00654 p = NULL; 00655 } 00656 apids[NumApids] = 0; 00657 atypes[NumApids] = 0; 00658 if (dpidbuf) { 00659 char *p = dpidbuf; 00660 char *q; 00661 int NumDpids = 0; 00662 char *strtok_next; 00663 while ((q = strtok_r(p, ",", &strtok_next)) != NULL) { 00664 if (NumDpids < MAXDPIDS) { 00665 dtypes[NumDpids] = SI::AC3DescriptorTag; // backwards compatibility 00666 char *l = strchr(q, '='); 00667 if (l) { 00668 *l++ = 0; 00669 char *t = strchr(l, '@'); 00670 if (t) { 00671 *t++ = 0; 00672 dtypes[NumDpids] = strtol(t, NULL, 10); 00673 } 00674 strn0cpy(dlangs[NumDpids], l, MAXLANGCODE2); 00675 } 00676 else 00677 *dlangs[NumDpids] = 0; 00678 if ((dpids[NumDpids] = strtol(q, NULL, 10)) != 0) 00679 NumDpids++; 00680 } 00681 else 00682 esyslog("ERROR: too many DPIDs!"); // no need to set ok to 'false' 00683 p = NULL; 00684 } 00685 dpids[NumDpids] = 0; 00686 dtypes[NumDpids] = 0; 00687 } 00688 int NumSpids = 0; 00689 if ((p = strchr(tpidbuf, ';')) != NULL) { 00690 *p++ = 0; 00691 char *q; 00692 char *strtok_next; 00693 while ((q = strtok_r(p, ",", &strtok_next)) != NULL) { 00694 if (NumSpids < MAXSPIDS) { 00695 char *l = strchr(q, '='); 00696 if (l) { 00697 *l++ = 0; 00698 strn0cpy(slangs[NumSpids], l, MAXLANGCODE2); 00699 } 00700 else 00701 *slangs[NumSpids] = 0; 00702 spids[NumSpids++] = strtol(q, NULL, 10); 00703 } 00704 else 00705 esyslog("ERROR: too many SPIDs!"); // no need to set ok to 'false' 00706 p = NULL; 00707 } 00708 spids[NumSpids] = 0; 00709 } 00710 fixedTtxtSubtitlePages = 0; 00711 if ((p = strchr(tpidbuf, '+')) != NULL) { 00712 *p++ = 0; 00713 char *q; 00714 char *strtok_next; 00715 while ((q = strtok_r(p, ",", &strtok_next)) != NULL) { 00716 if (fixedTtxtSubtitlePages < MAXTXTPAGES) { 00717 int page; 00718 char *l = strchr(q, '='); 00719 if (l) 00720 *l++ = 0; 00721 if (sscanf(q, "%d", &page) == 1) { 00722 teletextSubtitlePages[fixedTtxtSubtitlePages] = tTeletextSubtitlePage(page); 00723 if (l) 00724 strn0cpy(teletextSubtitlePages[fixedTtxtSubtitlePages].ttxtLanguage, l, MAXLANGCODE2); 00725 fixedTtxtSubtitlePages++; 00726 } 00727 else 00728 esyslog("ERROR: invalid Teletext page!"); // no need to set ok to 'false' 00729 } 00730 else 00731 esyslog("ERROR: too many Teletext pages!"); // no need to set ok to 'false' 00732 p = NULL; 00733 } 00734 totalTtxtSubtitlePages = fixedTtxtSubtitlePages; 00735 } 00736 if (sscanf(tpidbuf, "%d", &tpid) != 1) 00737 return false; 00738 if (caidbuf) { 00739 char *p = caidbuf; 00740 char *q; 00741 int NumCaIds = 0; 00742 char *strtok_next; 00743 while ((q = strtok_r(p, ",", &strtok_next)) != NULL) { 00744 if (NumCaIds < MAXCAIDS) { 00745 caids[NumCaIds++] = strtol(q, NULL, 16) & 0xFFFF; 00746 if (NumCaIds == 1 && caids[0] <= CA_USER_MAX) 00747 break; 00748 } 00749 else 00750 esyslog("ERROR: too many CA ids!"); // no need to set ok to 'false' 00751 p = NULL; 00752 } 00753 caids[NumCaIds] = 0; 00754 } 00755 } 00756 strreplace(namebuf, '|', ':'); 00757 00758 char *p = strchr(namebuf, ';'); 00759 if (p) { 00760 *p++ = 0; 00761 provider = strcpyrealloc(provider, p); 00762 } 00763 p = strrchr(namebuf, ','); // long name might contain a ',', so search for the rightmost one 00764 if (p) { 00765 *p++ = 0; 00766 shortName = strcpyrealloc(shortName, p); 00767 } 00768 name = strcpyrealloc(name, namebuf); 00769 00770 free(parambuf); 00771 free(sourcebuf); 00772 free(vpidbuf); 00773 free(apidbuf); 00774 free(tpidbuf); 00775 free(caidbuf); 00776 free(namebuf); 00777 if (!GetChannelID().Valid()) { 00778 esyslog("ERROR: channel data results in invalid ID!"); 00779 return false; 00780 } 00781 } 00782 else 00783 return false; 00784 } 00785 return ok; 00786 } 00787 00788 bool cChannel::Save(FILE *f) 00789 { 00790 return fprintf(f, "%s", *ToText()) > 0; 00791 } 00792 00793 // --- cChannelSorter -------------------------------------------------------- 00794 00795 class cChannelSorter : public cListObject { 00796 public: 00797 cChannel *channel; 00798 tChannelID channelID; 00799 cChannelSorter(cChannel *Channel) { 00800 channel = Channel; 00801 channelID = channel->GetChannelID(); 00802 } 00803 virtual int Compare(const cListObject &ListObject) const { 00804 cChannelSorter *cs = (cChannelSorter *)&ListObject; 00805 return memcmp(&channelID, &cs->channelID, sizeof(channelID)); 00806 } 00807 }; 00808 00809 // --- cChannels ------------------------------------------------------------- 00810 00811 cChannels Channels; 00812 00813 cChannels::cChannels(void) 00814 { 00815 maxNumber = 0; 00816 maxChannelNameLength = 0; 00817 maxShortChannelNameLength = 0; 00818 modified = CHANNELSMOD_NONE; 00819 } 00820 00821 void cChannels::DeleteDuplicateChannels(void) 00822 { 00823 cList<cChannelSorter> ChannelSorter; 00824 for (cChannel *channel = First(); channel; channel = Next(channel)) { 00825 if (!channel->GroupSep()) 00826 ChannelSorter.Add(new cChannelSorter(channel)); 00827 } 00828 ChannelSorter.Sort(); 00829 cChannelSorter *cs = ChannelSorter.First(); 00830 while (cs) { 00831 cChannelSorter *next = ChannelSorter.Next(cs); 00832 if (next && cs->channelID == next->channelID) { 00833 dsyslog("deleting duplicate channel %s", *next->channel->ToText()); 00834 Del(next->channel); 00835 } 00836 cs = next; 00837 } 00838 } 00839 00840 bool cChannels::Load(const char *FileName, bool AllowComments, bool MustExist) 00841 { 00842 if (cConfig<cChannel>::Load(FileName, AllowComments, MustExist)) { 00843 DeleteDuplicateChannels(); 00844 ReNumber(); 00845 return true; 00846 } 00847 return false; 00848 } 00849 00850 void cChannels::HashChannel(cChannel *Channel) 00851 { 00852 channelsHashSid.Add(Channel, Channel->Sid()); 00853 } 00854 00855 void cChannels::UnhashChannel(cChannel *Channel) 00856 { 00857 channelsHashSid.Del(Channel, Channel->Sid()); 00858 } 00859 00860 int cChannels::GetNextGroup(int Idx) 00861 { 00862 cChannel *channel = Get(++Idx); 00863 while (channel && !(channel->GroupSep() && *channel->Name())) 00864 channel = Get(++Idx); 00865 return channel ? Idx : -1; 00866 } 00867 00868 int cChannels::GetPrevGroup(int Idx) 00869 { 00870 cChannel *channel = Get(--Idx); 00871 while (channel && !(channel->GroupSep() && *channel->Name())) 00872 channel = Get(--Idx); 00873 return channel ? Idx : -1; 00874 } 00875 00876 int cChannels::GetNextNormal(int Idx) 00877 { 00878 cChannel *channel = Get(++Idx); 00879 while (channel && channel->GroupSep()) 00880 channel = Get(++Idx); 00881 return channel ? Idx : -1; 00882 } 00883 00884 int cChannels::GetPrevNormal(int Idx) 00885 { 00886 cChannel *channel = Get(--Idx); 00887 while (channel && channel->GroupSep()) 00888 channel = Get(--Idx); 00889 return channel ? Idx : -1; 00890 } 00891 00892 void cChannels::ReNumber(void) 00893 { 00894 channelsHashSid.Clear(); 00895 maxNumber = 0; 00896 int Number = 1; 00897 for (cChannel *channel = First(); channel; channel = Next(channel)) { 00898 if (channel->GroupSep()) { 00899 if (channel->Number() > Number) 00900 Number = channel->Number(); 00901 } 00902 else { 00903 HashChannel(channel); 00904 maxNumber = Number; 00905 channel->SetNumber(Number++); 00906 } 00907 } 00908 } 00909 00910 cChannel *cChannels::GetByNumber(int Number, int SkipGap) 00911 { 00912 cChannel *previous = NULL; 00913 for (cChannel *channel = First(); channel; channel = Next(channel)) { 00914 if (!channel->GroupSep()) { 00915 if (channel->Number() == Number) 00916 return channel; 00917 else if (SkipGap && channel->Number() > Number) 00918 return SkipGap > 0 ? channel : previous; 00919 previous = channel; 00920 } 00921 } 00922 return NULL; 00923 } 00924 00925 cChannel *cChannels::GetByServiceID(int Source, int Transponder, unsigned short ServiceID) 00926 { 00927 cList<cHashObject> *list = channelsHashSid.GetList(ServiceID); 00928 if (list) { 00929 for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) { 00930 cChannel *channel = (cChannel *)hobj->Object(); 00931 if (channel->Sid() == ServiceID && channel->Source() == Source && ISTRANSPONDER(channel->Transponder(), Transponder)) 00932 return channel; 00933 } 00934 } 00935 return NULL; 00936 } 00937 00938 cChannel *cChannels::GetByChannelID(tChannelID ChannelID, bool TryWithoutRid, bool TryWithoutPolarization) 00939 { 00940 int sid = ChannelID.Sid(); 00941 cList<cHashObject> *list = channelsHashSid.GetList(sid); 00942 if (list) { 00943 for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) { 00944 cChannel *channel = (cChannel *)hobj->Object(); 00945 if (channel->Sid() == sid && channel->GetChannelID() == ChannelID) 00946 return channel; 00947 } 00948 if (TryWithoutRid) { 00949 ChannelID.ClrRid(); 00950 for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) { 00951 cChannel *channel = (cChannel *)hobj->Object(); 00952 if (channel->Sid() == sid && channel->GetChannelID().ClrRid() == ChannelID) 00953 return channel; 00954 } 00955 } 00956 if (TryWithoutPolarization) { 00957 ChannelID.ClrPolarization(); 00958 for (cHashObject *hobj = list->First(); hobj; hobj = list->Next(hobj)) { 00959 cChannel *channel = (cChannel *)hobj->Object(); 00960 if (channel->Sid() == sid && channel->GetChannelID().ClrPolarization() == ChannelID) 00961 return channel; 00962 } 00963 } 00964 } 00965 return NULL; 00966 } 00967 cChannel *cChannels::GetByTransponderID(tChannelID ChannelID) 00968 { 00969 int source = ChannelID.Source(); 00970 int nid = ChannelID.Nid(); 00971 int tid = ChannelID.Tid(); 00972 for (cChannel *channel = First(); channel; channel = Next(channel)) { 00973 if (channel->Tid() == tid && channel->Nid() == nid && channel->Source() == source) 00974 return channel; 00975 } 00976 return NULL; 00977 } 00978 00979 bool cChannels::HasUniqueChannelID(cChannel *NewChannel, cChannel *OldChannel) 00980 { 00981 tChannelID NewChannelID = NewChannel->GetChannelID(); 00982 for (cChannel *channel = First(); channel; channel = Next(channel)) { 00983 if (!channel->GroupSep() && channel != OldChannel && channel->GetChannelID() == NewChannelID) 00984 return false; 00985 } 00986 return true; 00987 } 00988 00989 bool cChannels::SwitchTo(int Number) 00990 { 00991 cChannel *channel = GetByNumber(Number); 00992 return channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true); 00993 } 00994 00995 int cChannels::MaxChannelNameLength(void) 00996 { 00997 if (!maxChannelNameLength) { 00998 for (cChannel *channel = First(); channel; channel = Next(channel)) { 00999 if (!channel->GroupSep()) 01000 maxChannelNameLength = max(Utf8StrLen(channel->Name()), maxChannelNameLength); 01001 } 01002 } 01003 return maxChannelNameLength; 01004 } 01005 01006 int cChannels::MaxShortChannelNameLength(void) 01007 { 01008 if (!maxShortChannelNameLength) { 01009 for (cChannel *channel = First(); channel; channel = Next(channel)) { 01010 if (!channel->GroupSep()) 01011 maxShortChannelNameLength = max(Utf8StrLen(channel->ShortName()), maxShortChannelNameLength); 01012 } 01013 } 01014 return maxShortChannelNameLength; 01015 } 01016 01017 void cChannels::SetModified(bool ByUser) 01018 { 01019 modified = ByUser ? CHANNELSMOD_USER : !modified ? CHANNELSMOD_AUTO : modified; 01020 maxChannelNameLength = maxShortChannelNameLength = 0; 01021 } 01022 01023 int cChannels::Modified(void) 01024 { 01025 int Result = modified; 01026 modified = CHANNELSMOD_NONE; 01027 return Result; 01028 } 01029 01030 cChannel *cChannels::NewChannel(const cChannel *Transponder, const char *Name, const char *ShortName, const char *Provider, int Nid, int Tid, int Sid, int Rid) 01031 { 01032 if (Transponder) { 01033 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); 01034 cChannel *NewChannel = new cChannel; 01035 NewChannel->CopyTransponderData(Transponder); 01036 NewChannel->SetId(Nid, Tid, Sid, Rid); 01037 NewChannel->SetName(Name, ShortName, Provider); 01038 Add(NewChannel); 01039 ReNumber(); 01040 return NewChannel; 01041 } 01042 return NULL; 01043 } 01044 01045 cString ChannelString(const cChannel *Channel, int Number) 01046 { 01047 char buffer[256]; 01048 if (Channel) { 01049 if (Channel->GroupSep()) 01050 snprintf(buffer, sizeof(buffer), "%s", Channel->Name()); 01051 else 01052 snprintf(buffer, sizeof(buffer), "%d%s %s", Channel->Number(), Number ? "-" : "", Channel->Name()); 01053 } 01054 else if (Number) 01055 snprintf(buffer, sizeof(buffer), "%d-", Number); 01056 else 01057 snprintf(buffer, sizeof(buffer), "%s", tr("*** Invalid Channel ***")); 01058 return buffer; 01059 }