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