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