MythTV  master
channelutil.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 
3 #include <algorithm>
4 #include <cstdint>
5 #include <set>
6 using namespace std;
7 
8 #include <QFile>
9 #include <QHash>
10 #include <QImage>
11 #include <QReadWriteLock>
12 #include <QRegExp>
13 #include <utility>
14 
15 #include "channelutil.h"
16 #include "mythdb.h"
17 #include "dvbtables.h"
18 #include "mythmiscutil.h"
19 #include "HLSReader.h"
20 
21 #define LOC QString("ChanUtil: ")
22 
23 const QString ChannelUtil::kATSCSeparators = "(_|-|#|\\.)";
24 
25 static uint get_dtv_multiplex(uint db_source_id, const QString& sistandard,
26  uint64_t frequency,
27  // DVB specific
28  uint transport_id,
29  // tsid exists with other sistandards,
30  // but we only trust it in dvb-land.
31  uint network_id,
32  // must check polarity for dvb-s
33  signed char polarity)
34 {
35  QString qstr =
36  "SELECT mplexid "
37  "FROM dtv_multiplex "
38  "WHERE sourceid = :SOURCEID "
39  " AND sistandard = :SISTANDARD ";
40 
41  if (sistandard.toLower() != "dvb")
42  qstr += "AND frequency = :FREQUENCY ";
43  else
44  {
45  qstr += "AND transportid = :TRANSPORTID ";
46  qstr += "AND networkid = :NETWORKID ";
47  qstr += "AND polarity = :POLARITY ";
48  }
49 
50 
52  query.prepare(qstr);
53 
54  query.bindValue(":SOURCEID", db_source_id);
55  query.bindValue(":SISTANDARD", sistandard);
56 
57  if (sistandard.toLower() != "dvb")
58  query.bindValue(":FREQUENCY", QString::number(frequency));
59  else
60  {
61  query.bindValue(":TRANSPORTID", transport_id);
62  query.bindValue(":NETWORKID", network_id);
63  query.bindValue(":POLARITY", QString(polarity));
64  }
65 
66  if (!query.exec() || !query.isActive())
67  {
68  MythDB::DBError("get_dtv_multiplex", query);
69  return 0;
70  }
71 
72  if (query.next())
73  return query.value(0).toUInt();
74 
75  return 0;
76 }
77 
79  int db_source_id, const QString& sistandard,
80  uint64_t frequency, const QString& modulation,
81  // DVB specific
82  int transport_id, int network_id,
83  int symbol_rate, signed char bandwidth,
84  signed char polarity, signed char inversion,
85  signed char trans_mode,
86  const QString& inner_FEC, const QString& constellation,
87  signed char hierarchy, const QString& hp_code_rate,
88  const QString& lp_code_rate, const QString& guard_interval,
89  const QString& mod_sys, const QString& rolloff)
90 {
92 
93  // If transport is already present, skip insert
94  uint mplex = get_dtv_multiplex(
95  db_source_id, sistandard, frequency,
96  // DVB specific
97  transport_id, network_id, polarity);
98 
99  LOG(VB_CHANSCAN, LOG_INFO, QString(
100  "insert_dtv_multiplex(db_source_id: %1, sistandard: '%2', "
101  "frequency: %3, modulation: %4, transport_id: %5, "
102  "network_id: %6, polarity: %7...) mplexid:%8")
103  .arg(db_source_id).arg(sistandard)
104  .arg(frequency).arg(modulation)
105  .arg(transport_id).arg(network_id)
106  .arg(polarity).arg(mplex));
107 
108  bool isDVB = (sistandard.toLower() == "dvb");
109 
110  QString updateStr =
111  "UPDATE dtv_multiplex "
112  "SET frequency = :FREQUENCY1, ";
113 
114  updateStr += (!modulation.isNull()) ?
115  "modulation = :MODULATION, " : "";
116  updateStr += (symbol_rate >= 0) ?
117  "symbolrate = :SYMBOLRATE, " : "";
118  updateStr += (bandwidth >= 0) ?
119  "bandwidth = :BANDWIDTH, " : "";
120  updateStr += (polarity >= 0) ?
121  "polarity = :POLARITY, " : "";
122  updateStr += (inversion >= 0) ?
123  "inversion = :INVERSION, " : "";
124  updateStr += (trans_mode >= 0) ?
125  "transmission_mode= :TRANS_MODE, " : "";
126  updateStr += (!inner_FEC.isNull()) ?
127  "fec = :INNER_FEC, " : "";
128  updateStr += (!constellation.isNull()) ?
129  "constellation = :CONSTELLATION, " : "";
130  updateStr += (hierarchy >= 0) ?
131  "hierarchy = :HIERARCHY, " : "";
132  updateStr += (!hp_code_rate.isNull()) ?
133  "hp_code_rate = :HP_CODE_RATE, " : "";
134  updateStr += (!lp_code_rate.isNull()) ?
135  "lp_code_rate = :LP_CODE_RATE, " : "";
136  updateStr += (!guard_interval.isNull()) ?
137  "guard_interval = :GUARD_INTERVAL, " : "";
138  updateStr += (!mod_sys.isNull()) ?
139  "mod_sys = :MOD_SYS, " : "";
140  updateStr += (symbol_rate >= 0) ?
141  "rolloff = :ROLLOFF, " : "";
142  updateStr += (transport_id && !isDVB) ?
143  "transportid = :TRANSPORTID, " : "";
144 
145  updateStr = updateStr.left(updateStr.length()-2) + " ";
146 
147  updateStr +=
148  "WHERE sourceid = :SOURCEID AND "
149  " sistandard = :SISTANDARD AND ";
150 
151  updateStr += (isDVB) ?
152  " polarity = :WHEREPOLARITY AND "
153  " transportid = :TRANSPORTID AND networkid = :NETWORKID " :
154  " frequency = :FREQUENCY2 ";
155 
156  QString insertStr =
157  "INSERT INTO dtv_multiplex "
158  " (sourceid, sistandard, frequency, ";
159 
160  insertStr += (!modulation.isNull()) ? "modulation, " : "";
161  insertStr += (transport_id || isDVB) ? "transportid, " : "";
162  insertStr += (isDVB) ? "networkid, " : "";
163  insertStr += (symbol_rate >= 0) ? "symbolrate, " : "";
164  insertStr += (bandwidth >= 0) ? "bandwidth, " : "";
165  insertStr += (polarity >= 0) ? "polarity, " : "";
166  insertStr += (inversion >= 0) ? "inversion, " : "";
167  insertStr += (trans_mode >= 0) ? "transmission_mode, " : "";
168  insertStr += (!inner_FEC.isNull()) ? "fec, " : "";
169  insertStr += (!constellation.isNull()) ? "constellation, " : "";
170  insertStr += (hierarchy >= 0) ? "hierarchy, " : "";
171  insertStr += (!hp_code_rate.isNull()) ? "hp_code_rate, " : "";
172  insertStr += (!lp_code_rate.isNull()) ? "lp_code_rate, " : "";
173  insertStr += (!guard_interval.isNull()) ? "guard_interval, " : "";
174  insertStr += (!mod_sys.isNull()) ? "mod_sys, " : "";
175  insertStr += (!rolloff.isNull()) ? "rolloff, " : "";
176  insertStr = insertStr.left(insertStr.length()-2) + ") ";
177 
178  insertStr +=
179  "VALUES "
180  " (:SOURCEID, :SISTANDARD, :FREQUENCY1, ";
181  insertStr += (!modulation.isNull()) ? ":MODULATION, " : "";
182  insertStr += (transport_id || isDVB) ? ":TRANSPORTID, " : "";
183  insertStr += (isDVB) ? ":NETWORKID, " : "";
184  insertStr += (symbol_rate >= 0) ? ":SYMBOLRATE, " : "";
185  insertStr += (bandwidth >= 0) ? ":BANDWIDTH, " : "";
186  insertStr += (polarity >= 0) ? ":POLARITY, " : "";
187  insertStr += (inversion >= 0) ? ":INVERSION, " : "";
188  insertStr += (trans_mode >= 0) ? ":TRANS_MODE, " : "";
189  insertStr += (!inner_FEC.isNull()) ? ":INNER_FEC, " : "";
190  insertStr += (!constellation.isNull()) ? ":CONSTELLATION, " : "";
191  insertStr += (hierarchy >= 0) ? ":HIERARCHY, " : "";
192  insertStr += (!hp_code_rate.isNull()) ? ":HP_CODE_RATE, " : "";
193  insertStr += (!lp_code_rate.isNull()) ? ":LP_CODE_RATE, " : "";
194  insertStr += (!guard_interval.isNull()) ? ":GUARD_INTERVAL, " : "";
195  insertStr += (!mod_sys.isNull()) ? ":MOD_SYS, " : "";
196  insertStr += (!rolloff.isNull()) ? ":ROLLOFF, " : "";
197  insertStr = insertStr.left(insertStr.length()-2) + ");";
198 
199  query.prepare((mplex) ? updateStr : insertStr);
200 
201  query.bindValue(":SOURCEID", db_source_id);
202  query.bindValue(":SISTANDARD", sistandard);
203  query.bindValue(":FREQUENCY1", QString::number(frequency));
204 
205  if (mplex)
206  {
207  if (isDVB)
208  {
209  query.bindValue(":TRANSPORTID", transport_id);
210  query.bindValue(":NETWORKID", network_id);
211  query.bindValue(":WHEREPOLARITY", QString(polarity));
212  }
213  else
214  {
215  query.bindValue(":FREQUENCY2", QString::number(frequency));
216  if (transport_id)
217  query.bindValue(":TRANSPORTID", transport_id);
218  }
219  }
220  else
221  {
222  if (transport_id || isDVB)
223  query.bindValue(":TRANSPORTID", transport_id);
224  if (isDVB)
225  query.bindValue(":NETWORKID", network_id);
226  }
227 
228  if (!modulation.isNull())
229  query.bindValue(":MODULATION", modulation);
230 
231  if (symbol_rate >= 0)
232  query.bindValue(":SYMBOLRATE", symbol_rate);
233  if (bandwidth >= 0)
234  query.bindValue(":BANDWIDTH", QString("%1").arg((char)bandwidth));
235  if (polarity >= 0)
236  query.bindValue(":POLARITY", QString("%1").arg((char)polarity));
237  if (inversion >= 0)
238  query.bindValue(":INVERSION", QString("%1").arg((char)inversion));
239  if (trans_mode >= 0)
240  query.bindValue(":TRANS_MODE", QString("%1").arg((char)trans_mode));
241 
242  if (!inner_FEC.isNull())
243  query.bindValue(":INNER_FEC", inner_FEC);
244  if (!constellation.isNull())
245  query.bindValue(":CONSTELLATION", constellation);
246  if (hierarchy >= 0)
247  query.bindValue(":HIERARCHY", QString("%1").arg((char)hierarchy));
248  if (!hp_code_rate.isNull())
249  query.bindValue(":HP_CODE_RATE", hp_code_rate);
250  if (!lp_code_rate.isNull())
251  query.bindValue(":LP_CODE_RATE", lp_code_rate);
252  if (!guard_interval.isNull())
253  query.bindValue(":GUARD_INTERVAL",guard_interval);
254  if (!mod_sys.isNull())
255  query.bindValue(":MOD_SYS", mod_sys);
256  if (!rolloff.isNull())
257  query.bindValue(":ROLLOFF", rolloff);
258 
259  if (!query.exec() || !query.isActive())
260  {
261  MythDB::DBError("Adding transport to Database.", query);
262  return 0;
263  }
264 
265  if (mplex)
266  return mplex;
267 
268  mplex = get_dtv_multiplex(
269  db_source_id, sistandard, frequency,
270  // DVB specific
271  transport_id, network_id, polarity);
272 
273  LOG(VB_CHANSCAN, LOG_INFO, QString("insert_dtv_multiplex -- ") +
274  QString("inserted %1").arg(mplex));
275 
276  return mplex;
277 }
278 
279 static void handle_transport_desc(vector<uint> &muxes,
280  const MPEGDescriptor &desc,
281  uint sourceid, uint tsid, uint netid)
282 {
283  uint tag = desc.DescriptorTag();
284 
286  {
288  uint64_t freq = cd.FrequencyHz();
289 
290  // Use the frequency we already have for this mplex
291  // as it may be one of the other_frequencies for this mplex
292  int mux = ChannelUtil::GetMplexID(sourceid, tsid, netid);
293  if (mux > 0)
294  {
295  QString dummy_mod;
296  QString dummy_sistd;
297  uint dummy_tsid, dummy_netid;
298  ChannelUtil::GetTuningParams(mux, dummy_mod, freq,
299  dummy_tsid, dummy_netid, dummy_sistd);
300  }
301 
303  (int)sourceid, "dvb",
304  freq, QString(),
305  // DVB specific
306  (int)tsid, (int)netid,
307  -1, cd.BandwidthString()[0].toLatin1(),
308  -1, 'a',
309  cd.TransmissionModeString()[0].toLatin1(),
310  QString(), cd.ConstellationString(),
311  cd.HierarchyString()[0].toLatin1(), cd.CodeRateHPString(),
313  QString(), QString());
314 
315  if (mux)
316  muxes.push_back(mux);
317 
318  /* unused
319  HighPriority()
320  IsTimeSlicingIndicatorUsed()
321  IsMPE_FECUsed()
322  NativeInterleaver()
323  Alpha()
324  */
325  }
327  {
328  const SatelliteDeliverySystemDescriptor cd(desc);
329 
331  sourceid, "dvb",
332  cd.FrequencyHz(), cd.ModulationString(),
333  // DVB specific
334  tsid, netid,
335  cd.SymbolRateHz(), -1,
336  cd.PolarizationString()[0].toLatin1(), 'a',
337  -1,
338  cd.FECInnerString(), QString(),
339  -1, QString(),
340  QString(), QString(),
342 
343  if (mux)
344  muxes.push_back(mux);
345 
346  /* unused
347  OrbitalPositionString() == OrbitalLocation
348  */
349  }
350  else if (tag == DescriptorID::cable_delivery_system)
351  {
352  const CableDeliverySystemDescriptor cd(desc);
353 
355  sourceid, "dvb",
356  cd.FrequencyHz(), cd.ModulationString(),
357  // DVB specific
358  tsid, netid,
359  cd.SymbolRateHz(), -1,
360  -1, 'a',
361  -1,
362  cd.FECInnerString(), QString(),
363  -1, QString(),
364  QString(), QString(),
365  QString(), QString());
366 
367  if (mux)
368  muxes.push_back(mux);
369  }
370 }
371 
372 uint ChannelUtil::CreateMultiplex(int sourceid, QString sistandard,
373  uint64_t frequency, QString modulation,
374  int transport_id, int network_id)
375 {
376  return CreateMultiplex(
377  sourceid, std::move(sistandard),
378  frequency, std::move(modulation),
379  transport_id, network_id,
380  -1, -1,
381  -1, -1,
382  -1,
383  QString(), QString(),
384  -1, QString(),
385  QString(), QString(),
386  QString(), QString());
387 }
388 
390  int sourceid, QString sistandard,
391  uint64_t freq, QString modulation,
392  // DVB specific
393  int transport_id, int network_id,
394  int symbol_rate, signed char bandwidth,
395  signed char polarity, signed char inversion,
396  signed char trans_mode,
397  QString inner_FEC, QString constellation,
398  signed char hierarchy, QString hp_code_rate,
399  QString lp_code_rate, QString guard_interval,
400  QString mod_sys, QString rolloff)
401 {
402  return insert_dtv_multiplex(
403  sourceid, std::move(sistandard),
404  freq, std::move(modulation),
405  // DVB specific
406  transport_id, network_id,
407  symbol_rate, bandwidth,
408  polarity, inversion,
409  trans_mode,
410  std::move(inner_FEC), std::move(constellation),
411  hierarchy, std::move(hp_code_rate),
412  std::move(lp_code_rate), std::move(guard_interval),
413  std::move(mod_sys), std::move(rolloff));
414 }
415 
417  int transport_id, int network_id)
418 {
419  return insert_dtv_multiplex(
420  sourceid, mux.m_sistandard,
421  mux.m_frequency, mux.m_modulation.toString(),
422  // DVB specific
423  transport_id, network_id,
424  mux.m_symbolrate, mux.m_bandwidth.toChar().toLatin1(),
425  mux.m_polarity.toChar().toLatin1(), mux.m_inversion.toChar().toLatin1(),
426  mux.m_trans_mode.toChar().toLatin1(),
427  mux.m_fec.toString(), mux.m_modulation.toString(),
428  mux.m_hierarchy.toChar().toLatin1(), mux.m_hp_code_rate.toString(),
430  mux.m_mod_sys.toString(), mux.m_rolloff.toString());
431 }
432 
433 
438  int sourceid, const NetworkInformationTable *nit)
439 {
440  vector<uint> muxes;
441 
442  if (sourceid <= 0)
443  return muxes;
444 
445  for (uint i = 0; i < nit->TransportStreamCount(); ++i)
446  {
447  const desc_list_t& list =
450 
451  uint tsid = nit->TSID(i);
452  uint netid = nit->OriginalNetworkID(i);
453  for (size_t j = 0; j < list.size(); ++j)
454  {
455  const MPEGDescriptor desc(list[j]);
456  handle_transport_desc(muxes, desc, sourceid, tsid, netid);
457  }
458  }
459  return muxes;
460 }
461 
462 uint ChannelUtil::GetMplexID(uint sourceid, const QString &channum)
463 {
464  MSqlQuery query(MSqlQuery::InitCon());
465  /* See if mplexid is already in the database */
466  query.prepare(
467  "SELECT mplexid "
468  "FROM channel "
469  "WHERE sourceid = :SOURCEID AND "
470  " channum = :CHANNUM");
471 
472  query.bindValue(":SOURCEID", sourceid);
473  query.bindValue(":CHANNUM", channum);
474 
475  if (!query.exec() || !query.isActive())
476  MythDB::DBError("GetMplexID 0", query);
477  else if (query.next())
478  return query.value(0).toInt();
479 
480  return 0;
481 }
482 
483 int ChannelUtil::GetMplexID(uint sourceid, uint64_t frequency)
484 {
485  MSqlQuery query(MSqlQuery::InitCon());
486  /* See if mplexid is already in the database */
487  query.prepare(
488  "SELECT mplexid "
489  "FROM dtv_multiplex "
490  "WHERE sourceid = :SOURCEID AND "
491  " frequency = :FREQUENCY");
492 
493  query.bindValue(":SOURCEID", sourceid);
494  query.bindValue(":FREQUENCY", QString::number(frequency));
495 
496  if (!query.exec() || !query.isActive())
497  {
498  MythDB::DBError("GetMplexID 1", query);
499  return -1;
500  }
501 
502  if (query.next())
503  return query.value(0).toInt();
504 
505  return -1;
506 }
507 
508 int ChannelUtil::GetMplexID(uint sourceid, uint64_t frequency,
509  uint transport_id, uint network_id)
510 {
511  MSqlQuery query(MSqlQuery::InitCon());
512  // See if transport already in database
513  query.prepare(
514  "SELECT mplexid "
515  "FROM dtv_multiplex "
516  "WHERE networkid = :NETWORKID AND "
517  " transportid = :TRANSPORTID AND "
518  " frequency = :FREQUENCY AND "
519  " sourceid = :SOURCEID");
520 
521  query.bindValue(":SOURCEID", sourceid);
522  query.bindValue(":NETWORKID", network_id);
523  query.bindValue(":TRANSPORTID", transport_id);
524  query.bindValue(":FREQUENCY", QString::number(frequency));
525 
526  if (!query.exec() || !query.isActive())
527  {
528  MythDB::DBError("GetMplexID 2", query);
529  return -1;
530  }
531 
532  if (query.next())
533  return query.value(0).toInt();
534 
535  return -1;
536 }
537 
539  uint transport_id, uint network_id)
540 {
541  MSqlQuery query(MSqlQuery::InitCon());
542  // See if transport already in database
543  query.prepare(
544  "SELECT mplexid "
545  "FROM dtv_multiplex "
546  "WHERE networkid = :NETWORKID AND "
547  " transportid = :TRANSPORTID AND "
548  " sourceid = :SOURCEID");
549 
550  query.bindValue(":SOURCEID", sourceid);
551  query.bindValue(":NETWORKID", network_id);
552  query.bindValue(":TRANSPORTID", transport_id);
553 
554  if (!query.exec() || !query.isActive())
555  {
556  MythDB::DBError("GetMplexID 3", query);
557  return -1;
558  }
559 
560  if (query.next())
561  return query.value(0).toInt();
562 
563  return -1;
564 }
565 
567 {
568  MSqlQuery query(MSqlQuery::InitCon());
569  /* See if mplexid is already in the database */
570  query.prepare(
571  "SELECT mplexid "
572  "FROM channel "
573  "WHERE chanid = :CHANID");
574 
575  query.bindValue(":CHANID", chanid);
576 
577  if (!query.exec())
578  MythDB::DBError("GetMplexID 4", query);
579  else if (query.next())
580  return query.value(0).toInt();
581 
582  return 0;
583 }
584 
607 // current_mplexid always exists in scanner, see ScanTranport()
608 //
609 int ChannelUtil::GetBetterMplexID(int current_mplexid,
610  int transport_id,
611  int network_id)
612 {
613  LOG(VB_CHANSCAN, LOG_INFO,
614  QString("GetBetterMplexID(mplexId %1, tId %2, netId %3)")
615  .arg(current_mplexid).arg(transport_id).arg(network_id));
616 
617  int q_networkid = 0, q_transportid = 0;
618  MSqlQuery query(MSqlQuery::InitCon());
619 
620  query.prepare("SELECT networkid, transportid "
621  "FROM dtv_multiplex "
622  "WHERE mplexid = :MPLEX_ID");
623 
624  query.bindValue(":MPLEX_ID", current_mplexid);
625 
626  if (!query.exec())
627  MythDB::DBError("Getting mplexid global search", query);
628  else if (query.next())
629  {
630  q_networkid = query.value(0).toInt();
631  q_transportid = query.value(1).toInt();
632  }
633 
634  // Got a match, return it.
635  if ((q_networkid == network_id) && (q_transportid == transport_id))
636  {
637  LOG(VB_CHANSCAN, LOG_INFO,
638  QString("GetBetterMplexID(): Returning perfect match %1")
639  .arg(current_mplexid));
640  return current_mplexid;
641  }
642 
643  // Not in DB at all, insert it
644  if (!q_networkid && !q_transportid)
645  {
646  int qsize = query.size();
647  query.prepare("UPDATE dtv_multiplex "
648  "SET networkid = :NETWORK_ID, "
649  " transportid = :TRANSPORT_ID "
650  "WHERE mplexid = :MPLEX_ID");
651 
652  query.bindValue(":NETWORK_ID", network_id);
653  query.bindValue(":TRANSPORT_ID", transport_id);
654  query.bindValue(":MPLEX_ID", current_mplexid);
655 
656  if (!query.exec())
657  MythDB::DBError("Getting mplexid global search", query);
658 
659  LOG(VB_CHANSCAN, LOG_INFO,
660  QString("GetBetterMplexID(): net id and transport id "
661  "are null, qsize(%1), Returning %2")
662  .arg(qsize).arg(current_mplexid));
663  return current_mplexid;
664  }
665 
666  // We have a partial match, so we try to do better...
667  QString theQueries[2] =
668  {
669  QString("SELECT a.mplexid "
670  "FROM dtv_multiplex a, dtv_multiplex b "
671  "WHERE a.networkid = :NETWORK_ID AND "
672  " a.transportid = :TRANSPORT_ID AND "
673  " a.sourceid = b.sourceid AND "
674  " b.mplexid = :MPLEX_ID"),
675 
676  QString("SELECT mplexid "
677  "FROM dtv_multiplex "
678  "WHERE networkid = :NETWORK_ID AND "
679  " transportid = :TRANSPORT_ID"),
680  };
681 
682  for (uint i=0; i<2; i++)
683  {
684  query.prepare(theQueries[i]);
685 
686  query.bindValue(":NETWORK_ID", network_id);
687  query.bindValue(":TRANSPORT_ID", transport_id);
688  if (i == 0)
689  query.bindValue(":MPLEX_ID", current_mplexid);
690 
691  if (!query.exec() || !query.isActive())
692  MythDB::DBError("Finding matching mplexid", query);
693 
694  if (query.size() == 1 && query.next())
695  {
696  LOG(VB_CHANSCAN, LOG_INFO,
697  QString("GetBetterMplexID(): query#%1 qsize(%2) "
698  "Returning %3")
699  .arg(i).arg(query.size()).arg(current_mplexid));
700  return query.value(0).toInt();
701  }
702 
703  if (query.next())
704  {
705  int ret = (i==0) ? current_mplexid : query.value(0).toInt();
706  LOG(VB_CHANSCAN, LOG_INFO,
707  QString("GetBetterMplexID(): query#%1 qsize(%2) "
708  "Returning %3")
709  .arg(i).arg(query.size()).arg(ret));
710  return ret;
711  }
712  }
713 
714  // If you still didn't find this combo return -1 (failure)
715  LOG(VB_CHANSCAN, LOG_INFO, "GetBetterMplexID(): Returning -1");
716  return -1;
717 }
718 
720  QString &modulation,
721  uint64_t &frequency,
722  uint &dvb_transportid,
723  uint &dvb_networkid,
724  QString &si_std)
725 {
726  if (!mplexid || (mplexid == 32767)) /* 32767 deals with old lineups */
727  return false;
728 
729  MSqlQuery query(MSqlQuery::InitCon());
730  query.prepare(
731  "SELECT transportid, networkid, frequency, modulation, sistandard "
732  "FROM dtv_multiplex "
733  "WHERE mplexid = :MPLEXID");
734  query.bindValue(":MPLEXID", mplexid);
735 
736  if (!query.exec())
737  {
738  MythDB::DBError("GetTuningParams failed ", query);
739  return false;
740  }
741 
742  if (!query.next())
743  return false;
744 
745  dvb_transportid = query.value(0).toUInt();
746  dvb_networkid = query.value(1).toUInt();
747  frequency = query.value(2).toULongLong();
748  modulation = query.value(3).toString();
749  si_std = query.value(4).toString();
750 
751  return true;
752 }
753 
754 QString ChannelUtil::GetChannelStringField(int chan_id, const QString &field)
755 {
756  if (chan_id < 0)
757  return QString();
758 
759  MSqlQuery query(MSqlQuery::InitCon());
760  query.prepare(QString("SELECT %1 FROM channel "
761  "WHERE chanid = :CHANID").arg(field));
762  query.bindValue(":CHANID", chan_id);
763  if (!query.exec())
764  {
765  MythDB::DBError("Selecting channel/dtv_multiplex 1", query);
766  return QString();
767  }
768 
769  if (!query.next())
770  return QString();
771 
772  return query.value(0).toString();
773 }
774 
775 QString ChannelUtil::GetChanNum(int chan_id)
776 {
777  return GetChannelStringField(chan_id, QString("channum"));
778 }
779 
781 {
782  return GetChannelStringField(chan_id, QString("tmoffset")).toInt();
783 }
784 
785 int ChannelUtil::GetSourceID(int db_mplexid)
786 {
787  MSqlQuery query(MSqlQuery::InitCon());
788 
789  query.prepare("SELECT sourceid "
790  "FROM dtv_multiplex "
791  "WHERE mplexid = :MPLEXID");
792  query.bindValue(":MPLEXID", db_mplexid);
793  if (!query.exec())
794  {
795  MythDB::DBError("Selecting channel/dtv_multiplex", query);
796  return -1;
797  }
798 
799  if (query.next())
800  return query.value(0).toInt();
801 
802  return -1;
803 }
804 
806 {
807  MSqlQuery query(MSqlQuery::InitCon());
808 
809  query.prepare(
810  "SELECT sourceid "
811  "FROM channel "
812  "WHERE chanid = :CHANID");
813  query.bindValue(":CHANID", chanid);
814 
815  if (!query.exec())
816  MythDB::DBError("Selecting channel/dtv_multiplex", query);
817  else if (query.next())
818  return query.value(0).toUInt();
819 
820  return 0;
821 }
822 
823 QStringList ChannelUtil::GetInputTypes(uint chanid)
824 {
825  MSqlQuery query(MSqlQuery::InitCon());
826  query.prepare("SELECT cardtype "
827  "FROM capturecard, channel "
828  "WHERE channel.chanid = :CHANID AND "
829  " channel.sourceid = capturecard.sourceid "
830  "GROUP BY cardtype");
831  query.bindValue(":CHANID", chanid);
832 
833  QStringList list;
834  if (!query.exec())
835  {
836  MythDB::DBError("ChannelUtil::GetInputTypes", query);
837  return list;
838  }
839  while (query.next())
840  list.push_back(query.value(0).toString());
841  return list;
842 }
843 
844 static bool lt_pidcache(
845  const pid_cache_item_t &a, const pid_cache_item_t &b)
846 {
847  return a.GetPID() < b.GetPID();
848 }
849 
857  pid_cache_t &pid_cache)
858 {
859  MSqlQuery query(MSqlQuery::InitCon());
860  query.prepare("SELECT pid, tableid FROM pidcache "
861  "WHERE chanid = :CHANID");
862 
863  query.bindValue(":CHANID", chanid);
864 
865  if (!query.exec())
866  {
867  MythDB::DBError("GetCachedPids: fetching pids", query);
868  return false;
869  }
870 
871  while (query.next())
872  {
873  int pid = query.value(0).toInt(), tid = query.value(1).toInt();
874  if ((pid >= 0) && (tid >= 0))
875  pid_cache.push_back(pid_cache_item_t(pid, tid));
876  }
877  stable_sort(pid_cache.begin(), pid_cache.end(), lt_pidcache);
878 
879  return true;
880 }
881 
889  const pid_cache_t &_pid_cache,
890  bool delete_all)
891 {
892  MSqlQuery query(MSqlQuery::InitCon());
893 
895  if (delete_all)
896  query.prepare("DELETE FROM pidcache WHERE chanid = :CHANID");
897  else
898  query.prepare(
899  "DELETE FROM pidcache "
900  "WHERE chanid = :CHANID AND tableid < 65536");
901 
902  query.bindValue(":CHANID", chanid);
903 
904  if (!query.exec())
905  {
906  MythDB::DBError("GetCachedPids -- delete", query);
907  return false;
908  }
909 
910  pid_cache_t old_cache;
911  GetCachedPids(chanid, old_cache);
912  pid_cache_t pid_cache = _pid_cache;
913  stable_sort(pid_cache.begin(), pid_cache.end(), lt_pidcache);
914 
916  query.prepare(
917  "INSERT INTO pidcache "
918  "SET chanid = :CHANID, pid = :PID, tableid = :TABLEID");
919  query.bindValue(":CHANID", chanid);
920 
921  bool ok = true;
922  pid_cache_t::const_iterator ito = old_cache.begin();
923  pid_cache_t::const_iterator itn = pid_cache.begin();
924  for (; itn != pid_cache.end(); ++itn)
925  {
926  // if old pid smaller than current new pid, skip this old pid
927  for (; ito != old_cache.end() && ito->GetPID() < itn->GetPID(); ++ito);
928 
929  // if already in DB, skip DB insert
930  if (ito != old_cache.end() && ito->GetPID() == itn->GetPID())
931  continue;
932 
933  query.bindValue(":PID", itn->GetPID());
934  query.bindValue(":TABLEID", itn->GetComposite());
935 
936  if (!query.exec())
937  {
938  MythDB::DBError("GetCachedPids -- insert", query);
939  ok = false;
940  }
941  }
942 
943  return ok;
944 }
945 
946 QString ChannelUtil::GetChannelValueStr(const QString &channel_field,
947  uint sourceid,
948  const QString &channum)
949 {
950  QString retval;
951 
952  MSqlQuery query(MSqlQuery::InitCon());
953 
954  query.prepare(
955  QString(
956  "SELECT channel.%1 "
957  "FROM channel "
958  "WHERE channum = :CHANNUM AND "
959  " sourceid = :SOURCEID")
960  .arg(channel_field));
961 
962  query.bindValue(":SOURCEID", sourceid);
963  query.bindValue(":CHANNUM", channum);
964 
965  if (!query.exec() || !query.isActive())
966  MythDB::DBError("getchannelvalue", query);
967  else if (query.next())
968  retval = query.value(0).toString();
969 
970  return retval;
971 }
972 
973 int ChannelUtil::GetChannelValueInt(const QString &channel_field,
974  uint sourceid,
975  const QString &channum)
976 {
977  QString val = GetChannelValueStr(channel_field, sourceid, channum);
978 
979  int retval = 0;
980  if (!val.isEmpty())
981  retval = val.toInt();
982 
983  return (retval) ? retval : -1;
984 }
985 
987  const QString &new_channum,
988  const QString &old_channum)
989 {
990  if (new_channum.isEmpty() || old_channum.isEmpty())
991  return false;
992 
993  if (new_channum == old_channum)
994  return true;
995 
996  uint old_mplexid = GetMplexID(srcid, old_channum);
997  if (!old_mplexid)
998  return false;
999 
1000  uint new_mplexid = GetMplexID(srcid, new_channum);
1001  if (!new_mplexid)
1002  return false;
1003 
1004  LOG(VB_CHANNEL, LOG_INFO, QString("IsOnSameMultiplex? %1==%2 -> %3")
1005  .arg(old_mplexid).arg(new_mplexid)
1006  .arg(old_mplexid == new_mplexid));
1007 
1008  return old_mplexid == new_mplexid;
1009 }
1010 
1016 static QStringList get_valid_recorder_list(uint chanid)
1017 {
1018  QStringList reclist;
1019 
1020  // Query the database to determine which source is being used currently.
1021  // set the EPG so that it only displays the channels of the current source
1022  MSqlQuery query(MSqlQuery::InitCon());
1023  // We want to get the current source id for this recorder
1024  query.prepare(
1025  "SELECT capturecard.cardid "
1026  "FROM channel "
1027  "LEFT JOIN capturecard ON channel.sourceid = capturecard.sourceid "
1028  "WHERE channel.chanid = :CHANID AND "
1029  " capturecard.livetvorder > 0 "
1030  "ORDER BY capturecard.livetvorder, capturecard.cardid");
1031  query.bindValue(":CHANID", chanid);
1032 
1033  if (!query.exec() || !query.isActive())
1034  {
1035  MythDB::DBError("get_valid_recorder_list ChanID", query);
1036  return reclist;
1037  }
1038 
1039  while (query.next())
1040  reclist << query.value(0).toString();
1041 
1042  return reclist;
1043 }
1044 
1050 static QStringList get_valid_recorder_list(const QString &channum)
1051 {
1052  QStringList reclist;
1053 
1054  // Query the database to determine which source is being used currently.
1055  // set the EPG so that it only displays the channels of the current source
1056  MSqlQuery query(MSqlQuery::InitCon());
1057  // We want to get the current source id for this recorder
1058  query.prepare(
1059  "SELECT capturecard.cardid "
1060  "FROM channel "
1061  "LEFT JOIN capturecard ON channel.sourceid = capturecard.sourceid "
1062  "WHERE channel.channum = :CHANNUM AND "
1063  " capturecard.livetvorder > 0 "
1064  "ORDER BY capturecard.livetvorder, capturecard.cardid");
1065  query.bindValue(":CHANNUM", channum);
1066 
1067  if (!query.exec() || !query.isActive())
1068  {
1069  MythDB::DBError("get_valid_recorder_list ChanNum", query);
1070  return reclist;
1071  }
1072 
1073  while (query.next())
1074  reclist << query.value(0).toString();
1075 
1076  return reclist;
1077 }
1078 
1087  uint chanid, const QString &channum)
1088 {
1089  if (chanid)
1090  return get_valid_recorder_list(chanid);
1091  if (!channum.isEmpty())
1092  return get_valid_recorder_list(channum);
1093  return QStringList();
1094 }
1095 
1096 
1097 vector<uint> ChannelUtil::GetConflicting(const QString &channum, uint sourceid)
1098 {
1099  MSqlQuery query(MSqlQuery::InitCon());
1100  vector<uint> conflicting;
1101 
1102  if (sourceid)
1103  {
1104  query.prepare(
1105  "SELECT chanid from channel "
1106  "WHERE sourceid = :SOURCEID AND "
1107  " channum = :CHANNUM");
1108  query.bindValue(":SOURCEID", sourceid);
1109  }
1110  else
1111  {
1112  query.prepare(
1113  "SELECT chanid from channel "
1114  "WHERE channum = :CHANNUM");
1115  }
1116 
1117  query.bindValue(":CHANNUM", channum);
1118  if (!query.exec())
1119  {
1120  MythDB::DBError("IsConflicting", query);
1121  conflicting.push_back(0);
1122  return conflicting;
1123  }
1124 
1125  while (query.next())
1126  conflicting.push_back(query.value(0).toUInt());
1127 
1128  return conflicting;
1129 }
1130 
1131 bool ChannelUtil::SetChannelValue(const QString &field_name,
1132  const QString& value,
1133  uint sourceid,
1134  const QString &channum)
1135 {
1136  MSqlQuery query(MSqlQuery::InitCon());
1137 
1138  query.prepare(
1139  QString("UPDATE channel SET channel.%1=:VALUE "
1140  "WHERE channel.channum = :CHANNUM AND "
1141  " channel.sourceid = :SOURCEID").arg(field_name));
1142 
1143  query.bindValue(":VALUE", value);
1144  query.bindValue(":CHANNUM", channum);
1145  query.bindValue(":SOURCEID", sourceid);
1146 
1147  return query.exec();
1148 }
1149 
1150 bool ChannelUtil::SetChannelValue(const QString &field_name,
1151  const QString& value,
1152  int chanid)
1153 {
1154  MSqlQuery query(MSqlQuery::InitCon());
1155 
1156  query.prepare(
1157  QString("UPDATE channel SET channel.%1=:VALUE "
1158  "WHERE channel.chanid = :CHANID").arg(field_name));
1159 
1160  query.bindValue(":VALUE", value);
1161  query.bindValue(":CHANID", chanid);
1162 
1163  return query.exec();
1164 }
1165 
1168 {
1169  static QReadWriteLock channel_default_authority_map_lock;
1170  static QMap<uint,QString> channel_default_authority_map;
1171  static bool run_init = true;
1172 
1173  channel_default_authority_map_lock.lockForRead();
1174 
1175  if (run_init)
1176  {
1177  channel_default_authority_map_lock.unlock();
1178  channel_default_authority_map_lock.lockForWrite();
1179  if (run_init)
1180  {
1181  MSqlQuery query(MSqlQuery::InitCon());
1182  query.prepare(
1183  "SELECT chanid, m.default_authority "
1184  "FROM channel c "
1185  "LEFT JOIN dtv_multiplex m "
1186  "ON (c.mplexid = m.mplexid)");
1187  if (query.exec())
1188  {
1189  while (query.next())
1190  {
1191  if (!query.value(1).toString().isEmpty())
1192  {
1193  channel_default_authority_map[query.value(0).toUInt()] =
1194  query.value(1).toString();
1195  }
1196  }
1197  run_init = false;
1198  }
1199  else
1200  {
1201  MythDB::DBError("GetDefaultAuthority 1", query);
1202  }
1203 
1204  query.prepare(
1205  "SELECT chanid, default_authority "
1206  "FROM channel");
1207  if (query.exec())
1208  {
1209  while (query.next())
1210  {
1211  if (!query.value(1).toString().isEmpty())
1212  {
1213  channel_default_authority_map[query.value(0).toUInt()] =
1214  query.value(1).toString();
1215  }
1216  }
1217  run_init = false;
1218  }
1219  else
1220  {
1221  MythDB::DBError("GetDefaultAuthority 2", query);
1222  }
1223 
1224  }
1225  }
1226 
1227  QMap<uint,QString>::iterator it = channel_default_authority_map.find(chanid);
1228  QString ret;
1229  if (it != channel_default_authority_map.end())
1230  ret = *it;
1231  channel_default_authority_map_lock.unlock();
1232 
1233  return ret;
1234 }
1235 
1237 {
1238  static QReadWriteLock channel_icon_map_lock;
1239  static QHash<uint,QString> channel_icon_map;
1240  static bool run_init = true;
1241 
1242  channel_icon_map_lock.lockForRead();
1243 
1244  QString ret(channel_icon_map.value(chanid, "_cold_"));
1245 
1246  channel_icon_map_lock.unlock();
1247 
1248  if (ret != "_cold_")
1249  return ret;
1250 
1251  channel_icon_map_lock.lockForWrite();
1252 
1253  MSqlQuery query(MSqlQuery::InitCon());
1254  QString iconquery = "SELECT chanid, icon FROM channel";
1255 
1256  if (run_init)
1257  iconquery += " WHERE visible = 1";
1258  else
1259  iconquery += " WHERE chanid = :CHANID";
1260 
1261  query.prepare(iconquery);
1262 
1263  if (!run_init)
1264  query.bindValue(":CHANID", chanid);
1265 
1266  if (query.exec())
1267  {
1268  if (run_init)
1269  {
1270  channel_icon_map.reserve(query.size());
1271  while (query.next())
1272  {
1273  channel_icon_map[query.value(0).toUInt()] =
1274  query.value(1).toString();
1275  }
1276  run_init = false;
1277  }
1278  else
1279  {
1280  channel_icon_map[chanid] = (query.next()) ?
1281  query.value(1).toString() : "";
1282  }
1283  }
1284  else
1285  {
1286  MythDB::DBError("GetIcon", query);
1287  }
1288 
1289  ret = channel_icon_map.value(chanid, "");
1290 
1291  channel_icon_map_lock.unlock();
1292 
1293  return ret;
1294 }
1295 
1297 {
1298  return tr("UNKNOWN", "Synthesized callsign");
1299 }
1300 
1301 int ChannelUtil::GetChanID(int mplexid, int service_transport_id,
1302  int major_channel, int minor_channel,
1303  int program_number)
1304 {
1305  MSqlQuery query(MSqlQuery::InitCon());
1306 
1307  // find source id, so we can find manually inserted ATSC channels
1308  query.prepare("SELECT sourceid "
1309  "FROM dtv_multiplex "
1310  "WHERE mplexid = :MPLEXID");
1311  query.bindValue(":MPLEXID", mplexid);
1312  if (!query.exec())
1313  {
1314  MythDB::DBError("Selecting channel/dtv_multiplex 2", query);
1315  return -1;
1316  }
1317  if (!query.next())
1318  return -1;
1319 
1320  int source_id = query.value(0).toInt();
1321 
1322  // find a proper ATSC channel
1323  query.prepare("SELECT chanid FROM channel,dtv_multiplex "
1324  "WHERE channel.sourceid = :SOURCEID AND "
1325  " atsc_major_chan = :MAJORCHAN AND "
1326  " atsc_minor_chan = :MINORCHAN AND "
1327  " dtv_multiplex.transportid = :TRANSPORTID AND "
1328  " dtv_multiplex.mplexid = :MPLEXID AND "
1329  " dtv_multiplex.sourceid = channel.sourceid AND "
1330  " dtv_multiplex.mplexid = channel.mplexid");
1331 
1332  query.bindValue(":SOURCEID", source_id);
1333  query.bindValue(":MAJORCHAN", major_channel);
1334  query.bindValue(":MINORCHAN", minor_channel);
1335  query.bindValue(":TRANSPORTID", service_transport_id);
1336  query.bindValue(":MPLEXID", mplexid);
1337 
1338  if (query.exec() && query.next())
1339  return query.value(0).toInt();
1340 
1341  // Find manually inserted/edited channels in order of scariness.
1342  // find renamed channel, where atsc is valid
1343  query.prepare("SELECT chanid FROM channel "
1344  "WHERE sourceid = :SOURCEID AND "
1345  "atsc_major_chan = :MAJORCHAN AND "
1346  "atsc_minor_chan = :MINORCHAN");
1347 
1348  query.bindValue(":SOURCEID", source_id);
1349  query.bindValue(":MAJORCHAN", major_channel);
1350  query.bindValue(":MINORCHAN", minor_channel);
1351 
1352  if (query.exec() && query.next())
1353  return query.value(0).toInt();
1354 
1355  // find based on mpeg program number and mplexid alone
1356  query.prepare("SELECT chanid FROM channel "
1357  "WHERE sourceid = :SOURCEID AND "
1358  "serviceID = :SERVICEID AND "
1359  "mplexid = :MPLEXID");
1360 
1361  query.bindValue(":SOURCEID", source_id);
1362  query.bindValue(":SERVICEID", program_number);
1363  query.bindValue(":MPLEXID", mplexid);
1364 
1365  if (query.exec() && query.next())
1366  return query.value(0).toInt();
1367 
1368  return -1;
1369 }
1370 
1371 uint ChannelUtil::FindChannel(uint sourceid, const QString &freqid)
1372 {
1373  MSqlQuery query(MSqlQuery::InitCon());
1374  query.prepare("SELECT chanid "
1375  "FROM channel "
1376  "WHERE sourceid = :SOURCEID AND "
1377  " freqid = :FREQID");
1378 
1379  query.bindValue(":SOURCEID", sourceid);
1380  query.bindValue(":FREQID", freqid);
1381 
1382  if (!query.exec() || !query.isActive())
1383  MythDB::DBError("FindChannel", query);
1384  else if (query.next())
1385  return query.value(0).toUInt();
1386 
1387  return 0;
1388 }
1389 
1390 
1391 static uint get_max_chanid(uint sourceid)
1392 {
1393  QString qstr = "SELECT MAX(chanid) FROM channel ";
1394  qstr += (sourceid) ? "WHERE sourceid = :SOURCEID" : "";
1395 
1397  query.prepare(qstr);
1398 
1399  if (sourceid)
1400  query.bindValue(":SOURCEID", sourceid);
1401 
1402  if (!query.exec() || !query.isActive())
1403  MythDB::DBError("Getting chanid for new channel (2)", query);
1404  else if (!query.next())
1405  LOG(VB_GENERAL, LOG_ERR, "Error getting chanid for new channel.");
1406  else
1407  return query.value(0).toUInt();
1408 
1409  return 0;
1410 }
1411 
1412 static bool chanid_available(uint chanid)
1413 {
1415  query.prepare(
1416  "SELECT chanid "
1417  "FROM channel "
1418  "WHERE chanid = :CHANID");
1419  query.bindValue(":CHANID", chanid);
1420 
1421  if (!query.exec() || !query.isActive())
1422  MythDB::DBError("is_chan_id_available", query);
1423  else if (query.size() == 0)
1424  return true;
1425 
1426  return false;
1427 }
1428 
1433 int ChannelUtil::CreateChanID(uint sourceid, const QString &chan_num)
1434 {
1435  // first try to base it on the channel number for human readability
1436  uint chanid = 0;
1437  int chansep = chan_num.indexOf(QRegExp("\\D"));
1438  if (chansep > 0)
1439  {
1440  chanid =
1441  sourceid * 10000 +
1442  chan_num.left(chansep).toInt() * 100 +
1443  chan_num.right(chan_num.length() - chansep - 1).toInt();
1444  }
1445  else
1446  {
1447  chanid = sourceid * 10000 + chan_num.toInt();
1448  }
1449 
1450  if ((chanid > sourceid * 10000) && (chanid_available(chanid)))
1451  return chanid;
1452 
1453  // try to at least base it on the sourceid for human readability
1454  chanid = max(get_max_chanid(sourceid) + 1, sourceid * 10000);
1455 
1456  if (chanid_available(chanid))
1457  return chanid;
1458 
1459  // just get a chanid we know should work
1460  chanid = get_max_chanid(0) + 1;
1461 
1462  if (chanid_available(chanid))
1463  return chanid;
1464 
1465  // failure
1466  return -1;
1467 }
1468 
1470  uint db_sourceid,
1471  uint new_channel_id,
1472  const QString &callsign,
1473  const QString &service_name,
1474  const QString &chan_num,
1475  uint service_id,
1476  uint atsc_major_channel,
1477  uint atsc_minor_channel,
1478  bool use_on_air_guide,
1479  bool hidden,
1480  bool hidden_in_guide,
1481  const QString &freqid,
1482  QString icon,
1483  QString format,
1484  QString xmltvid,
1485  QString default_authority)
1486 {
1487  MSqlQuery query(MSqlQuery::InitCon());
1488 
1489  QString chanNum = (chan_num == "-1") ?
1490  QString::number(service_id) : chan_num;
1491 
1492  QString qstr =
1493  "INSERT INTO channel "
1494  " (chanid, channum, sourceid, "
1495  " callsign, name, serviceid, ";
1496  qstr += (db_mplexid > 0) ? "mplexid, " : "";
1497  qstr += (!freqid.isEmpty()) ? "freqid, " : "";
1498  qstr +=
1499  " atsc_major_chan, atsc_minor_chan, "
1500  " useonairguide, visible, tvformat, "
1501  " icon, xmltvid, default_authority) "
1502  "VALUES "
1503  " (:CHANID, :CHANNUM, :SOURCEID, "
1504  " :CALLSIGN, :NAME, :SERVICEID, ";
1505  qstr += (db_mplexid > 0) ? ":MPLEXID, " : "";
1506  qstr += (!freqid.isEmpty()) ? ":FREQID, " : "";
1507  qstr +=
1508  " :MAJORCHAN, :MINORCHAN, "
1509  " :USEOAG, :VISIBLE, :TVFORMAT, "
1510  " :ICON, :XMLTVID, :AUTHORITY) ";
1511 
1512  query.prepare(qstr);
1513 
1514  query.bindValue (":CHANID", new_channel_id);
1515  query.bindValueNoNull(":CHANNUM", chanNum);
1516  query.bindValue (":SOURCEID", db_sourceid);
1517  query.bindValueNoNull(":CALLSIGN", callsign);
1518  query.bindValueNoNull(":NAME", service_name);
1519 
1520  if (db_mplexid > 0)
1521  query.bindValue(":MPLEXID", db_mplexid);
1522 
1523  query.bindValue(":SERVICEID", service_id);
1524  query.bindValue(":MAJORCHAN", atsc_major_channel);
1525  query.bindValue(":MINORCHAN", atsc_minor_channel);
1526  query.bindValue(":USEOAG", use_on_air_guide);
1527  query.bindValue(":VISIBLE", !hidden);
1528  (void) hidden_in_guide; // MythTV can't hide the channel in just the guide.
1529 
1530  if (!freqid.isEmpty())
1531  query.bindValue(":FREQID", freqid);
1532 
1533  QString tvformat = (atsc_minor_channel > 0) ? "ATSC" : std::move(format);
1534  query.bindValueNoNull(":TVFORMAT", tvformat);
1535  query.bindValueNoNull(":ICON", icon);
1536  query.bindValueNoNull(":XMLTVID", xmltvid);
1537  query.bindValueNoNull(":AUTHORITY", default_authority);
1538 
1539  if (!query.exec() || !query.isActive())
1540  {
1541  MythDB::DBError("Adding Service", query);
1542  return false;
1543  }
1544  return true;
1545 }
1546 
1548  uint source_id,
1549  uint channel_id,
1550  const QString &callsign,
1551  const QString &service_name,
1552  const QString &chan_num,
1553  uint service_id,
1554  uint atsc_major_channel,
1555  uint atsc_minor_channel,
1556  bool use_on_air_guide,
1557  bool hidden,
1558  bool hidden_in_guide,
1559  const QString& freqid,
1560  const QString& icon,
1561  QString format,
1562  const QString& xmltvid,
1563  const QString& default_authority)
1564 {
1565  if (!channel_id)
1566  return false;
1567 
1568  QString tvformat = (atsc_minor_channel > 0) ? "ATSC" : std::move(format);
1569  bool set_channum = !chan_num.isEmpty() && chan_num != "-1";
1570  QString qstr = QString(
1571  "UPDATE channel "
1572  "SET %1 %2 %3 %4 %5 %6"
1573  " mplexid = :MPLEXID, serviceid = :SERVICEID, "
1574  " atsc_major_chan = :MAJORCHAN, atsc_minor_chan = :MINORCHAN, "
1575  " callsign = :CALLSIGN, name = :NAME, "
1576  " sourceid = :SOURCEID, useonairguide = :USEOAG, "
1577  " visible = :VISIBLE "
1578  "WHERE chanid=:CHANID")
1579  .arg((!set_channum) ? "" : "channum = :CHANNUM, ")
1580  .arg((freqid.isEmpty()) ? "" : "freqid = :FREQID, ")
1581  .arg((icon.isEmpty()) ? "" : "icon = :ICON, ")
1582  .arg((tvformat.isEmpty()) ? "" : "tvformat = :TVFORMAT, ")
1583  .arg((xmltvid.isEmpty()) ? "" : "xmltvid = :XMLTVID, ")
1584  .arg((default_authority.isEmpty()) ?
1585  "" : "default_authority = :AUTHORITY,");
1586 
1587  MSqlQuery query(MSqlQuery::InitCon());
1588  query.prepare(qstr);
1589 
1590  query.bindValue(":CHANID", channel_id);
1591 
1592  if (set_channum)
1593  query.bindValue(":CHANNUM", chan_num);
1594 
1595  query.bindValue (":SOURCEID", source_id);
1596  query.bindValueNoNull(":CALLSIGN", callsign);
1597  query.bindValueNoNull(":NAME", service_name);
1598 
1599  query.bindValue(":MPLEXID", db_mplexid);
1600 
1601  query.bindValue(":SERVICEID", service_id);
1602  query.bindValue(":MAJORCHAN", atsc_major_channel);
1603  query.bindValue(":MINORCHAN", atsc_minor_channel);
1604  query.bindValue(":USEOAG", use_on_air_guide);
1605  query.bindValue(":VISIBLE", !hidden);
1606  (void) hidden_in_guide; // MythTV can't hide the channel in just the guide.
1607 
1608  if (!freqid.isEmpty())
1609  query.bindValue(":FREQID", freqid);
1610 
1611  if (!tvformat.isEmpty())
1612  query.bindValue(":TVFORMAT", tvformat);
1613 
1614  if (!icon.isEmpty())
1615  query.bindValue(":ICON", icon);
1616  if (!xmltvid.isEmpty())
1617  query.bindValue(":XMLTVID", xmltvid);
1618  if (!default_authority.isEmpty())
1619  query.bindValue(":AUTHORITY", default_authority);
1620 
1621  if (!query.exec())
1622  {
1623  MythDB::DBError("Updating Service", query);
1624  return false;
1625  }
1626  return true;
1627 }
1628 
1630 {
1631  MSqlQuery query(MSqlQuery::InitCon());
1632  query.prepare(
1633  "SELECT channum "
1634  "FROM channel "
1635  "WHERE chanid = :ID");
1636  query.bindValue(":ID", chan.m_channel_id);
1637 
1638  if (!query.exec())
1639  {
1640  MythDB::DBError("UpdateChannelNumberFromDB", query);
1641  return;
1642  }
1643 
1644  if (query.next())
1645  {
1646  QString channum = query.value(0).toString();
1647 
1648  if (!channum.isEmpty())
1649  {
1650  chan.m_chan_num = channum;
1651  }
1652  }
1653 }
1654 
1656 {
1657  MSqlQuery query(MSqlQuery::InitCon());
1658  query.prepare(
1659  "SELECT xmltvid, useonairguide, visible "
1660  "FROM channel "
1661  "WHERE chanid = :ID");
1662  query.bindValue(":ID", chan.m_channel_id);
1663 
1664  if (!query.exec())
1665  {
1666  MythDB::DBError("UpdateInsertInfoFromDB", query);
1667  return;
1668  }
1669 
1670  if (query.next())
1671  {
1672  QString xmltvid = query.value(0).toString();
1673  bool useeit = query.value(1).toBool();
1674  bool visible = query.value(2).toBool();
1675 
1676  if (!xmltvid.isEmpty())
1677  {
1678  if (useeit)
1679  LOG(VB_GENERAL, LOG_ERR,
1680  "Using EIT and xmltv for the same channel "
1681  "is an unsupported configuration.");
1682  chan.m_xmltvid = xmltvid;
1683  }
1684  chan.m_use_on_air_guide = useeit;
1685  chan.m_hidden = !visible;
1686  }
1687 }
1688 
1690  uint channel_id, const IPTVTuningData &tuning)
1691 {
1692  MSqlQuery query(MSqlQuery::InitCon());
1693 
1694  query.prepare(
1695  "DELETE FROM iptv_channel "
1696  "WHERE chanid=:CHANID");
1697  query.bindValue(":CHANID", channel_id);
1698 
1699  if (!query.exec())
1700  {
1701  MythDB::DBError("UpdateIPTVTuningData -- delete", query);
1702  return false;
1703  }
1704 
1705  query.prepare(
1706  "INSERT INTO iptv_channel (chanid, url, type, bitrate) "
1707  "VALUES (:CHANID, :URL, :TYPE, :BITRATE)");
1708  query.bindValue(":CHANID", channel_id);
1709 
1710  query.bindValue(":URL", tuning.GetDataURL().toString());
1711  query.bindValue(":TYPE", tuning.GetFECTypeString(0));
1712  query.bindValue(":BITRATE", tuning.GetBitrate(0));
1713 
1714  if (!query.exec())
1715  {
1716  MythDB::DBError("UpdateIPTVTuningData -- data", query);
1717  return false;
1718  }
1719 
1720  if (tuning.GetFECURL0().port() >= 0)
1721  {
1722  query.bindValue(":URL", tuning.GetFECURL0().toString());
1723  query.bindValue(":TYPE", tuning.GetFECTypeString(1));
1724  query.bindValue(":BITRATE", tuning.GetBitrate(1));
1725  if (!query.exec())
1726  {
1727  MythDB::DBError("UpdateIPTVTuningData -- fec 0", query);
1728  return false;
1729  }
1730  }
1731 
1732  if (tuning.GetFECURL1().port() >= 0)
1733  {
1734  query.bindValue(":URL", tuning.GetFECURL1().toString());
1735  query.bindValue(":TYPE", tuning.GetFECTypeString(2));
1736  query.bindValue(":BITRATE", tuning.GetBitrate(2));
1737  if (!query.exec())
1738  {
1739  MythDB::DBError("UpdateIPTVTuningData -- fec 1", query);
1740  return false;
1741  }
1742  }
1743 
1744  return true;
1745 }
1746 
1748 {
1749  MSqlQuery query(MSqlQuery::InitCon());
1750  query.prepare(
1751  "DELETE FROM channel "
1752  "WHERE chanid = :ID");
1753  query.bindValue(":ID", channel_id);
1754 
1755  if (!query.exec())
1756  {
1757  MythDB::DBError("Delete Channel", query);
1758  return false;
1759  }
1760 
1761  query.prepare(
1762  "DELETE FROM iptv_channel "
1763  "WHERE chanid = :ID");
1764  query.bindValue(":ID", channel_id);
1765 
1766  if (!query.exec())
1767  {
1768  MythDB::DBError("Delete Channel 2", query);
1769  return false;
1770  }
1771 
1772  return true;
1773 }
1774 
1775 bool ChannelUtil::SetVisible(uint channel_id, bool visible)
1776 {
1777  MSqlQuery query(MSqlQuery::InitCon());
1778  query.prepare(
1779  "UPDATE channel "
1780  "SET visible = :VISIBLE "
1781  "WHERE chanid = :ID");
1782  query.bindValue(":ID", channel_id);
1783  query.bindValue(":VISIBLE", visible);
1784 
1785  if (!query.exec())
1786  {
1787  MythDB::DBError("ChannelUtil::SetVisible", query);
1788  return false;
1789  }
1790 
1791  return true;
1792 }
1793 
1795 {
1796  MSqlQuery query(MSqlQuery::InitCon());
1797 
1798  query.prepare("UPDATE dtv_multiplex "
1799  "SET serviceversion = :VERSION "
1800  "WHERE mplexid = :MPLEXID");
1801 
1802  query.bindValue(":VERSION", version);
1803  query.bindValue(":MPLEXID", mplexid);
1804 
1805  if (!query.exec())
1806  {
1807  MythDB::DBError("Selecting channel/dtv_multiplex", query);
1808  return false;
1809  }
1810  return true;
1811 }
1812 
1814 {
1815  MSqlQuery query(MSqlQuery::InitCon());
1816 
1817  query.prepare("SELECT serviceversion "
1818  "FROM dtv_multiplex "
1819  "WHERE mplexid = :MPLEXID");
1820 
1821  query.bindValue(":MPLEXID", mplexid);
1822 
1823  if (!query.exec())
1824  {
1825  MythDB::DBError("Selecting channel/dtv_multiplex", query);
1826  return 0;
1827  }
1828 
1829  if (query.next())
1830  return query.value(0).toInt();
1831 
1832  return -1;
1833 }
1834 
1835 bool ChannelUtil::GetATSCChannel(uint sourceid, const QString &channum,
1836  uint &major, uint &minor)
1837 {
1838  major = minor = 0;
1839 
1840  MSqlQuery query(MSqlQuery::InitCon());
1841  query.prepare(
1842  "SELECT atsc_major_chan, atsc_minor_chan "
1843  "FROM channel "
1844  "WHERE channum = :CHANNUM AND "
1845  " sourceid = :SOURCEID");
1846 
1847  query.bindValue(":SOURCEID", sourceid);
1848  query.bindValue(":CHANNUM", channum);
1849 
1850  if (!query.exec() || !query.isActive())
1851  MythDB::DBError("getatscchannel", query);
1852  else if (query.next())
1853  {
1854  major = query.value(0).toUInt();
1855  minor = query.value(1).toUInt();
1856  return true;
1857  }
1858 
1859  return false;
1860 }
1861 
1863  uint sourceid,
1864  uint &chanid, const QString &channum,
1865  QString &tvformat, QString &modulation,
1866  QString &freqtable, QString &freqid,
1867  int &finetune, uint64_t &frequency,
1868  QString &dtv_si_std, int &mpeg_prog_num,
1869  uint &atsc_major, uint &atsc_minor,
1870  uint &dvb_transportid, uint &dvb_networkid,
1871  uint &mplexid,
1872  bool &commfree)
1873 {
1874  chanid = 0;
1875  tvformat.clear();
1876  modulation.clear();
1877  freqtable.clear();;
1878  freqid.clear();
1879  dtv_si_std.clear();
1880  finetune = 0;
1881  frequency = 0;
1882  mpeg_prog_num = -1;
1883  atsc_major = atsc_minor = mplexid = 0;
1884  dvb_networkid = dvb_transportid = 0;
1885  commfree = false;
1886 
1887  int found = 0;
1888  MSqlQuery query(MSqlQuery::InitCon());
1889  query.prepare(
1890  "SELECT finetune, freqid, tvformat, freqtable, "
1891  " commmethod, mplexid, "
1892  " atsc_major_chan, atsc_minor_chan, serviceid, "
1893  " chanid, visible "
1894  "FROM channel, videosource "
1895  "WHERE videosource.sourceid = channel.sourceid AND "
1896  " channum = :CHANNUM AND "
1897  " channel.sourceid = :SOURCEID "
1898  "ORDER BY channel.visible DESC, channel.chanid ");
1899  query.bindValue(":CHANNUM", channum);
1900  query.bindValue(":SOURCEID", sourceid);
1901 
1902  if (!query.exec() || !query.isActive())
1903  {
1904  MythDB::DBError("GetChannelData", query);
1905  return false;
1906  }
1907 
1908  if (query.next())
1909  {
1910  finetune = query.value(0).toInt();
1911  freqid = query.value(1).toString();
1912  tvformat = query.value(2).toString();
1913  freqtable = query.value(3).toString();
1914  commfree = (query.value(4).toInt() == -2);
1915  mplexid = query.value(5).toUInt();
1916  atsc_major = query.value(6).toUInt();
1917  atsc_minor = query.value(7).toUInt();
1918  mpeg_prog_num = (query.value(8).isNull()) ? -1
1919  : query.value(8).toInt();
1920  chanid = query.value(9).toUInt();
1921 
1922  found += query.value(10).toInt();
1923  }
1924 
1925  while (query.next())
1926  found += query.value(10).toInt();
1927 
1928  if (found == 0 && chanid)
1929  {
1930  LOG(VB_GENERAL, LOG_WARNING,
1931  QString("No visible channels for %1, using invisble chanid %2")
1932  .arg(channum).arg(chanid));
1933  }
1934 
1935  if (found > 1)
1936  {
1937  LOG(VB_GENERAL, LOG_WARNING,
1938  QString("Found multiple visible channels for %1, using chanid %2")
1939  .arg(channum).arg(chanid));
1940  }
1941 
1942  if (!chanid)
1943  {
1944  LOG(VB_GENERAL, LOG_ERR,
1945  QString("GetChannelData() failed because it could not\n"
1946  "\t\t\tfind channel number '%1' in DB for source '%2'.")
1947  .arg(channum).arg(sourceid));
1948  return false;
1949  }
1950 
1951  if (!mplexid || (mplexid == 32767)) /* 32767 deals with old lineups */
1952  return true;
1953 
1954  return GetTuningParams(mplexid, modulation, frequency,
1955  dvb_transportid, dvb_networkid, dtv_si_std);
1956 }
1957 
1959 {
1960  MSqlQuery query(MSqlQuery::InitCon());
1961  query.prepare(
1962  "SELECT type+0, url, bitrate "
1963  "FROM iptv_channel "
1964  "WHERE chanid = :CHANID "
1965  "ORDER BY type+0");
1966  query.bindValue(":CHANID", chanid);
1967 
1968  if (!query.exec())
1969  {
1970  MythDB::DBError("GetChannelData -- iptv", query);
1971  return IPTVTuningData();
1972  }
1973 
1974  QString data_url, fec_url0, fec_url1;
1976  uint bitrate[3] = { 0, 0, 0, };
1977  while (query.next())
1978  {
1980  query.value(0).toUInt();
1981  switch (type)
1982  {
1983  case IPTVTuningData::kData:
1984  data_url = query.value(1).toString();
1985  bitrate[0] = query.value(2).toUInt();
1986  break;
1990  fec_url0 = query.value(1).toString();
1991  bitrate[1] = query.value(2).toUInt();
1992  break;
1996  fec_url1 = query.value(1).toString();
1997  bitrate[2] = query.value(2).toUInt();
1998  break;
1999  }
2000  switch (type)
2001  {
2002  case IPTVTuningData::kData:
2003  break;
2005  fec_type = IPTVTuningData::kRFC2733;
2006  break;
2008  fec_type = IPTVTuningData::kRFC5109;
2009  break;
2011  fec_type = IPTVTuningData::kSMPTE2022;
2012  break;
2016  break; // will be handled by type of first FEC stream
2017  }
2018  }
2019 
2020  IPTVTuningData tuning(data_url, bitrate[0], fec_type,
2021  fec_url0, bitrate[1], fec_url1, bitrate[2]);
2022  LOG(VB_GENERAL, LOG_INFO, QString("Loaded %1 for %2")
2023  .arg(tuning.GetDeviceName()).arg(chanid));
2024  return tuning;
2025 }
2026 
2027 // TODO This should be modified to load a complete channelinfo object including
2028 // all fields from the database
2033  uint sourceid, bool visible_only, bool include_disconnected,
2034  const QString &group_by, uint channel_groupid)
2035 {
2036  ChannelInfoList list;
2037 
2038  MSqlQuery query(MSqlQuery::InitCon());
2039 
2040  QString qstr = QString(
2041  "SELECT channum, callsign, channel.chanid, "
2042  " atsc_major_chan, atsc_minor_chan, "
2043  " name, icon, mplexid, visible, "
2044  " channel.sourceid, GROUP_CONCAT(DISTINCT capturecard.cardid), "
2045  " GROUP_CONCAT(DISTINCT channelgroup.grpid), "
2046  " xmltvid "
2047  "FROM channel "
2048  "LEFT JOIN channelgroup ON channel.chanid = channelgroup.chanid "
2049  " %1 JOIN capturecard ON capturecard.sourceid = channel.sourceid ")
2050  .arg((include_disconnected) ? "LEFT" : "");
2051 
2052  QString cond = " WHERE ";
2053 
2054  if (sourceid)
2055  {
2056  qstr += QString("WHERE channel.sourceid='%1' ").arg(sourceid);
2057  cond = " AND ";
2058  }
2059 
2060  // Select only channels from the specified channel group
2061  if (channel_groupid > 0)
2062  {
2063  qstr += QString("%1 channelgroup.grpid = '%2' ")
2064  .arg(cond).arg(channel_groupid);
2065  cond = " AND ";
2066  }
2067 
2068  if (visible_only)
2069  {
2070  qstr += QString("%1 visible=1 ").arg(cond);
2071  cond = " AND ";
2072  }
2073 
2074  qstr += " GROUP BY chanid";
2075 
2076  if (!group_by.isEmpty())
2077  qstr += QString(", %1").arg(group_by);
2078 
2079  query.prepare(qstr);
2080  if (!query.exec())
2081  {
2082  MythDB::DBError("ChannelUtil::GetChannels()", query);
2083  return list;
2084  }
2085 
2086  while (query.next())
2087  {
2088  if (query.value(0).toString().isEmpty() || !query.value(2).toBool())
2089  continue; // skip if channum blank, or chanid empty
2090 
2091  ChannelInfo chan(
2092  query.value(0).toString(), /* channum */
2093  query.value(1).toString(), /* callsign */
2094  query.value(2).toUInt(), /* chanid */
2095  query.value(3).toUInt(), /* ATSC major */
2096  query.value(4).toUInt(), /* ATSC minor */
2097  query.value(7).toUInt(), /* mplexid */
2098  query.value(8).toBool(), /* visible */
2099  query.value(5).toString(), /* name */
2100  query.value(6).toString(), /* icon */
2101  query.value(9).toUInt()); /* sourceid */
2102 
2103  chan.m_xmltvid = query.value(12).toString(); /* xmltvid */
2104 
2105  QStringList inputIDs = query.value(11).toString().split(",");
2106  QString inputid;
2107  while (!inputIDs.isEmpty())
2108  chan.AddInputId(inputIDs.takeFirst().toUInt());
2109 
2110  QStringList groupIDs = query.value(10).toString().split(",");
2111  QString groupid;
2112  while (!groupIDs.isEmpty())
2113  chan.AddGroupId(groupIDs.takeFirst().toUInt());
2114 
2115  list.push_back(chan);
2116 
2117  }
2118 
2119  return list;
2120 }
2121 
2122 vector<uint> ChannelUtil::GetChanIDs(int sourceid, bool onlyVisible)
2123 {
2124  MSqlQuery query(MSqlQuery::InitCon());
2125 
2126  QString select = "SELECT chanid FROM channel ";
2127  // Yes, this a little ugly
2128  if (onlyVisible || sourceid > 0)
2129  {
2130  select += "WHERE ";
2131  if (onlyVisible)
2132  select += "visible = 1 ";
2133  if (sourceid > 0)
2134  {
2135  if (onlyVisible)
2136  select += "AND ";
2137  select += "sourceid=" + QString::number(sourceid);
2138  }
2139  }
2140 
2141  vector<uint> list;
2142  if (!query.exec(select))
2143  {
2144  MythDB::DBError("SourceUtil::GetChanIDs()", query);
2145  return list;
2146  }
2147 
2148  while (query.next())
2149  list.push_back(query.value(0).toUInt());
2150 
2151  return list;
2152 }
2153 
2154 inline bool lt_callsign(const ChannelInfo &a, const ChannelInfo &b)
2155 {
2156  return naturalCompare(a.m_callsign, b.m_callsign) < 0;
2157 }
2158 
2159 inline bool lt_smart(const ChannelInfo &a, const ChannelInfo &b)
2160 {
2161  static QMutex sepExprLock;
2162  static const QRegExp sepExpr(ChannelUtil::kATSCSeparators);
2163 
2164  bool isIntA, isIntB;
2165  int a_int = a.m_channum.toUInt(&isIntA);
2166  int b_int = b.m_channum.toUInt(&isIntB);
2167  int a_major = a.m_atsc_major_chan;
2168  int b_major = b.m_atsc_major_chan;
2169  int a_minor = a.m_atsc_minor_chan;
2170  int b_minor = b.m_atsc_minor_chan;
2171 
2172  // Extract minor and major numbers from channum..
2173  bool tmp1, tmp2;
2174  int idxA, idxB;
2175  {
2176  QMutexLocker locker(&sepExprLock);
2177  idxA = a.m_channum.indexOf(sepExpr);
2178  idxB = b.m_channum.indexOf(sepExpr);
2179  }
2180  if (idxA >= 0)
2181  {
2182  int major = a.m_channum.left(idxA).toUInt(&tmp1);
2183  int minor = a.m_channum.mid(idxA+1).toUInt(&tmp2);
2184  if (tmp1 && tmp2)
2185  (a_major = major), (a_minor = minor), (isIntA = false);
2186  }
2187 
2188  if (idxB >= 0)
2189  {
2190  int major = b.m_channum.left(idxB).toUInt(&tmp1);
2191  int minor = b.m_channum.mid(idxB+1).toUInt(&tmp2);
2192  if (tmp1 && tmp2)
2193  (b_major = major), (b_minor = minor), (isIntB = false);
2194  }
2195 
2196  // If ATSC channel has been renumbered, sort by new channel number
2197  if ((a_minor > 0) && isIntA)
2198  {
2199  int atsc_int = (QString("%1%2").arg(a_major).arg(a_minor)).toInt();
2200  a_minor = (atsc_int == a_int) ? a_minor : 0;
2201  }
2202 
2203  if ((b_minor > 0) && isIntB)
2204  {
2205  int atsc_int = (QString("%1%2").arg(b_major).arg(b_minor)).toInt();
2206  b_minor = (atsc_int == b_int) ? b_minor : 0;
2207  }
2208 
2209  // one of the channels is an ATSC channel, and the other
2210  // is either ATSC or is numeric.
2211  if ((a_minor || b_minor) &&
2212  (a_minor || isIntA) && (b_minor || isIntB))
2213  {
2214  int a_maj = (!a_minor && isIntA) ? a_int : a_major;
2215  int b_maj = (!b_minor && isIntB) ? b_int : b_major;
2216  int cmp;
2217  if ((cmp = a_maj - b_maj))
2218  return cmp < 0;
2219 
2220  if ((cmp = a_minor - b_minor))
2221  return cmp < 0;
2222  }
2223 
2224  if (isIntA && isIntB)
2225  {
2226  // both channels have a numeric channum
2227  int cmp = a_int - b_int;
2228  if (cmp)
2229  return cmp < 0;
2230  }
2231  else if (isIntA ^ isIntB)
2232  {
2233  // if only one is channel numeric always consider it less than
2234  return isIntA;
2235  }
2236  else
2237  {
2238  // neither of channels have a numeric channum
2239  int cmp = naturalCompare(a.m_channum, b.m_channum);
2240  if (cmp)
2241  return cmp < 0;
2242  }
2243 
2244  return lt_callsign(a,b);
2245 }
2246 
2248 {
2249  MSqlQuery query(MSqlQuery::InitCon());
2250  QString select;
2251 
2252 
2253  select = "SELECT chanid FROM channel";
2254  if (sourceid >= 0)
2255  select += " WHERE sourceid=" + QString::number(sourceid);
2256  select += ';';
2257 
2258  query.prepare(select);
2259 
2260  if (!query.exec() || !query.isActive())
2261  return 0;
2262 
2263  return query.size();
2264 }
2265 
2266 void ChannelUtil::SortChannels(ChannelInfoList &list, const QString &order,
2267  bool eliminate_duplicates)
2268 {
2269  bool cs = order.toLower() == "callsign";
2270  if (cs)
2271  stable_sort(list.begin(), list.end(), lt_callsign);
2272  else /* if (sortorder == "channum") */
2273  stable_sort(list.begin(), list.end(), lt_smart);
2274 
2275  if (eliminate_duplicates && !list.empty())
2276  {
2278  tmp.push_back(list[0]);
2279  for (size_t i = 1; i < list.size(); i++)
2280  {
2281  if ((cs && lt_callsign(tmp.back(), list[i])) ||
2282  (!cs && lt_smart(tmp.back(), list[i])))
2283  {
2284  tmp.push_back(list[i]);
2285  }
2286  }
2287 
2288  list = tmp;
2289  }
2290 }
2291 
2292 // Return the array index of the best matching channel. An exact
2293 // match is the best match. Otherwise, find the closest numerical
2294 // value greater than channum. E.g., if the channel list is {2_1,
2295 // 2_2, 4_1, 4_2, 300} then input 3 returns 2_2, input 4 returns 2_2,
2296 // and input 5 returns 4_2.
2297 //
2298 // The list does not need to be sorted.
2300  const QString &channum)
2301 {
2302  ChannelInfo target;
2303  target.m_channum = channum;
2304  int b = -1; // index of best seen so far
2305  for (int i = 0; i < (int)list.size(); ++i)
2306  {
2307  // Index i is a better result if any of the following hold:
2308  // i is the first element seen
2309  // i < target < best (i.e., i is the first one less than the target)
2310  // best < i < target
2311  // target < i < best
2312  if ((b < 0) ||
2313  (lt_smart(list[i], target) && lt_smart(target, list[b])) ||
2314  (lt_smart(list[b], list[i]) && lt_smart(list[i], target)) ||
2315  (lt_smart(target, list[i]) && lt_smart(list[i], list[b])))
2316  {
2317  b = i;
2318  }
2319  }
2320  return b;
2321 }
2322 
2324  const ChannelInfoList &sorted,
2325  uint old_chanid,
2326  uint mplexid_restriction,
2327  uint chanid_restriction,
2328  ChannelChangeDirection direction,
2329  bool skip_non_visible,
2330  bool skip_same_channum_and_callsign)
2331 {
2332  ChannelInfoList::const_iterator it =
2333  find(sorted.begin(), sorted.end(), old_chanid);
2334 
2335  if (it == sorted.end())
2336  it = sorted.begin(); // not in list, pretend we are on first channel
2337 
2338  if (it == sorted.end())
2339  return 0; // no channels..
2340 
2341  ChannelInfoList::const_iterator start = it;
2342 
2343  if (CHANNEL_DIRECTION_DOWN == direction)
2344  {
2345  do
2346  {
2347  if (it == sorted.begin())
2348  {
2349  it = find(sorted.begin(), sorted.end(),
2350  sorted.rbegin()->m_chanid);
2351  if (it == sorted.end())
2352  {
2353  --it;
2354  }
2355  }
2356  else
2357  --it;
2358  }
2359  while ((it != start) &&
2360  ((skip_non_visible && !it->m_visible) ||
2361  (skip_same_channum_and_callsign &&
2362  it->m_channum == start->m_channum &&
2363  it->m_callsign == start->m_callsign) ||
2364  (mplexid_restriction &&
2365  (mplexid_restriction != it->m_mplexid)) ||
2366  (chanid_restriction &&
2367  (chanid_restriction != it->m_chanid))));
2368  }
2369  else if ((CHANNEL_DIRECTION_UP == direction) ||
2370  (CHANNEL_DIRECTION_FAVORITE == direction))
2371  {
2372  do
2373  {
2374  ++it;
2375  if (it == sorted.end())
2376  it = sorted.begin();
2377  }
2378  while ((it != start) &&
2379  ((skip_non_visible && !it->m_visible) ||
2380  (skip_same_channum_and_callsign &&
2381  it->m_channum == start->m_channum &&
2382  it->m_callsign == start->m_callsign) ||
2383  (mplexid_restriction &&
2384  (mplexid_restriction != it->m_mplexid)) ||
2385  (chanid_restriction &&
2386  (chanid_restriction != it->m_chanid))));
2387  }
2388 
2389  return it->m_chanid;
2390 }
2391 
2393  uint &totalAvailable,
2394  bool ignoreHidden,
2395  ChannelUtil::OrderBy orderBy,
2396  ChannelUtil::GroupBy groupBy,
2397  uint sourceID,
2398  uint channelGroupID,
2399  bool liveTVOnly,
2400  const QString& callsign,
2401  const QString& channum)
2402 {
2403  ChannelInfoList channelList;
2404 
2405  MSqlQuery query(MSqlQuery::InitCon());
2406  QString sql = "SELECT %1 channum, freqid, channel.sourceid, "
2407  "callsign, name, icon, finetune, videofilters, xmltvid, "
2408  "channel.recpriority, channel.contrast, channel.brightness, "
2409  "channel.colour, channel.hue, tvformat, "
2410  "visible, outputfilters, useonairguide, mplexid, "
2411  "serviceid, atsc_major_chan, atsc_minor_chan, last_record, "
2412  "default_authority, commmethod, tmoffset, iptvid, "
2413  "channel.chanid, "
2414  "GROUP_CONCAT(DISTINCT channelgroup.grpid "
2415  " ORDER BY channelgroup.grpid), " // Creates a CSV list of channel groupids for this channel
2416  "GROUP_CONCAT(DISTINCT capturecard.cardid "
2417  " ORDER BY livetvorder), " // Creates a CSV list of inputids for this channel
2418  "MIN(livetvorder) livetvorder "
2419  "FROM channel "
2420  "LEFT JOIN channelgroup ON channel.chanid = channelgroup.chanid "
2421  "INNER JOIN capturecard ON capturecard.sourceid = channel.sourceid ";
2422 
2423  QStringList cond;
2424  if (ignoreHidden)
2425  cond << "channel.visible = :VISIBLE ";
2426 
2427  if (channelGroupID > 0)
2428  cond << "channelgroup.grpid = :CHANGROUPID ";
2429 
2430  if (sourceID > 0)
2431  cond << "channel.sourceid = :SOURCEID ";
2432 
2433  if (liveTVOnly)
2434  cond << "capturecard.livetvorder > 0 ";
2435 
2436  if (!cond.isEmpty())
2437  sql += QString("WHERE %1").arg(cond.join("AND "));
2438 
2439  if (groupBy == kChanGroupByCallsign)
2440  sql += "GROUP BY channel.callsign ";
2441  else
2442  sql += "GROUP BY channel.chanid "; // We must always group for this query
2443 
2444  if (orderBy == kChanOrderByName)
2445  sql += "ORDER BY channel.name ";
2446  else if (orderBy == kChanOrderByChanNum)
2447  {
2448  // Natural sorting including subchannels e.g. 2_4, 1.3
2449  sql += "ORDER BY LPAD(CAST(channel.channum AS UNSIGNED), 10, 0), "
2450  " LPAD(channel.channum, 10, 0) ";
2451  }
2452  else // kChanOrderByLiveTV
2453  {
2454  sql += "ORDER BY callsign = :CALLSIGN1 AND channum = :CHANNUM DESC, "
2455  " callsign = :CALLSIGN2 DESC, "
2456  " livetvorder, "
2457  " channel.recpriority DESC, "
2458  " chanid ";
2459  }
2460 
2461  if (count > 0)
2462  sql += "LIMIT :LIMIT ";
2463 
2464  if (startIndex > 0)
2465  sql += "OFFSET :STARTINDEX ";
2466 
2467 
2468  if (startIndex > 0 || count > 0)
2469  sql = sql.arg("SQL_CALC_FOUND_ROWS");
2470  else
2471  sql = sql.arg(""); // remove place holder
2472 
2473  query.prepare(sql);
2474 
2475  if (ignoreHidden)
2476  query.bindValue(":VISIBLE", 1);
2477 
2478  if (channelGroupID > 0)
2479  query.bindValue(":CHANGROUPID", channelGroupID);
2480 
2481  if (sourceID > 0)
2482  query.bindValue(":SOURCEID", sourceID);
2483 
2484  if (count > 0)
2485  query.bindValue(":LIMIT", count);
2486 
2487  if (startIndex > 0)
2488  query.bindValue(":STARTINDEX", startIndex);
2489 
2490  if (orderBy == kChanOrderByLiveTV)
2491  {
2492  query.bindValue(":CALLSIGN1", callsign);
2493  query.bindValue(":CHANNUM", channum);
2494  query.bindValue(":CALLSIGN2", callsign);
2495  }
2496 
2497  if (!query.exec())
2498  {
2499  MythDB::DBError("ChannelInfo::Load()", query);
2500  return channelList;
2501  }
2502 
2503  while (query.next())
2504  {
2505  ChannelInfo channelInfo;
2506  channelInfo.m_channum = query.value(0).toString();
2507  channelInfo.m_freqid = query.value(1).toString();
2508  channelInfo.m_sourceid = query.value(2).toUInt();
2509  channelInfo.m_callsign = query.value(3).toString();
2510  channelInfo.m_name = query.value(4).toString();
2511  channelInfo.m_icon = query.value(5).toString();
2512  channelInfo.m_finetune = query.value(6).toInt();
2513  channelInfo.m_videofilters = query.value(7).toString();
2514  channelInfo.m_xmltvid = query.value(8).toString();
2515  channelInfo.m_recpriority = query.value(9).toInt();
2516  channelInfo.m_contrast = query.value(10).toUInt();
2517  channelInfo.m_brightness = query.value(11).toUInt();
2518  channelInfo.m_colour = query.value(12).toUInt();
2519  channelInfo.m_hue = query.value(13).toUInt();
2520  channelInfo.m_tvformat = query.value(14).toString();
2521  channelInfo.m_visible = query.value(15).toBool();
2522  channelInfo.m_outputfilters = query.value(16).toString();
2523  channelInfo.m_useonairguide = query.value(17).toBool();
2524  channelInfo.m_mplexid = query.value(18).toUInt();
2525  channelInfo.m_serviceid = query.value(19).toUInt();
2526  channelInfo.m_atsc_major_chan = query.value(20).toUInt();
2527  channelInfo.m_atsc_minor_chan = query.value(21).toUInt();
2528  channelInfo.m_last_record = query.value(22).toDateTime();
2529  channelInfo.m_default_authority = query.value(23).toString();
2530  channelInfo.m_commmethod = query.value(24).toUInt();
2531  channelInfo.m_tmoffset = query.value(25).toUInt();
2532  channelInfo.m_iptvid = query.value(26).toUInt();
2533  channelInfo.m_chanid = query.value(27).toUInt();
2534 
2535  QStringList groupIDs = query.value(28).toString().split(",");
2536  QString groupid;
2537  while (!groupIDs.isEmpty())
2538  channelInfo.AddGroupId(groupIDs.takeFirst().toUInt());
2539 
2540  QStringList inputIDs = query.value(29).toString().split(",");
2541  QString inputid;
2542  while (!inputIDs.isEmpty())
2543  channelInfo.AddInputId(inputIDs.takeFirst().toUInt());
2544 
2545  channelList.push_back(channelInfo);
2546  }
2547 
2548  if ((startIndex > 0 || count > 0) &&
2549  query.exec("SELECT FOUND_ROWS()") && query.next())
2550  totalAvailable = query.value(0).toUInt();
2551  else
2552  totalAvailable = query.size();
2553 
2554  return channelList;
2555 }
2556 
2557 /* vim: set expandtab tabstop=4 shiftwidth=4: */
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:782
static bool SetVisible(uint channel_id, bool visible)
static MSqlQueryInfo ChannelCon()
Returns dedicated connection. (Required for using temporary SQL tables.)
Definition: mythdbcon.cpp:584
unsigned long long FrequencyHz(void) const
uint64_t m_symbolrate
Definition: dtvmultiplex.h:95
DTVGuardInterval m_guard_interval
Definition: dtvmultiplex.h:102
static uint get_dtv_multiplex(uint db_source_id, const QString &sistandard, uint64_t frequency, uint transport_id, uint network_id, signed char polarity)
Definition: channelutil.cpp:25
static bool CreateChannel(uint db_mplexid, uint db_sourceid, uint new_channel_id, const QString &callsign, const QString &service_name, const QString &chan_num, uint service_id, uint atsc_major_channel, uint atsc_minor_channel, bool use_on_air_guide, bool hidden, bool hidden_in_guide, const QString &freqid, QString icon=QString(), QString format="Default", QString xmltvid=QString(), QString default_authority=QString())
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:863
QString m_name
Definition: channelinfo.h:82
static bool GetTuningParams(uint mplexid, QString &modulation, uint64_t &frequency, uint &dvb_transportid, uint &dvb_networkid, QString &si_std)
ChannelChangeDirection
ChannelChangeDirection is an enumeration of possible channel changing directions.
Definition: tv.h:28
int m_recpriority
Definition: channelinfo.h:88
static uint insert_dtv_multiplex(int db_source_id, const QString &sistandard, uint64_t frequency, const QString &modulation, int transport_id, int network_id, int symbol_rate, signed char bandwidth, signed char polarity, signed char inversion, signed char trans_mode, const QString &inner_FEC, const QString &constellation, signed char hierarchy, const QString &hp_code_rate, const QString &lp_code_rate, const QString &guard_interval, const QString &mod_sys, const QString &rolloff)
Definition: channelutil.cpp:78
DTVBandwidth m_bandwidth
Definition: dtvmultiplex.h:97
static QString GetChannelStringField(int chan_id, const QString &field)
DTVHierarchy m_hierarchy
Definition: dtvmultiplex.h:103
static ChannelInfoList LoadChannels(uint startIndex, uint count, uint &totalAvailable, bool ignoreHidden=true, OrderBy orderBy=kChanOrderByChanNum, GroupBy groupBy=kChanGroupByChanid, uint sourceID=0, uint channelGroupID=0, bool liveTVOnly=false, const QString &callsign="", const QString &channum="")
Load channels from database into a list of ChannelInfo objects.
static bool UpdateIPTVTuningData(uint channel_id, const IPTVTuningData &tuning)
DTVPolarity m_polarity
Definition: dtvmultiplex.h:104
static vector< uint > GetConflicting(const QString &channum, uint sourceid=0)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
static bool GetCachedPids(uint chanid, pid_cache_t &pid_cache)
Returns cached MPEG PIDs when given a Channel ID.
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
QString m_videofilters
Definition: channelinfo.h:86
QString m_outputfilters
Definition: channelinfo.h:97
vector< pid_cache_item_t > pid_cache_t
Definition: channelutil.h:43
QString FECInnerString(void) const
int size(void) const
Definition: mythdbcon.h:203
uint DescriptorTag(void) const
static int GetBetterMplexID(int current_mplexid, int transport_id, int network_id)
Returns best match multiplex ID, creating one if needed.
void AddInputId(uint linputid)
Definition: channelinfo.h:61
unsigned int uint
Definition: compat.h:140
DTVInversion m_inversion
Definition: dtvmultiplex.h:96
uint32_t freq[4]
Definition: element.c:44
vector< const unsigned char * > desc_list_t
QString m_xmltvid
Definition: channelinfo.h:87
static QStringList GetInputTypes(uint chanid)
static void SortChannels(ChannelInfoList &list, const QString &order, bool eliminate_duplicates=false)
DTVCodeRate m_lp_code_rate
Low Priority FEC rate.
Definition: dtvmultiplex.h:99
static int GetSourceID(int mplexid)
static guint32 * tmp
Definition: goom_core.c:35
const unsigned char * TransportDescriptors(uint i) const
for(j=0;j<N;j++) x 6.0+p { descriptor() }
Definition: dvbtables.h:80
static uint GetMplexID(uint sourceid, const QString &channum)
static IPTVTuningData GetIPTVTuningData(uint chanid)
static bool lt_pidcache(const pid_cache_item_t &a, const pid_cache_item_t &b)
static bool SetChannelValue(const QString &field_name, const QString &value, uint sourceid, const QString &channum)
QString m_tvformat
Definition: channelinfo.h:95
static bool GetATSCChannel(uint sourceid, const QString &channum, uint &major, uint &minor)
DTVCodeRate m_fec
Inner Forward Error Correction rate.
Definition: dtvmultiplex.h:105
QString m_icon
Definition: channelinfo.h:83
unsigned char b
Definition: ParseText.cpp:329
QChar toChar() const
QVariant value(int i) const
Definition: mythdbcon.h:198
QUrl GetFECURL0(void) const
QString GetFECTypeString(uint i) const
static uint get_max_chanid(uint sourceid)
DTVCodeRate m_hp_code_rate
High Priority FEC rate.
Definition: dtvmultiplex.h:98
DTVModulation m_modulation
Definition: dtvmultiplex.h:100
uint m_serviceid
Definition: channelinfo.h:101
QString toString() const
void AddGroupId(uint lgroupid)
Definition: channelinfo.h:49
static bool chanid_available(uint chanid)
static QString GetUnknownCallsign(void)
static int GetChanID(int db_mplexid, int service_transport_id, int major_channel, int minor_channel, int program_number)
uint m_atsc_major_chan
Definition: channelinfo.h:102
uint GetBitrate(uint i) const
static uint FindChannel(uint sourceid, const QString &freqid)
static ChannelInfoList GetChannelsInternal(uint sourceid, bool visible_only, bool include_disconnected, const QString &group_by, uint channel_groupid)
QString m_freqid
Definition: channelinfo.h:77
uint m_sourceid
Definition: channelinfo.h:79
int m_finetune
Definition: channelinfo.h:85
#define minor(X)
Definition: compat.h:138
uint m_mplexid
Definition: channelinfo.h:100
QString toString() const
bool isActive(void) const
Definition: mythdbcon.h:204
bool lt_smart(const ChannelInfo &a, const ChannelInfo &b)
int m_commmethod
Definition: channelinfo.h:108
QUrl GetFECURL1(void) const
static QString GetIcon(uint chanid)
QString ModulationString(void) const
QString toString() const
QString ConstellationString(void) const
static int GetChannelValueInt(const QString &channel_field, uint sourceid, const QString &channum)
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
QChar toChar() const
QChar toChar() const
static int CreateChanID(uint sourceid, const QString &chan_num)
Creates a unique channel ID for database use.
static int GetServiceVersion(int mplexid)
QString GetDeviceName(void) const
QDateTime m_last_record
Definition: channelinfo.h:105
static bool UpdateChannel(uint db_mplexid, uint source_id, uint channel_id, const QString &callsign, const QString &service_name, const QString &chan_num, uint service_id, uint atsc_major_channel, uint atsc_minor_channel, bool use_on_air_guide, bool hidden, bool hidden_in_guide, const QString &freqid=QString(), const QString &icon=QString(), QString format=QString(), const QString &xmltvid=QString(), const QString &default_authority=QString())
QString m_channum
Definition: channelinfo.h:76
uint m_contrast
Definition: channelinfo.h:90
QString m_sistandard
Definition: dtvmultiplex.h:111
vector< ChannelInfo > ChannelInfoList
Definition: channelinfo.h:120
QString toString() const
static void UpdateInsertInfoFromDB(ChannelInsertInfo &chan)
static QStringList get_valid_recorder_list(uint chanid)
Returns list of the recorders that have chanid in their sources.
bool lt_callsign(const ChannelInfo &a, const ChannelInfo &b)
static QString GetChannelValueStr(const QString &channel_field, uint sourceid, const QString &channum)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:807
DTVRollOff m_rolloff
Definition: dtvmultiplex.h:107
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static QStringList GetValidRecorderList(uint chanid, const QString &channum)
Returns list of the recorders that have chanid or channum in their sources.
static uint GetSourceIDForChannel(uint chanid)
static bool GetChannelData(uint sourceid, uint &chanid, const QString &channum, QString &tvformat, QString &modulation, QString &freqtable, QString &freqid, int &finetune, uint64_t &frequency, QString &dtv_si_std, int &mpeg_prog_num, uint &atsc_major, uint &atsc_minor, uint &dvb_transportid, uint &dvb_networkid, uint &mplexid, bool &commfree)
static bool SaveCachedPids(uint chanid, const pid_cache_t &_pid_cache, bool delete_all=false)
Saves PIDs for PSIP tables to database.
uint OriginalNetworkID(uint i) const
original_network_id 16 2.0+p
Definition: dvbtables.h:72
QString TransmissionModeString(void) const
uint m_atsc_minor_chan
Definition: channelinfo.h:103
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
Definition: mythdbcon.cpp:868
static int GetNearestChannel(const ChannelInfoList &list, const QString &channum)
uint TransportStreamCount(void) const
Definition: dvbtables.h:66
static vector< uint > GetChanIDs(int sourceid=-1, bool onlyVisible=false)
static QString GetDefaultAuthority(uint chanid)
Returns the DVB default authority for the chanid given.
QString FECInnerString(void) const
unsigned long long FrequencyHz(void) const
static bool IsOnSameMultiplex(uint srcid, const QString &new_channum, const QString &old_channum)
bool m_visible
Definition: channelinfo.h:96
static void UpdateChannelNumberFromDB(ChannelInsertInfo &chan)
uint TSID(uint i) const
transport_stream_id 16 0.0+p
Definition: dvbtables.h:70
QString ModulationSystemString(void) const
QString m_callsign
Definition: channelinfo.h:81
static uint CreateMultiplex(int sourceid, QString sistandard, uint64_t frequency, QString modulation, int transport_id=-1, int network_id=-1)
static bool SetServiceVersion(int mplexid, int version)
uint TransportDescriptorsLength(uint i) const
trans_desc_length 12 4.4+p
Definition: dvbtables.h:76
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
static void handle_transport_desc(vector< uint > &muxes, const MPEGDescriptor &desc, uint sourceid, uint tsid, uint netid)
bool m_useonairguide
Definition: channelinfo.h:98
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
uint m_colour
Definition: channelinfo.h:92
QString m_default_authority
Definition: channelinfo.h:107
DTVModulationSystem m_mod_sys
Modulation system.
Definition: dtvmultiplex.h:106
This table tells the decoder on which PIDs to find other tables.
Definition: dvbtables.h:21
QString ModulationString(void) const
int naturalCompare(const QString &_a, const QString &_b, Qt::CaseSensitivity caseSensitivity)
static int GetTimeOffset(int chan_id)
Returns the listings time offset in minutes for given channel.
static const QString kATSCSeparators
Definition: channelutil.h:324
static uint GetChannelCount(int sourceid=-1)
static desc_list_t Parse(const unsigned char *data, uint len)
static QString GetChanNum(int chan_id)
Returns the channel-number string of the given channel.
QChar toChar() const
static vector< uint > CreateMultiplexes(int sourceid, const NetworkInformationTable *nit)
QUrl GetDataURL(void) const
uint m_chanid
Definition: channelinfo.h:75
uint m_brightness
Definition: channelinfo.h:91
uint GetPID(void) const
Definition: channelutil.h:30
static bool DeleteChannel(uint channel_id)
uint64_t m_frequency
Definition: dtvmultiplex.h:94
DTVTransmitMode m_trans_mode
Definition: dtvmultiplex.h:101
static uint GetNextChannel(const ChannelInfoList &sorted, uint old_chanid, uint mplexid_restriction, uint chanid_restriction, ChannelChangeDirection direction, bool skip_non_visible=true, bool skip_same_channum_and_callsign=false)