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