00001
00002 #include <fcntl.h>
00003 #include <unistd.h>
00004
00005 #include <algorithm>
00006
00007 #if defined(USING_V4L2) || defined(USING_DVB)
00008 #include <sys/ioctl.h>
00009 #endif
00010
00011
00012 #include <QMap>
00013 #include <QDir>
00014
00015
00016 #include "mythconfig.h"
00017 #include "cardutil.h"
00018 #include "videosource.h"
00019 #include "dvbchannel.h"
00020 #include "diseqcsettings.h"
00021 #include "sourceutil.h"
00022 #include "mythdb.h"
00023 #include "mythlogging.h"
00024
00025 #ifdef USING_DVB
00026 #include "dvbtypes.h"
00027 #endif
00028
00029 #ifdef USING_V4L1
00030 #include <linux/videodev.h>
00031 #endif
00032
00033 #ifdef USING_V4L2
00034 #include <linux/videodev2.h>
00035 #endif
00036
00037 #ifdef USING_HDHOMERUN
00038 #include "hdhomerun.h"
00039 #endif
00040
00041 #ifdef USING_ASI
00042 #include <dveo/asi.h>
00043 #include <dveo/master.h>
00044 #endif
00045
00046 #define LOC QString("CardUtil: ")
00047
00048 QString CardUtil::GetScanableCardTypes(void)
00049 {
00050 QString cardTypes = "";
00051
00052 #ifdef USING_DVB
00053 cardTypes += "'DVB'";
00054 #endif // USING_DVB
00055
00056 #ifdef USING_V4L2
00057 if (!cardTypes.isEmpty())
00058 cardTypes += ",";
00059 cardTypes += "'V4L'";
00060 # ifdef USING_IVTV
00061 cardTypes += ",'MPEG'";
00062 # endif // USING_IVTV
00063 #endif // USING_V4L2
00064
00065 #ifdef USING_IPTV
00066 if (!cardTypes.isEmpty())
00067 cardTypes += ",";
00068 cardTypes += "'FREEBOX'";
00069 #endif // USING_IPTV
00070
00071 #ifdef USING_HDHOMERUN
00072 if (!cardTypes.isEmpty())
00073 cardTypes += ",";
00074 cardTypes += "'HDHOMERUN'";
00075 #endif // USING_HDHOMERUN
00076
00077 #ifdef USING_ASI
00078 if (!cardTypes.isEmpty())
00079 cardTypes += ",";
00080 cardTypes += "'ASI'";
00081 #endif
00082
00083 #ifdef USING_CETON
00084 if (!cardTypes.isEmpty())
00085 cardTypes += ",";
00086 cardTypes += "'CETON'";
00087 #endif // USING_CETON
00088
00089 if (cardTypes.isEmpty())
00090 cardTypes = "'DUMMY'";
00091
00092 return QString("(%1)").arg(cardTypes);
00093 }
00094
00095 bool CardUtil::IsCableCardPresent(uint cardid,
00096 const QString &cardType)
00097 {
00098 if (cardType == "HDHOMERUN")
00099 {
00100 #ifdef USING_HDHOMERUN
00101 hdhomerun_device_t *hdhr;
00102 hdhomerun_tuner_status_t status;
00103 QString device = GetVideoDevice(cardid);
00104 hdhr = hdhomerun_device_create_from_str(device.toAscii(), NULL);
00105 if (!hdhr)
00106 return false;
00107
00108 int oob = -1;
00109 oob = hdhomerun_device_get_oob_status(hdhr, NULL, &status);
00110
00111
00112
00113 if (oob > 0 && (strncmp(status.channel, "none", 4) != 0))
00114 {
00115 LOG(VB_GENERAL, LOG_INFO, "Cardutil: HDHomeRun Cablecard Present.");
00116 return true;
00117 }
00118 else
00119 #endif
00120 return false;
00121 }
00122 else if (cardType == "CETON")
00123 {
00124 #ifdef USING_CETON
00125
00126 LOG(VB_GENERAL, LOG_INFO, "Cardutil: TODO Ceton Is Cablecard Present?");
00127 return true;
00128 #else
00129 return false;
00130 #endif
00131 }
00132 else
00133 return false;
00134 }
00135
00136 bool CardUtil::IsTunerShared(uint cardidA, uint cardidB)
00137 {
00138 LOG(VB_GENERAL, LOG_DEBUG, QString("IsTunerShared(%1,%2)")
00139 .arg(cardidA).arg(cardidB));
00140
00141 MSqlQuery query(MSqlQuery::InitCon());
00142 query.prepare("SELECT videodevice, hostname, cardtype "
00143 "FROM capturecard "
00144 "WHERE ( (cardid = :CARDID_A) OR "
00145 " (cardid = :CARDID_B) )");
00146 query.bindValue(":CARDID_A", cardidA);
00147 query.bindValue(":CARDID_B", cardidB);
00148
00149 if (!query.exec())
00150 {
00151 MythDB::DBError("CardUtil::is_tuner_shared", query);
00152 return false;
00153 }
00154
00155 if (!query.next())
00156 return false;
00157
00158 const QString vdevice = query.value(0).toString();
00159 const QString hostname = query.value(1).toString();
00160 const QString cardtype = query.value(2).toString();
00161
00162 if (!IsTunerSharingCapable(cardtype.toUpper()))
00163 return false;
00164
00165 if (!query.next())
00166 return false;
00167
00168 bool ret = ((vdevice == query.value(0).toString()) &&
00169 (hostname == query.value(1).toString()) &&
00170 (cardtype == query.value(2).toString()));
00171
00172 LOG(VB_RECORD, LOG_DEBUG, QString("IsTunerShared(%1,%2) -> %3")
00173 .arg(cardidA).arg(cardidB).arg(ret));
00174
00175 return ret;
00176 }
00177
00183 bool CardUtil::IsCardTypePresent(const QString &rawtype, QString hostname)
00184 {
00185 if (hostname.isEmpty())
00186 hostname = gCoreContext->GetHostName();
00187
00188 MSqlQuery query(MSqlQuery::InitCon());
00189 QString qstr =
00190 "SELECT count(cardtype) "
00191 "FROM capturecard, cardinput "
00192 "WHERE cardinput.cardid = capturecard.cardid AND "
00193 " capturecard.hostname = :HOSTNAME";
00194
00195 if (!rawtype.isEmpty())
00196 qstr += " AND capturecard.cardtype = :CARDTYPE";
00197
00198 query.prepare(qstr);
00199
00200 if (!rawtype.isEmpty())
00201 query.bindValue(":CARDTYPE", rawtype.toUpper());
00202
00203 query.bindValue(":HOSTNAME", hostname);
00204
00205 if (!query.exec())
00206 {
00207 MythDB::DBError("CardUtil::IsCardTypePresent", query);
00208 return false;
00209 }
00210
00211 uint count = 0;
00212 if (query.next())
00213 count = query.value(0).toUInt();
00214
00215 return count > 0;
00216 }
00217
00218 QStringList CardUtil::GetCardTypes(void)
00219 {
00220 QStringList cardtypes;
00221
00222 MSqlQuery query(MSqlQuery::InitCon());
00223 query.prepare("SELECT DISTINCT cardtype "
00224 "FROM capturecard");
00225
00226 if (!query.exec())
00227 {
00228 MythDB::DBError("CardUtil::GetCardTypes()", query);
00229 }
00230 else
00231 {
00232 while (query.next())
00233 cardtypes.push_back(query.value(0).toString());
00234 }
00235
00236 return cardtypes;
00237 }
00238
00244 QStringList CardUtil::GetVideoDevices(const QString &rawtype, QString hostname)
00245 {
00246 QStringList list;
00247
00248 if (hostname.isEmpty())
00249 hostname = gCoreContext->GetHostName();
00250
00251 MSqlQuery query(MSqlQuery::InitCon());
00252 QString qstr =
00253 "SELECT videodevice "
00254 "FROM capturecard "
00255 "WHERE hostname = :HOSTNAME";
00256
00257 if (!rawtype.isEmpty())
00258 qstr += " AND cardtype = :CARDTYPE";
00259
00260 query.prepare(qstr);
00261
00262 if (!rawtype.isEmpty())
00263 query.bindValue(":CARDTYPE", rawtype.toUpper());
00264
00265 query.bindValue(":HOSTNAME", hostname);
00266
00267 if (!query.exec())
00268 {
00269 MythDB::DBError("CardUtil::GetVideoDevices", query);
00270 return list;
00271 }
00272
00273 QMap<QString,bool> dup;
00274 while (query.next())
00275 {
00276 QString videodevice = query.value(0).toString();
00277 if (dup[videodevice])
00278 continue;
00279
00280 list.push_back(videodevice);
00281 dup[videodevice] = true;
00282 }
00283
00284 return list;
00285 }
00286
00287 QStringList CardUtil::ProbeVideoDevices(const QString &rawtype)
00288 {
00289 QStringList devs;
00290
00291 if (rawtype.toUpper() == "DVB")
00292 {
00293 QDir dir("/dev/dvb", "adapter*", QDir::Name, QDir::Dirs);
00294 const QFileInfoList il = dir.entryInfoList();
00295 if (il.isEmpty())
00296 return devs;
00297
00298 QFileInfoList::const_iterator it = il.begin();
00299
00300 for (; it != il.end(); ++it)
00301 {
00302 QDir subdir(it->filePath(), "frontend*", QDir::Name, QDir::Files | QDir::System);
00303 const QFileInfoList subil = subdir.entryInfoList();
00304 if (subil.isEmpty())
00305 continue;
00306
00307 QFileInfoList::const_iterator subit = subil.begin();
00308 for (; subit != subil.end(); ++subit)
00309 devs.push_back(subit->filePath());
00310 }
00311 }
00312 else if (rawtype.toUpper() == "ASI")
00313 {
00314 QDir dir("/dev/", "asirx*", QDir::Name, QDir::System);
00315 const QFileInfoList il = dir.entryInfoList();
00316 if (il.isEmpty())
00317 return devs;
00318
00319 QFileInfoList::const_iterator it = il.begin();
00320 for (; it != il.end(); ++it)
00321 {
00322 if (GetASIDeviceNumber(it->filePath()) >= 0)
00323 {
00324 devs.push_back(it->filePath());
00325 continue;
00326 }
00327 break;
00328 }
00329 }
00330 #ifdef USING_HDHOMERUN
00331 else if (rawtype.toUpper() == "HDHOMERUN")
00332 {
00333 uint32_t target_ip = 0;
00334 uint32_t device_type = HDHOMERUN_DEVICE_TYPE_TUNER;
00335 uint32_t device_id = HDHOMERUN_DEVICE_ID_WILDCARD;
00336 const int max_count = 50;
00337 hdhomerun_discover_device_t result_list[max_count];
00338
00339 int result = hdhomerun_discover_find_devices_custom(
00340 target_ip, device_type, device_id, result_list, max_count);
00341
00342 if (result == -1)
00343 {
00344 LOG(VB_GENERAL, LOG_ERR, "Error finding HDHomerun devices");
00345 return devs;
00346 }
00347
00348 if (result >= max_count)
00349 {
00350 LOG(VB_GENERAL, LOG_WARNING,
00351 "Warning: may be > 50 HDHomerun devices");
00352 }
00353
00354
00355 for (int i = 0; i < result; i++)
00356 {
00357 QString id = QString("%1").arg(result_list[i].device_id, 0, 16);
00358 QString ip = QString("%1.%2.%3.%4")
00359 .arg((result_list[i].ip_addr>>24) & 0xFF)
00360 .arg((result_list[i].ip_addr>>16) & 0xFF)
00361 .arg((result_list[i].ip_addr>> 8) & 0xFF)
00362 .arg((result_list[i].ip_addr>> 0) & 0xFF);
00363
00364 for (int tuner = 0; tuner < result_list[i].tuner_count; tuner++)
00365 {
00366 QString hdhrdev = id.toUpper() + " " + ip + " " +
00367 QString("%1").arg(tuner);
00368 devs.push_back(hdhrdev);
00369 }
00370 }
00371 }
00372 #endif // USING_HDHOMERUN
00373 #ifdef USING_CETON
00374 else if (rawtype.toUpper() == "CETON")
00375 {
00376
00377 LOG(VB_GENERAL, LOG_INFO, "CardUtil::ProbeVideoDevices: "
00378 "TODO Probe Ceton devices");
00379 }
00380 #endif // USING_CETON
00381 else
00382 {
00383 LOG(VB_GENERAL, LOG_ERR, QString("Raw Type: '%1' is not supported")
00384 .arg(rawtype));
00385 }
00386
00387 return devs;
00388 }
00389
00390 QString CardUtil::ProbeDVBType(const QString &device)
00391 {
00392 QString ret = "ERROR_UNKNOWN";
00393
00394 if (device.isEmpty())
00395 return ret;
00396
00397 #ifdef USING_DVB
00398 QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
00399 QByteArray dev = dvbdev.toAscii();
00400 int fd_frontend = open(dev.constData(), O_RDONLY | O_NONBLOCK);
00401 if (fd_frontend < 0)
00402 {
00403 LOG(VB_GENERAL, LOG_ERR, QString("Can't open DVB frontend (%1) for %2.")
00404 .arg(dvbdev).arg(device));
00405 return ret;
00406 }
00407
00408 struct dvb_frontend_info info;
00409 int err = ioctl(fd_frontend, FE_GET_INFO, &info);
00410 if (err < 0)
00411 {
00412 close(fd_frontend);
00413 LOG(VB_GENERAL, LOG_ERR, QString("FE_GET_INFO ioctl failed (%1)")
00414 .arg(dvbdev) + ENO);
00415 return ret;
00416 }
00417 close(fd_frontend);
00418
00419 DTVTunerType type(info.type);
00420 #if HAVE_FE_CAN_2G_MODULATION
00421 if (type == DTVTunerType::kTunerTypeDVBS1 &&
00422 (info.caps & FE_CAN_2G_MODULATION))
00423 type = DTVTunerType::kTunerTypeDVBS2;
00424 #endif // HAVE_FE_CAN_2G_MODULATION
00425 ret = (type.toString() != "UNKNOWN") ? type.toString().toUpper() : ret;
00426 #endif // USING_DVB
00427
00428 return ret;
00429 }
00430
00434 QString CardUtil::ProbeDVBFrontendName(const QString &device)
00435 {
00436 QString ret = "ERROR_UNKNOWN";
00437 (void) device;
00438
00439 #ifdef USING_DVB
00440 QString dvbdev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, device);
00441 QByteArray dev = dvbdev.toAscii();
00442 int fd_frontend = open(dev.constData(), O_RDWR | O_NONBLOCK);
00443 if (fd_frontend < 0)
00444 return "ERROR_OPEN";
00445
00446 struct dvb_frontend_info info;
00447 int err = ioctl(fd_frontend, FE_GET_INFO, &info);
00448 if (err < 0)
00449 {
00450 close(fd_frontend);
00451 return "ERROR_PROBE";
00452 }
00453
00454 ret = info.name;
00455
00456 close(fd_frontend);
00457 #endif // USING_DVB
00458
00459 return ret;
00460 }
00461
00479 bool CardUtil::HasDVBCRCBug(const QString &device)
00480 {
00481 QString name = ProbeDVBFrontendName(device);
00482 return ((name == "VLSI VES1x93 DVB-S") ||
00483 (name == "ST STV0299 DVB-S"));
00484 }
00485
00486 uint CardUtil::GetMinSignalMonitoringDelay(const QString &device)
00487 {
00488 QString name = ProbeDVBFrontendName(device);
00489 if (name.indexOf("DVB-S") >= 0)
00490 return 300;
00491 if (name == "DiBcom 3000P/M-C DVB-T")
00492 return 100;
00493 return 25;
00494 }
00495
00496 QString CardUtil::ProbeSubTypeName(uint cardid)
00497 {
00498 QString type = GetRawCardType(cardid);
00499 if ("DVB" != type)
00500 return type;
00501
00502 QString device = GetVideoDevice(cardid);
00503
00504 if (device.isEmpty())
00505 return "ERROR_OPEN";
00506
00507 return ProbeDVBType(device);
00508 }
00509
00511 bool CardUtil::IsDVBCardType(const QString &card_type)
00512 {
00513 QString ct = card_type.toUpper();
00514 return (ct == "DVB") || (ct == "QAM") || (ct == "QPSK") ||
00515 (ct == "OFDM") || (ct == "ATSC") || (ct == "DVB_S2");
00516 }
00517
00518 QString get_on_cardid(const QString &to_get, uint cardid)
00519 {
00520 MSqlQuery query(MSqlQuery::InitCon());
00521 query.prepare(
00522 QString("SELECT %1 ").arg(to_get) +
00523 "FROM capturecard "
00524 "WHERE capturecard.cardid = :CARDID");
00525 query.bindValue(":CARDID", cardid);
00526
00527 if (!query.exec())
00528 MythDB::DBError("CardUtil::get_on_source", query);
00529 else if (query.next())
00530 return query.value(0).toString();
00531
00532 return QString::null;
00533 }
00534
00535 bool set_on_source(const QString &to_set, uint cardid, uint sourceid,
00536 const QString value)
00537 {
00538 QString tmp = get_on_cardid("capturecard.cardid", cardid);
00539 if (tmp.isEmpty())
00540 return false;
00541
00542 bool ok;
00543 uint input_cardid = tmp.toUInt(&ok);
00544 if (!ok)
00545 return false;
00546
00547 MSqlQuery query(MSqlQuery::InitCon());
00548 query.prepare(
00549 QString("UPDATE capturecard SET %1 = :VALUE ").arg(to_set) +
00550 "WHERE cardid = :CARDID");
00551 query.bindValue(":CARDID", input_cardid);
00552 query.bindValue(":VALUE", value);
00553
00554 if (query.exec())
00555 return true;
00556
00557 MythDB::DBError("CardUtil::set_on_source", query);
00558 return false;
00559 }
00560
00561 QString get_on_inputid(const QString &to_get, uint inputid)
00562 {
00563 MSqlQuery query(MSqlQuery::InitCon());
00564 query.prepare(
00565 QString("SELECT %1 ").arg(to_get) +
00566 "FROM cardinput "
00567 "WHERE cardinput.cardinputid = :INPUTID");
00568 query.bindValue(":INPUTID", inputid);
00569
00570 if (!query.exec())
00571 MythDB::DBError("CardUtil::get_on_inputid", query);
00572 else if (query.next())
00573 return query.value(0).toString();
00574
00575 return QString::null;
00576 }
00577
00578 bool set_on_input(const QString &to_set, uint inputid, const QString value)
00579 {
00580 QString tmp = get_on_inputid("cardinput.cardinputid", inputid);
00581 if (tmp.isEmpty())
00582 return false;
00583
00584 bool ok;
00585 uint input_cardinputid = tmp.toUInt(&ok);
00586 if (!ok)
00587 return false;
00588
00589 MSqlQuery query(MSqlQuery::InitCon());
00590 query.prepare(
00591 QString("UPDATE cardinput SET %1 = :VALUE ").arg(to_set) +
00592 "WHERE cardinputid = :INPUTID");
00593 query.bindValue(":INPUTID", input_cardinputid);
00594 query.bindValue(":VALUE", value);
00595
00596 if (query.exec())
00597 return true;
00598
00599 MythDB::DBError("CardUtil::set_on_input", query);
00600 return false;
00601 }
00602
00612 vector<uint> CardUtil::GetCardIDs(QString videodevice,
00613 QString rawtype,
00614 QString hostname)
00615 {
00616 vector<uint> list;
00617
00618 if (hostname.isEmpty())
00619 hostname = gCoreContext->GetHostName();
00620
00621 MSqlQuery query(MSqlQuery::InitCon());
00622 QString qstr =
00623 (videodevice.isEmpty()) ?
00624 "SELECT cardid "
00625 "FROM capturecard "
00626 "WHERE hostname = :HOSTNAME" :
00627
00628 "SELECT cardid "
00629 "FROM capturecard "
00630 "WHERE videodevice = :DEVICE AND "
00631 " hostname = :HOSTNAME";
00632
00633 if (!rawtype.isEmpty())
00634 qstr += " AND cardtype = :CARDTYPE";
00635
00636 qstr += " ORDER BY cardid";
00637
00638 query.prepare(qstr);
00639
00640 if (!videodevice.isEmpty())
00641 query.bindValue(":DEVICE", videodevice);
00642
00643 query.bindValue(":HOSTNAME", hostname);
00644
00645 if (!rawtype.isEmpty())
00646 query.bindValue(":CARDTYPE", rawtype.toUpper());
00647
00648 if (!query.exec())
00649 MythDB::DBError("CardUtil::GetCardIDs(videodevice...)", query);
00650 else
00651 {
00652 while (query.next())
00653 list.push_back(query.value(0).toUInt());
00654 }
00655
00656 return list;
00657 }
00658
00659 static uint clone_capturecard(uint src_cardid, uint orig_dst_cardid)
00660 {
00661 uint dst_cardid = orig_dst_cardid;
00662
00663 MSqlQuery query(MSqlQuery::InitCon());
00664 if (!dst_cardid)
00665 {
00666 query.prepare(
00667 "DELETE FROM capturecard "
00668 "WHERE videodevice = 'temp_dummy'");
00669
00670 if (!query.exec())
00671 {
00672 MythDB::DBError("clone_capturecard -- delete temp", query);
00673 return 0;
00674 }
00675
00676 query.prepare(
00677 "INSERT INTO capturecard "
00678 "SET videodevice = 'temp_dummy'");
00679
00680 if (!query.exec())
00681 {
00682 MythDB::DBError("clone_capturecard -- insert temp", query);
00683 return 0;
00684 }
00685
00686 query.prepare(
00687 "SELECT cardid "
00688 "FROM capturecard "
00689 "WHERE videodevice = 'temp_dummy'");
00690
00691 if (!query.exec())
00692 {
00693 MythDB::DBError("clone_capturecard -- get temp id", query);
00694 return 0;
00695 }
00696
00697 if (!query.next())
00698 {
00699 LOG(VB_GENERAL, LOG_ERR, "clone_capturecard -- get temp id");
00700 return 0;
00701 }
00702
00703 dst_cardid = query.value(0).toUInt();
00704 }
00705
00706 query.prepare(
00707 "SELECT videodevice, cardtype, "
00708 " hostname, signal_timeout, channel_timeout, "
00709 " dvb_wait_for_seqstart, dvb_on_demand, dvb_tuning_delay, "
00710 " dvb_diseqc_type, diseqcid, dvb_eitscan "
00711 "FROM capturecard "
00712 "WHERE cardid = :CARDID");
00713 query.bindValue(":CARDID", src_cardid);
00714
00715 if (!query.exec())
00716 {
00717 MythDB::DBError("clone_capturecard -- get data", query);
00718 return 0;
00719 }
00720 if (!query.next())
00721 {
00722 LOG(VB_GENERAL, LOG_ERR, "clone_cardinput -- get data 2");
00723 return 0;
00724 }
00725
00726 MSqlQuery query2(MSqlQuery::InitCon());
00727 query2.prepare(
00728 "UPDATE capturecard "
00729 "SET videodevice = :V0, "
00730 " cardtype = :V1, "
00731 " hostname = :V2, "
00732 " signal_timeout = :V3, "
00733 " channel_timeout = :V4, "
00734 " dvb_wait_for_seqstart = :V5, "
00735 " dvb_on_demand = :V6, "
00736 " dvb_tuning_delay = :V7, "
00737 " dvb_diseqc_type = :V8, "
00738 " diseqcid = :V9,"
00739 " dvb_eitscan = :V10 "
00740 "WHERE cardid = :CARDID");
00741 for (uint i = 0; i < 11; i++)
00742 query2.bindValue(QString(":V%1").arg(i), query.value(i).toString());
00743 query2.bindValue(":CARDID", dst_cardid);
00744
00745 if (!query2.exec())
00746 {
00747 MythDB::DBError("clone_capturecard -- save data", query2);
00748 if (!orig_dst_cardid)
00749 CardUtil::DeleteCard(dst_cardid);
00750 return 0;
00751 }
00752
00753 return dst_cardid;
00754 }
00755
00756 static bool clone_cardinputs(uint src_cardid, uint dst_cardid)
00757 {
00758 vector<uint> src_inputs = CardUtil::GetInputIDs(src_cardid);
00759 vector<uint> dst_inputs = CardUtil::GetInputIDs(dst_cardid);
00760 vector<QString> src_names;
00761 vector<QString> dst_names;
00762 QMap<uint,bool> dst_keep;
00763
00764 for (uint i = 0; i < src_inputs.size(); i++)
00765 src_names.push_back(CardUtil::GetInputName(src_inputs[i]));
00766
00767 for (uint i = 0; i < dst_inputs.size(); i++)
00768 dst_names.push_back(CardUtil::GetInputName(dst_inputs[i]));
00769
00770 bool ok = true;
00771
00772 MSqlQuery query(MSqlQuery::InitCon());
00773 MSqlQuery query2(MSqlQuery::InitCon());
00774
00775 for (uint i = 0; i < src_inputs.size(); i++)
00776 {
00777 query.prepare(
00778 "SELECT sourceid, inputname, externalcommand, "
00779 " tunechan, startchan, displayname, "
00780 " dishnet_eit, recpriority, quicktune, "
00781 " schedorder, livetvorder "
00782 "FROM cardinput "
00783 "WHERE cardinputid = :INPUTID");
00784 query.bindValue(":INPUTID", src_inputs[i]);
00785 if (!query.exec())
00786 {
00787 MythDB::DBError("clone_cardinput -- get data", query);
00788 ok = false;
00789 break;
00790 }
00791 if (!query.next())
00792 {
00793 LOG(VB_GENERAL, LOG_ERR, "clone_cardinput -- get data 2");
00794 ok = false;
00795 break;
00796 }
00797
00798 int match = -1;
00799 for (uint j = 0; j < dst_inputs.size(); j++)
00800 {
00801 if (src_names[i] == dst_names[j])
00802 {
00803 match = (int) j;
00804 break;
00805 }
00806 }
00807
00808 uint dst_inputid = 0;
00809 if (match >= 0)
00810 {
00811 dst_keep[match] = true;
00812
00813
00814 query2.prepare(
00815 "UPDATE cardinput "
00816 "SET sourceid = :V0, "
00817 " inputname = :V1, "
00818 " externalcommand = :V2, "
00819 " tunechan = :V3, "
00820 " startchan = :V4, "
00821 " displayname = :V5, "
00822 " dishnet_eit = :V6, "
00823 " recpriority = :V7, "
00824 " quicktune = :V8, "
00825 " schedorder = :V9, "
00826 " livetvorder = :V10 "
00827 "WHERE cardinputid = :INPUTID");
00828
00829 for (uint j = 0; j < 11; j++)
00830 {
00831 query2.bindValue(QString(":V%1").arg(j),
00832 query.value(j).toString());
00833 }
00834 query2.bindValue(":INPUTID", dst_inputs[match]);
00835
00836 if (!query2.exec())
00837 {
00838 MythDB::DBError("clone_cardinput -- update data", query2);
00839 ok = false;
00840 break;
00841 }
00842
00843 dst_inputid = dst_inputs[match];
00844 }
00845 else
00846 {
00847
00848
00849 query2.prepare(
00850 "INSERT cardinput "
00851 "SET cardid = :CARDID, "
00852 " sourceid = :V0, "
00853 " inputname = :V1, "
00854 " externalcommand = :V2, "
00855 " tunechan = :V3, "
00856 " startchan = :V4, "
00857 " displayname = :V5, "
00858 " dishnet_eit = :V6, "
00859 " recpriority = :V7, "
00860 " quicktune = :V8, "
00861 " schedorder = :V9, "
00862 " livetvorder = :V10 ");
00863
00864 query2.bindValue(":CARDID", dst_cardid);
00865 for (uint j = 0; j < 11; j++)
00866 {
00867 query2.bindValue(QString(":V%1").arg(j),
00868 query.value(j).toString());
00869 }
00870
00871 if (!query2.exec())
00872 {
00873 MythDB::DBError("clone_cardinput -- insert data", query2);
00874 ok = false;
00875 break;
00876 }
00877
00878 query2.prepare(
00879 "SELECT cardinputid "
00880 "FROM cardinput "
00881 "WHERE cardid = :CARDID AND "
00882 " inputname = :NAME");
00883 query2.bindValue(":CARDID", dst_cardid);
00884 query2.bindValue(":NAME", query.value(1).toString());
00885 if (!query2.exec())
00886 {
00887 MythDB::DBError("clone_cardinput -- "
00888 "insert, query inputid", query2);
00889 ok = false;
00890 break;
00891 }
00892 if (!query2.next())
00893 {
00894 LOG(VB_GENERAL, LOG_ERR, "clone_cardinput -- insert failed");
00895 ok = false;
00896 break;
00897 }
00898
00899 dst_inputid = query2.value(0).toUInt();
00900 }
00901
00902
00903 vector<uint> src_grps = CardUtil::GetInputGroups(src_inputs[i]);
00904 vector<uint> dst_grps = CardUtil::GetInputGroups(dst_inputid);
00905 for (uint j = 0; j < dst_grps.size(); j++)
00906 CardUtil::UnlinkInputGroup(dst_inputid, dst_grps[j]);
00907 for (uint j = 0; j < src_grps.size(); j++)
00908 CardUtil::LinkInputGroup(dst_inputid, src_grps[j]);
00909
00910
00911 DiSEqCDevSettings diseqc;
00912 if (diseqc.Load(src_inputs[i]))
00913 diseqc.Store(dst_inputid);
00914 }
00915
00916
00917 for (uint i = 0; i < dst_inputs.size(); i++)
00918 {
00919 if (!dst_keep[i])
00920 ok &= CardUtil::DeleteInput(dst_inputs[i]);
00921 }
00922
00923 return ok;
00924 }
00925
00926 bool CardUtil::CloneCard(uint src_cardid, uint orig_dst_cardid)
00927 {
00928 QString type = CardUtil::GetRawCardType(src_cardid);
00929 if (!IsTunerSharingCapable(type))
00930 return false;
00931
00932 uint dst_cardid = clone_capturecard(src_cardid, orig_dst_cardid);
00933 if (!dst_cardid)
00934 return false;
00935
00936 if (!clone_cardinputs(src_cardid, dst_cardid) && !orig_dst_cardid)
00937 {
00938 DeleteCard(dst_cardid);
00939 return false;
00940 }
00941
00942 return true;
00943 }
00944
00945 vector<uint> CardUtil::GetCloneCardIDs(uint cardid)
00946 {
00947 vector<uint> list;
00948 MSqlQuery query(MSqlQuery::InitCon());
00949 query.prepare(
00950 "SELECT cardtype, videodevice, hostname "
00951 "FROM capturecard "
00952 "WHERE cardid = :CARDID");
00953 query.bindValue(":CARDID", cardid);
00954
00955 if (!query.exec())
00956 {
00957 MythDB::DBError("CardUtil::GetCloneCardIDs() 1", query);
00958 return list;
00959 }
00960
00961 if (!query.next())
00962 return list;
00963
00964 QString rawtype = query.value(0).toString();
00965 QString videodevice = query.value(1).toString();
00966 QString hostname = query.value(2).toString();
00967
00968 if (!IsTunerSharingCapable(rawtype))
00969 return list;
00970
00971 query.prepare(
00972 "SELECT cardid "
00973 "FROM capturecard "
00974 "WHERE cardid != :CARDID AND "
00975 " videodevice = :DEVICE AND "
00976 " cardtype = :TYPE AND "
00977 " hostname = :HOSTNAME");
00978 query.bindValue(":CARDID", cardid);
00979 query.bindValue(":DEVICE", videodevice);
00980 query.bindValue(":TYPE", rawtype);
00981 query.bindValue(":HOSTNAME", hostname);
00982
00983 if (!query.exec())
00984 {
00985 MythDB::DBError("CardUtil::GetCloneCardIDs() 2", query);
00986 return list;
00987 }
00988
00989 while (query.next())
00990 list.push_back(query.value(0).toUInt());
00991
00992 return list;
00993 }
00994
00995 QString CardUtil::GetFirewireChangerNode(uint inputid)
00996 {
00997 QString fwnode;
00998
00999 MSqlQuery query(MSqlQuery::InitCon());
01000 query.prepare("SELECT changer_device "
01001 "FROM cardinput WHERE cardinputid = :INPUTID ");
01002 query.bindValue(":CARDID", inputid);
01003
01004 if (query.exec() && query.next())
01005 {
01006 fwnode = query.value(0).toString();
01007 }
01008
01009 return fwnode;
01010 }
01011
01012 QString CardUtil::GetFirewireChangerModel(uint inputid)
01013 {
01014 QString fwnode;
01015
01016 MSqlQuery query(MSqlQuery::InitCon());
01017 query.prepare("SELECT changer_model "
01018 "FROM cardinput WHERE cardinputid = :INPUTID ");
01019 query.bindValue(":CARDID", inputid);
01020
01021 if (query.exec() && query.next())
01022 {
01023 fwnode = query.value(0).toString();
01024 }
01025
01026 return fwnode;
01027 }
01028
01029 vector<uint> CardUtil::GetCardIDs(uint sourceid)
01030 {
01031 MSqlQuery query(MSqlQuery::InitCon());
01032
01033 query.prepare(
01034 "SELECT DISTINCT cardid "
01035 "FROM cardinput "
01036 "WHERE sourceid = :SOURCEID");
01037 query.bindValue(":SOURCEID", sourceid);
01038
01039 vector<uint> list;
01040
01041 if (!query.exec())
01042 {
01043 MythDB::DBError("CardUtil::GetCardIDs()", query);
01044 return list;
01045 }
01046
01047 while (query.next())
01048 list.push_back(query.value(0).toUInt());
01049
01050 return list;
01051 }
01052
01053 int CardUtil::GetCardInputID(
01054 uint cardid, const QString &channum, QString &inputname)
01055 {
01056 MSqlQuery query(MSqlQuery::InitCon());
01057 query.prepare(
01058 "SELECT cardinputid, inputname "
01059 "FROM channel, capturecard, cardinput "
01060 "WHERE channel.channum = :CHANNUM AND "
01061 " channel.sourceid = cardinput.sourceid AND "
01062 " cardinput.cardid = capturecard.cardid AND "
01063 " capturecard.cardid = :CARDID");
01064 query.bindValue(":CHANNUM", channum);
01065 query.bindValue(":CARDID", cardid);
01066
01067 if (!query.exec() || !query.isActive())
01068 MythDB::DBError("get_cardinputid", query);
01069 else if (query.next())
01070 {
01071 inputname = query.value(1).toString();
01072 return query.value(0).toInt();
01073 }
01074
01075 return -1;
01076 }
01077
01078 bool CardUtil::SetStartChannel(uint cardinputid, const QString &channum)
01079 {
01080 MSqlQuery query(MSqlQuery::InitCon());
01081 query.prepare("UPDATE cardinput "
01082 "SET startchan = :CHANNUM "
01083 "WHERE cardinputid = :INPUTID");
01084 query.bindValue(":CHANNUM", channum);
01085 query.bindValue(":INPUTID", cardinputid);
01086
01087 if (!query.exec())
01088 {
01089 MythDB::DBError("set_startchan", query);
01090 return false;
01091 }
01092
01093 return true;
01094 }
01095
01101 QString CardUtil::GetStartInput(uint nCardID)
01102 {
01103 QString str = QString::null;
01104 MSqlQuery query(MSqlQuery::InitCon());
01105 query.prepare("SELECT inputname "
01106 "FROM cardinput "
01107 "WHERE cardinput.cardid = :CARDID "
01108 "ORDER BY livetvorder = 0, livetvorder, cardinputid "
01109 "LIMIT 1");
01110 query.bindValue(":CARDID", nCardID);
01111
01112 if (!query.exec() || !query.isActive())
01113 MythDB::DBError("CardUtil::GetStartInput()", query);
01114 else if (query.next())
01115 str = query.value(0).toString();
01116
01117 return str;
01118 }
01119
01120 QStringList CardUtil::GetInputNames(uint cardid, uint sourceid)
01121 {
01122 QStringList list;
01123 MSqlQuery query(MSqlQuery::InitCon());
01124
01125 if (sourceid)
01126 {
01127 query.prepare("SELECT inputname "
01128 "FROM cardinput "
01129 "WHERE sourceid = :SOURCEID AND "
01130 " cardid = :CARDID");
01131 query.bindValue(":SOURCEID", sourceid);
01132 }
01133 else
01134 {
01135 query.prepare("SELECT inputname "
01136 "FROM cardinput "
01137 "WHERE cardid = :CARDID");
01138 }
01139 query.bindValue(":CARDID", cardid);
01140
01141 if (!query.exec())
01142 {
01143 MythDB::DBError("CardUtil::GetInputNames()", query);
01144 }
01145 else
01146 {
01147 while (query.next())
01148 list.append( query.value(0).toString() );
01149 }
01150
01151 return list;
01152 }
01153
01154 bool CardUtil::GetInputInfo(InputInfo &input, vector<uint> *groupids)
01155 {
01156 if (!input.inputid)
01157 return false;
01158
01159 MSqlQuery query(MSqlQuery::InitCon());
01160 query.prepare("SELECT inputname, sourceid, cardid, livetvorder "
01161 "FROM cardinput "
01162 "WHERE cardinputid = :INPUTID");
01163 query.bindValue(":INPUTID", input.inputid);
01164
01165 if (!query.exec())
01166 {
01167 MythDB::DBError("CardUtil::GetInputInfo()", query);
01168 return false;
01169 }
01170
01171 if (!query.next())
01172 return false;
01173
01174 input.name = query.value(0).toString();
01175 input.sourceid = query.value(1).toUInt();
01176 input.cardid = query.value(2).toUInt();
01177 input.livetvorder = query.value(3).toUInt();
01178
01179 if (groupids)
01180 *groupids = GetInputGroups(input.inputid);
01181
01182 return true;
01183 }
01184
01185 uint CardUtil::GetCardID(uint inputid)
01186 {
01187 InputInfo info(QString::null, 0, inputid, 0, 0, 0);
01188 GetInputInfo(info);
01189 return info.cardid;
01190 }
01191
01192 QString CardUtil::GetInputName(uint inputid)
01193 {
01194 InputInfo info(QString::null, 0, inputid, 0, 0, 0);
01195 GetInputInfo(info);
01196 return info.name;
01197 }
01198
01199 QString CardUtil::GetStartingChannel(uint inputid)
01200 {
01201 MSqlQuery query(MSqlQuery::InitCon());
01202 query.prepare("SELECT startchan "
01203 "FROM cardinput "
01204 "WHERE cardinputid = :INPUTID");
01205 query.bindValue(":INPUTID", inputid);
01206
01207 if (!query.exec())
01208 MythDB::DBError("CardUtil::GetStartingChannel(uint)", query);
01209 else if (query.next())
01210 return query.value(0).toString();
01211
01212 return QString::null;
01213 }
01214
01215 QString CardUtil::GetDisplayName(uint inputid)
01216 {
01217 if (!inputid)
01218 return QString::null;
01219
01220 MSqlQuery query(MSqlQuery::InitCon());
01221 query.prepare("SELECT displayname, cardid, inputname "
01222 "FROM cardinput "
01223 "WHERE cardinputid = :INPUTID");
01224 query.bindValue(":INPUTID", inputid);
01225
01226 if (!query.exec())
01227 MythDB::DBError("CardUtil::GetDisplayName(uint)", query);
01228 else if (query.next())
01229 {
01230 QString result = query.value(0).toString();
01231 if (result.isEmpty())
01232 result = QString("%1: %2").arg(query.value(1).toInt())
01233 .arg(query.value(2).toString());
01234 return result;
01235 }
01236
01237 return QString::null;
01238 }
01239
01240 uint CardUtil::GetInputID(uint cardid, const QString &inputname)
01241 {
01242 MSqlQuery query(MSqlQuery::InitCon());
01243 query.prepare("SELECT cardinputid "
01244 "FROM cardinput "
01245 "WHERE inputname = :INPUTNAME AND "
01246 " cardid = :CARDID");
01247 query.bindValue(":INPUTNAME", inputname);
01248 query.bindValue(":CARDID", cardid);
01249
01250 if (!query.exec())
01251 MythDB::DBError("CardUtil::GetInputID(uint,QString)", query);
01252 else if (query.next())
01253 return query.value(0).toUInt();
01254
01255 return 0;
01256 }
01257
01258 uint CardUtil::GetInputID(uint cardid, uint sourceid)
01259 {
01260 MSqlQuery query(MSqlQuery::InitCon());
01261 query.prepare("SELECT cardinputid "
01262 "FROM cardinput "
01263 "WHERE sourceid = :SOURCEID AND "
01264 " cardid = :CARDID");
01265 query.bindValue(":SOURCEID", sourceid);
01266 query.bindValue(":CARDID", cardid);
01267
01268 if (!query.exec())
01269 MythDB::DBError("CardUtil::GetInputID(uint,uint)", query);
01270 else if (query.next())
01271 return query.value(0).toUInt();
01272
01273 return 0;
01274 }
01275
01276 uint CardUtil::GetSourceID(uint inputid)
01277 {
01278 MSqlQuery query(MSqlQuery::InitCon());
01279 query.prepare(
01280 "SELECT sourceid "
01281 "FROM cardinput "
01282 "WHERE cardinputid = :INPUTID");
01283 query.bindValue(":INPUTID", inputid);
01284 if (!query.exec() || !query.isActive())
01285 MythDB::DBError("CardUtil::GetSourceID()", query);
01286 else if (query.next())
01287 return query.value(0).toUInt();
01288
01289 return 0;
01290 }
01291
01292 vector<uint> CardUtil::GetAllInputIDs(void)
01293 {
01294 vector<uint> list;
01295
01296 MSqlQuery query(MSqlQuery::InitCon());
01297 query.prepare(
01298 "SELECT cardinputid "
01299 "FROM cardinput");
01300
01301 if (!query.exec())
01302 {
01303 MythDB::DBError("CardUtil::GetAllInputIDs(uint)", query);
01304 return list;
01305 }
01306
01307 while (query.next())
01308 list.push_back(query.value(0).toUInt());
01309
01310 return list;
01311 }
01312
01313 vector<uint> CardUtil::GetInputIDs(uint cardid)
01314 {
01315 vector<uint> list;
01316
01317 MSqlQuery query(MSqlQuery::InitCon());
01318 query.prepare(
01319 "SELECT cardinputid "
01320 "FROM cardinput "
01321 "WHERE cardid = :CARDID");
01322
01323 query.bindValue(":CARDID", cardid);
01324
01325 if (!query.exec())
01326 {
01327 MythDB::DBError("CardUtil::GetInputIDs(uint)", query);
01328 return list;
01329 }
01330
01331 while (query.next())
01332 list.push_back(query.value(0).toUInt());
01333
01334 return list;
01335 }
01336
01337 int CardUtil::CreateCardInput(const uint cardid,
01338 const uint sourceid,
01339 const QString &inputname,
01340 const QString &externalcommand,
01341 const QString &changer_device,
01342 const QString &changer_model,
01343 const QString &hostname,
01344 const QString &tunechan,
01345 const QString &startchan,
01346 const QString &displayname,
01347 bool dishnet_eit,
01348 const uint recpriority,
01349 const uint quicktune,
01350 const uint schedorder,
01351 const uint livetvorder)
01352 {
01353 MSqlQuery query(MSqlQuery::InitCon());
01354
01355 query.prepare(
01356 "INSERT INTO cardinput "
01357 "(cardid, sourceid, inputname, externalcommand, changer_device, "
01358 "changer_model, tunechan, startchan, displayname, dishnet_eit, "
01359 "recpriority, quicktune, schedorder, livetvorder) "
01360 "VALUES (:CARDID, :SOURCEID, :INPUTNAME, :EXTERNALCOMMAND, "
01361 ":CHANGERDEVICE, :CHANGERMODEL, :TUNECHAN, :STARTCHAN, :DISPLAYNAME, "
01362 ":DISHNETEIT, :RECPRIORITY, :QUICKTUNE, :SCHEDORDER, :LIVETVORDER ) ");
01363
01364 query.bindValue(":CARDID", cardid);
01365 query.bindValue(":SOURCEID", sourceid);
01366 query.bindValue(":INPUTNAME", inputname);
01367 query.bindValue(":EXTERNALCOMMAND", externalcommand);
01368 query.bindValue(":CHANGERDEVICE", changer_device);
01369 query.bindValue(":CHANGERMODEL", changer_model);
01370 query.bindValue(":TUNECHAN", tunechan);
01371 query.bindValue(":STARTCHAN", startchan);
01372 query.bindValue(":DISPLAYNAME", displayname.isNull() ? "" : displayname);
01373 query.bindValue(":DISHNETEIT", dishnet_eit);
01374 query.bindValue(":RECPRIORITY", recpriority);
01375 query.bindValue(":QUICKTUNE", quicktune);
01376 query.bindValue(":SCHEDORDER", schedorder);
01377 query.bindValue(":LIVETVORDER", livetvorder);
01378
01379 if (!query.exec())
01380 {
01381 MythDB::DBError("CreateCardInput", query);
01382 return -1;
01383 }
01384
01385 query.prepare("SELECT MAX(cardinputid) FROM cardinput");
01386
01387 if (!query.exec())
01388 {
01389 MythDB::DBError("CreateCardInput maxinput", query);
01390 return -1;
01391 }
01392
01393 uint inputid = -1;
01394
01395 if (query.next())
01396 inputid = query.value(0).toUInt();
01397
01398 return inputid;
01399 }
01400
01401 bool CardUtil::DeleteInput(uint inputid)
01402 {
01403 MSqlQuery query(MSqlQuery::InitCon());
01404 query.prepare(
01405 "DELETE FROM cardinput "
01406 "WHERE cardinputid = :INPUTID");
01407 query.bindValue(":INPUTID", inputid);
01408
01409 if (!query.exec())
01410 {
01411 MythDB::DBError("DeleteInput", query);
01412 return false;
01413 }
01414
01415 return true;
01416 }
01417
01418 bool CardUtil::DeleteOrphanInputs(void)
01419 {
01420 MSqlQuery query(MSqlQuery::InitCon());
01421 query.prepare("SELECT cardinputid "
01422 "FROM cardinput "
01423 "LEFT JOIN capturecard "
01424 "ON (capturecard.cardid = cardinput.cardid) "
01425 "WHERE capturecard.cardid IS NULL");
01426 if (!query.exec())
01427 {
01428 MythDB::DBError("DeleteOrphanInputs -- query disconnects", query);
01429 return false;
01430 }
01431
01432 bool ok = true;
01433 while (query.next())
01434 {
01435 uint inputid = query.value(0).toUInt();
01436 if (DeleteInput(inputid))
01437 {
01438 LOG(VB_GENERAL, LOG_NOTICE, QString("Removed orphan input %1")
01439 .arg(inputid));
01440 }
01441 else
01442 {
01443 ok = false;
01444 LOG(VB_GENERAL, LOG_ERR, QString("Failed to remove orphan input %1")
01445 .arg(inputid));
01446 }
01447 }
01448
01449 return ok;
01450 }
01451
01452 uint CardUtil::CreateInputGroup(const QString &name)
01453 {
01454 MSqlQuery query(MSqlQuery::InitCon());
01455 query.prepare("SELECT MAX(inputgroupid) FROM inputgroup");
01456 if (!query.exec())
01457 {
01458 MythDB::DBError("CreateNewInputGroup 1", query);
01459 return 0;
01460 }
01461 uint inputgroupid = (query.next()) ? query.value(0).toUInt() + 1 : 1;
01462
01463 query.prepare(
01464 "INSERT INTO inputgroup "
01465 " (cardinputid, inputgroupid, inputgroupname) "
01466 "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
01467
01468 query.bindValue(":INPUTID", 0);
01469 query.bindValue(":GROUPID", inputgroupid);
01470 query.bindValue(":GROUPNAME", name);
01471
01472 if (!query.exec())
01473 {
01474 MythDB::DBError("CreateNewInputGroup 2", query);
01475 return 0;
01476 }
01477
01478 return inputgroupid;
01479 }
01480
01481 bool CardUtil::CreateInputGroupIfNeeded(uint cardid)
01482 {
01483
01484
01485 vector<uint> ingrps = CardUtil::GetSharedInputGroups(cardid);
01486 vector<uint> inputs = CardUtil::GetInputIDs(cardid);
01487
01488 if (ingrps.empty() && !inputs.empty())
01489 {
01490 QString name = CardUtil::GetRawCardType(cardid) + "_" +
01491 CardUtil::GetVideoDevice(cardid);
01492 uint id = 0;
01493 for (uint i = 0; !id && (i < 100); i++)
01494 {
01495 if (i)
01496 name += QString(":%1").arg(i);
01497 id = CardUtil::CreateInputGroup(name);
01498 }
01499 if (!id)
01500 {
01501 LOG(VB_GENERAL, LOG_ERR, "Failed to create input group");
01502 return false;
01503 }
01504
01505 bool ok = true;
01506 for (uint i = 0; i < inputs.size(); i++)
01507 ok &= CardUtil::LinkInputGroup(inputs[i], id);
01508
01509 if (!ok)
01510 LOG(VB_GENERAL, LOG_ERR, "Failed to link to new input group");
01511
01512 return ok;
01513 }
01514
01515 return true;
01516 }
01517
01518 bool CardUtil::LinkInputGroup(uint inputid, uint inputgroupid)
01519 {
01520 MSqlQuery query(MSqlQuery::InitCon());
01521
01522 query.prepare(
01523 "SELECT cardinputid, inputgroupid, inputgroupname "
01524 "FROM inputgroup "
01525 "WHERE inputgroupid = :GROUPID "
01526 "ORDER BY inputgroupid, cardinputid, inputgroupname");
01527 query.bindValue(":GROUPID", inputgroupid);
01528
01529 if (!query.exec())
01530 {
01531 MythDB::DBError("CardUtil::CreateInputGroup() 1", query);
01532 return false;
01533 }
01534
01535 if (!query.next())
01536 return false;
01537
01538 const QString name = query.value(2).toString();
01539
01540 query.prepare(
01541 "INSERT INTO inputgroup "
01542 " (cardinputid, inputgroupid, inputgroupname) "
01543 "VALUES (:INPUTID, :GROUPID, :GROUPNAME ) ");
01544
01545 query.bindValue(":INPUTID", inputid);
01546 query.bindValue(":GROUPID", inputgroupid);
01547 query.bindValue(":GROUPNAME", name);
01548
01549 if (!query.exec())
01550 {
01551 MythDB::DBError("CardUtil::CreateInputGroup() 2", query);
01552 return false;
01553 }
01554
01555 return true;
01556 }
01557
01558 bool CardUtil::UnlinkInputGroup(uint inputid, uint inputgroupid)
01559 {
01560 MSqlQuery query(MSqlQuery::InitCon());
01561
01562 if (!inputid && !inputgroupid)
01563 {
01564 query.prepare(
01565 "DELETE FROM inputgroup "
01566 "WHERE cardinputid = 0 ");
01567 }
01568 else
01569 {
01570 query.prepare(
01571 "DELETE FROM inputgroup "
01572 "WHERE cardinputid = :INPUTID AND "
01573 " inputgroupid = :GROUPID ");
01574
01575 query.bindValue(":INPUTID", inputid);
01576 query.bindValue(":GROUPID", inputgroupid);
01577 }
01578
01579 if (!query.exec())
01580 {
01581 MythDB::DBError("CardUtil::DeleteInputGroup()", query);
01582 return false;
01583 }
01584
01585 return true;
01586 }
01587
01588 vector<uint> CardUtil::GetInputGroups(uint inputid)
01589 {
01590 vector<uint> list;
01591
01592 MSqlQuery query(MSqlQuery::InitCon());
01593
01594 query.prepare(
01595 "SELECT inputgroupid "
01596 "FROM inputgroup "
01597 "WHERE cardinputid = :INPUTID "
01598 "ORDER BY inputgroupid, cardinputid, inputgroupname");
01599
01600 query.bindValue(":INPUTID", inputid);
01601
01602 if (!query.exec())
01603 {
01604 MythDB::DBError("CardUtil::GetInputGroups()", query);
01605 return list;
01606 }
01607
01608 while (query.next())
01609 list.push_back(query.value(0).toUInt());
01610
01611 return list;
01612 }
01613
01614 vector<uint> CardUtil::GetSharedInputGroups(uint cardid)
01615 {
01616 vector<uint> list;
01617
01618 vector<uint> inputs = GetInputIDs(cardid);
01619 if (inputs.empty())
01620 return list;
01621
01622 list = GetInputGroups(inputs[0]);
01623 for (uint i = 1; (i < inputs.size()) && list.size(); i++)
01624 {
01625 vector<uint> curlist = GetInputGroups(inputs[i]);
01626 vector<uint> newlist;
01627 for (uint j = 0; j < list.size(); j++)
01628 {
01629 if (find(curlist.begin(), curlist.end(), list[j]) != curlist.end())
01630 newlist.push_back(list[j]);
01631 }
01632 list = newlist;
01633 }
01634
01635 return list;
01636 }
01637
01638 vector<uint> CardUtil::GetGroupCardIDs(uint inputgroupid)
01639 {
01640 vector<uint> list;
01641
01642 MSqlQuery query(MSqlQuery::InitCon());
01643
01644 query.prepare(
01645 "SELECT DISTINCT cardid "
01646 "FROM cardinput, inputgroup "
01647 "WHERE inputgroupid = :GROUPID AND "
01648 " cardinput.cardinputid = inputgroup.cardinputid "
01649 "ORDER BY cardid");
01650
01651 query.bindValue(":GROUPID", inputgroupid);
01652
01653 if (!query.exec())
01654 {
01655 MythDB::DBError("CardUtil::GetGroupCardIDs()", query);
01656 return list;
01657 }
01658
01659 while (query.next())
01660 list.push_back(query.value(0).toUInt());
01661
01662 return list;
01663 }
01664
01665 vector<uint> CardUtil::GetConflictingCards(uint inputid, uint exclude_cardid)
01666 {
01667 vector<uint> inputgroupids = CardUtil::GetInputGroups(inputid);
01668
01669 for (uint i = 0; i < inputgroupids.size(); i++)
01670 {
01671 LOG(VB_RECORD, LOG_INFO, LOC + QString(" Group ID %1")
01672 .arg(inputgroupids[i]));
01673 }
01674
01675 vector<uint> cardids;
01676 for (uint i = 0; i < inputgroupids.size(); i++)
01677 {
01678 vector<uint> tmp = CardUtil::GetGroupCardIDs(inputgroupids[i]);
01679 for (uint j = 0; j < tmp.size(); j++)
01680 {
01681 if (tmp[j] == exclude_cardid)
01682 continue;
01683
01684 if (find(cardids.begin(), cardids.end(), tmp[j]) != cardids.end())
01685 continue;
01686
01687 cardids.push_back(tmp[j]);
01688 }
01689 }
01690
01691 for (uint i = 0; i < cardids.size(); i++)
01692 LOG(VB_RECORD, LOG_INFO, LOC + QString(" Card ID %1").arg(cardids[i]));
01693
01694 return cardids;
01695 }
01696
01697 bool CardUtil::GetTimeouts(uint cardid,
01698 uint &signal_timeout, uint &channel_timeout)
01699 {
01700 MSqlQuery query(MSqlQuery::InitCon());
01701 query.prepare(
01702 "SELECT signal_timeout, channel_timeout "
01703 "FROM capturecard "
01704 "WHERE cardid = :CARDID");
01705 query.bindValue(":CARDID", cardid);
01706
01707 if (!query.exec() || !query.isActive())
01708 MythDB::DBError("CardUtil::GetTimeouts()", query);
01709 else if (query.next())
01710 {
01711 signal_timeout = (uint) max(query.value(0).toInt(), 250);
01712 channel_timeout = (uint) max(query.value(1).toInt(), 500);
01713 return true;
01714 }
01715
01716 return false;
01717 }
01718
01719 bool CardUtil::IsInNeedOfExternalInputConf(uint cardid)
01720 {
01721 DiSEqCDev dev;
01722 DiSEqCDevTree *diseqc_tree = dev.FindTree(cardid);
01723
01724 bool needsConf = false;
01725 if (diseqc_tree)
01726 needsConf = diseqc_tree->IsInNeedOfConf();
01727
01728 return needsConf;
01729 }
01730
01731 uint CardUtil::GetQuickTuning(uint cardid, const QString &input_name)
01732 {
01733 uint quicktune = 0;
01734
01735 MSqlQuery query(MSqlQuery::InitCon());
01736 query.prepare(
01737 "SELECT quicktune "
01738 "FROM cardinput "
01739 "WHERE cardid = :CARDID AND "
01740 " inputname = :INPUTNAME");
01741 query.bindValue(":CARDID", cardid);
01742 query.bindValue(":INPUTNAME", input_name);
01743
01744 if (!query.exec() || !query.isActive())
01745 MythDB::DBError("CardUtil::GetQuickTuning()", query);
01746 else if (query.next())
01747 quicktune = query.value(0).toUInt();
01748
01749 return quicktune;
01750 }
01751
01752 bool CardUtil::hasV4L2(int videofd)
01753 {
01754 (void) videofd;
01755 #ifdef USING_V4L2
01756 struct v4l2_capability vcap;
01757 memset(&vcap, 0, sizeof(vcap));
01758
01759 return ((ioctl(videofd, VIDIOC_QUERYCAP, &vcap) >= 0) &&
01760 (vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE));
01761 #else // if !USING_V4L2
01762 return false;
01763 #endif // !USING_V4L2
01764 }
01765
01766 bool CardUtil::GetV4LInfo(
01767 int videofd, QString &card, QString &driver, uint32_t &version,
01768 uint32_t &capabilities)
01769 {
01770 card = driver = QString::null;
01771 version = 0;
01772 capabilities = 0;
01773
01774 if (videofd < 0)
01775 return false;
01776
01777 #ifdef USING_V4L2
01778
01779 struct v4l2_capability capability;
01780 memset(&capability, 0, sizeof(struct v4l2_capability));
01781 if (ioctl(videofd, VIDIOC_QUERYCAP, &capability) >= 0)
01782 {
01783 card = QString::fromAscii((const char*)capability.card);
01784 driver = QString::fromAscii((const char*)capability.driver);
01785 version = capability.version;
01786 capabilities = capability.capabilities;
01787 }
01788 #ifdef USING_V4L1
01789 else
01790 {
01791 struct video_capability capability;
01792 if (ioctl(videofd, VIDIOCGCAP, &capability) >= 0)
01793 card = QString::fromAscii((const char*)capability.name);
01794 }
01795 #endif // USING_V4L1
01796 #endif // USING_V4L2
01797
01798 if (!driver.isEmpty())
01799 driver.remove( QRegExp("\\[[0-9]\\]$") );
01800
01801 return !card.isEmpty();
01802 }
01803
01804 InputNames CardUtil::ProbeV4LVideoInputs(int videofd, bool &ok)
01805 {
01806 (void) videofd;
01807
01808 InputNames list;
01809 ok = false;
01810
01811 #ifdef USING_V4L2
01812 bool usingv4l2 = hasV4L2(videofd);
01813
01814
01815 struct v4l2_input vin;
01816 memset(&vin, 0, sizeof(vin));
01817 while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMINPUT, &vin) >= 0))
01818 {
01819 QString input((char *)vin.name);
01820 list[vin.index] = input;
01821 vin.index++;
01822 }
01823 if (vin.index)
01824 {
01825 ok = true;
01826 return list;
01827 }
01828
01829 #ifdef USING_V4L1
01830
01831 struct video_capability vidcap;
01832 memset(&vidcap, 0, sizeof(vidcap));
01833 if (ioctl(videofd, VIDIOCGCAP, &vidcap) != 0)
01834 {
01835 QString msg = QObject::tr("Could not query inputs.");
01836 LOG(VB_GENERAL, LOG_ERR, "ProbeV4LVideoInputs(): Error, " + msg + ENO);
01837 list[-1] = msg;
01838 vidcap.channels = 0;
01839 }
01840
01841 for (int i = 0; i < vidcap.channels; i++)
01842 {
01843 struct video_channel test;
01844 memset(&test, 0, sizeof(test));
01845 test.channel = i;
01846
01847 if (ioctl(videofd, VIDIOCGCHAN, &test) != 0)
01848 {
01849 LOG(VB_GENERAL, LOG_ERR, "ProbeV4LVideoInputs(): Error, " +
01850 QString("Could determine name of input #%1"
01851 "\n\t\t\tNot adding it to the list.")
01852 .arg(test.channel) + ENO);
01853 continue;
01854 }
01855
01856 list[i] = test.name;
01857 }
01858 #endif // USING_V4L1
01859
01860
01861 if (!list.size())
01862 list[0] = "Television";
01863
01864 ok = true;
01865 #else // if !USING_V4L2
01866 list[-1] += QObject::tr("ERROR, Compile with V4L support to query inputs");
01867 #endif // !USING_V4L2
01868 return list;
01869 }
01870
01871 InputNames CardUtil::ProbeV4LAudioInputs(int videofd, bool &ok)
01872 {
01873 (void) videofd;
01874
01875 InputNames list;
01876 ok = false;
01877
01878 #ifdef USING_V4L2
01879 bool usingv4l2 = hasV4L2(videofd);
01880
01881
01882 struct v4l2_audio ain;
01883 memset(&ain, 0, sizeof(ain));
01884 while (usingv4l2 && (ioctl(videofd, VIDIOC_ENUMAUDIO, &ain) >= 0))
01885 {
01886 QString input((char *)ain.name);
01887 list[ain.index] = input;
01888 ain.index++;
01889 }
01890 if (ain.index)
01891 {
01892 ok = true;
01893 return list;
01894 }
01895
01896 ok = true;
01897 #else // if !USING_V4L2
01898 list[-1] += QObject::tr(
01899 "ERROR, Compile with V4L support to query audio inputs");
01900 #endif // !USING_V4L2
01901 return list;
01902 }
01903
01904 InputNames CardUtil::GetConfiguredDVBInputs(uint cardid)
01905 {
01906 InputNames list;
01907 MSqlQuery query(MSqlQuery::InitCon());
01908 query.prepare(
01909 "SELECT cardinputid, inputname "
01910 "FROM cardinput "
01911 "WHERE cardid = :CARDID");
01912 query.bindValue(":CARDID", cardid);
01913
01914 if (!query.exec() || !query.isActive())
01915 MythDB::DBError("CardUtil::GetConfiguredDVBInputs", query);
01916 else
01917 {
01918 while (query.next())
01919 list[query.value(0).toUInt()] = query.value(1).toString();
01920 }
01921 return list;
01922 }
01923
01924 QStringList CardUtil::ProbeVideoInputs(QString device, QString cardtype)
01925 {
01926 QStringList ret;
01927
01928 if (IsSingleInputCard(cardtype))
01929 ret += "MPEG2TS";
01930 else if ("DVB" == cardtype)
01931 ret += ProbeDVBInputs(device);
01932 else
01933 ret += ProbeV4LVideoInputs(device);
01934
01935 return ret;
01936 }
01937
01938 QStringList CardUtil::ProbeAudioInputs(QString device, QString cardtype)
01939 {
01940 LOG(VB_GENERAL, LOG_DEBUG, QString("ProbeAudioInputs(%1,%2)")
01941 .arg(device).arg(cardtype));
01942 QStringList ret;
01943
01944 if ("HDPVR" == cardtype)
01945 ret += ProbeV4LAudioInputs(device);
01946
01947 return ret;
01948 }
01949
01950 QStringList CardUtil::ProbeV4LVideoInputs(QString device)
01951 {
01952 bool ok;
01953 QStringList ret;
01954 QByteArray dev = device.toAscii();
01955 int videofd = open(dev.constData(), O_RDWR);
01956 if (videofd < 0)
01957 {
01958 ret += QObject::tr("Could not open '%1' "
01959 "to probe its inputs.").arg(device);
01960 return ret;
01961 }
01962 InputNames list = CardUtil::ProbeV4LVideoInputs(videofd, ok);
01963 close(videofd);
01964
01965 if (!ok)
01966 {
01967 ret += list[-1];
01968 return ret;
01969 }
01970
01971 InputNames::iterator it;
01972 for (it = list.begin(); it != list.end(); ++it)
01973 {
01974 if (it.key() >= 0)
01975 ret += *it;
01976 }
01977
01978 return ret;
01979 }
01980
01981 QStringList CardUtil::ProbeV4LAudioInputs(QString device)
01982 {
01983 LOG(VB_GENERAL, LOG_DEBUG, QString("ProbeV4LAudioInputs(%1)").arg(device));
01984
01985 bool ok;
01986 QStringList ret;
01987 int videofd = open(device.toAscii().constData(), O_RDWR);
01988 if (videofd < 0)
01989 {
01990 LOG(VB_GENERAL, LOG_ERR, "ProbeAudioInputs() -> couldn't open device");
01991 ret += QObject::tr("Could not open '%1' to probe its inputs.")
01992 .arg(device);
01993 return ret;
01994 }
01995 InputNames list = CardUtil::ProbeV4LAudioInputs(videofd, ok);
01996 close(videofd);
01997
01998 if (!ok)
01999 {
02000 ret += list[-1];
02001 return ret;
02002 }
02003
02004 InputNames::iterator it;
02005 for (it = list.begin(); it != list.end(); ++it)
02006 {
02007 if (it.key() >= 0)
02008 ret += *it;
02009 }
02010
02011 return ret;
02012 }
02013
02014 QStringList CardUtil::ProbeDVBInputs(QString device)
02015 {
02016 QStringList ret;
02017
02018 #ifdef USING_DVB
02019 uint cardid = CardUtil::GetFirstCardID(device);
02020 if (!cardid)
02021 return ret;
02022
02023 InputNames list = GetConfiguredDVBInputs(cardid);
02024 InputNames::iterator it;
02025 for (it = list.begin(); it != list.end(); ++it)
02026 {
02027 if (it.key())
02028 ret += *it;
02029 }
02030 #else
02031 (void) device;
02032 ret += QObject::tr("ERROR, Compile with DVB support to query inputs");
02033 #endif
02034
02035 return ret;
02036 }
02037
02038 QString CardUtil::GetDeviceLabel(const QString &cardtype,
02039 const QString &videodevice)
02040 {
02041 return QString("[ %1 : %2 ]").arg(cardtype).arg(videodevice);
02042 }
02043
02044 QString CardUtil::GetDeviceLabel(uint cardid)
02045 {
02046 QString devlabel;
02047 MSqlQuery query(MSqlQuery::InitCon());
02048 query.prepare("SELECT cardtype, videodevice "
02049 "FROM capturecard WHERE cardid = :CARDID ");
02050 query.bindValue(":CARDID", cardid);
02051
02052 if (query.exec() && query.next())
02053 {
02054 return GetDeviceLabel(query.value(0).toString(),
02055 query.value(1).toString());
02056 }
02057
02058 return "[ UNKNOWN ]";
02059 }
02060
02061 void CardUtil::GetCardInputs(
02062 uint cardid,
02063 const QString &device,
02064 const QString &cardtype,
02065 QStringList &inputLabels,
02066 vector<CardInput*> &cardInputs)
02067 {
02068 QStringList inputs;
02069 bool is_dtv = !IsEncoder(cardtype) && !IsUnscanable(cardtype);
02070
02071 if (IsSingleInputCard(cardtype))
02072 inputs += "MPEG2TS";
02073 else if ("DVB" != cardtype)
02074 inputs += ProbeV4LVideoInputs(device);
02075
02076 QString dev_label = GetDeviceLabel(cardtype, device);
02077
02078 QStringList::iterator it = inputs.begin();
02079 for (; it != inputs.end(); ++it)
02080 {
02081 CardInput *cardinput = new CardInput(is_dtv, false, false, cardid);
02082 cardinput->loadByInput(cardid, (*it));
02083 inputLabels.push_back(
02084 dev_label + QString(" (%1) -> %2")
02085 .arg(*it).arg(cardinput->getSourceName()));
02086 cardInputs.push_back(cardinput);
02087 }
02088
02089 #ifdef USING_DVB
02090 if ("DVB" == cardtype)
02091 {
02092 bool needs_conf = IsInNeedOfExternalInputConf(cardid);
02093 InputNames list = GetConfiguredDVBInputs(cardid);
02094 if (!needs_conf && list.empty())
02095 list[0] = "DVBInput";
02096
02097 InputNames::const_iterator it;
02098 for (it = list.begin(); it != list.end(); ++it)
02099 {
02100 CardInput *cardinput = new CardInput(is_dtv, true, false, cardid);
02101 cardinput->loadByInput(cardid, *it);
02102 inputLabels.push_back(
02103 dev_label + QString(" (%1) -> %2")
02104 .arg(*it).arg(cardinput->getSourceName()));
02105 cardInputs.push_back(cardinput);
02106 }
02107
02108
02109 if (needs_conf)
02110 {
02111 CardInput *newcard = new CardInput(is_dtv, true, true, cardid);
02112 QString newname = QString("DVBInput #%1").arg(list.size() + 1);
02113 newcard->loadByInput(cardid, newname);
02114 inputLabels.push_back(dev_label + " " + QObject::tr("New Input"));
02115 cardInputs.push_back(newcard);
02116 }
02117 }
02118 #endif // USING_DVB
02119 }
02120
02121 int CardUtil::CreateCaptureCard(const QString &videodevice,
02122 const QString &audiodevice,
02123 const QString &vbidevice,
02124 const QString &cardtype,
02125 const uint audioratelimit,
02126 const QString &hostname,
02127 const uint dvb_swfilter,
02128 const uint dvb_sat_type,
02129 bool dvb_wait_for_seqstart,
02130 bool skipbtaudio,
02131 bool dvb_on_demand,
02132 const uint dvb_diseqc_type,
02133 const uint firewire_speed,
02134 const QString &firewire_model,
02135 const uint firewire_connection,
02136 const uint signal_timeout,
02137 const uint channel_timeout,
02138 const uint dvb_tuning_delay,
02139 const uint contrast,
02140 const uint brightness,
02141 const uint colour,
02142 const uint hue,
02143 const uint diseqcid,
02144 bool dvb_eitscan)
02145 {
02146 MSqlQuery query(MSqlQuery::InitCon());
02147
02148 query.prepare(
02149 "INSERT INTO capturecard "
02150 "(videodevice, audiodevice, vbidevice, cardtype, "
02151 "audioratelimit, hostname, dvb_swfilter, dvb_sat_type, "
02152 "dvb_wait_for_seqstart, skipbtaudio, dvb_on_demand, dvb_diseqc_type, "
02153 "firewire_speed, firewire_model, firewire_connection, signal_timeout, "
02154 "channel_timeout, dvb_tuning_delay, contrast, brightness, colour, "
02155 "hue, diseqcid, dvb_eitscan) "
02156 "VALUES (:VIDEODEVICE, :AUDIODEVICE, :VBIDEVICE, :CARDTYPE, "
02157 ":AUDIORATELIMIT, :HOSTNAME, :DVBSWFILTER, :DVBSATTYPE, "
02158 ":DVBWAITFORSEQSTART, :SKIPBTAUDIO, :DVBONDEMAND, :DVBDISEQCTYPE, "
02159 ":FIREWIRESPEED, :FIREWIREMODEL, :FIREWIRECONNECTION, :SIGNALTIMEOUT, "
02160 ":CHANNELTIMEOUT, :DVBTUNINGDELAY, :CONTRAST, :BRIGHTNESS, :COLOUR, "
02161 ":HUE, :DISEQCID, :DVBEITSCAN ) ");
02162
02163 query.bindValue(":VIDEODEVICE", videodevice);
02164 query.bindValue(":AUDIODEVICE", audiodevice);
02165 query.bindValue(":VBIDEVICE", vbidevice);
02166 query.bindValue(":CARDTYPE", cardtype);
02167 query.bindValue(":AUDIORATELIMIT", audioratelimit);
02168 query.bindValue(":HOSTNAME", hostname);
02169 query.bindValue(":DVBSWFILTER", dvb_swfilter);
02170 query.bindValue(":DVBSATTYPE", dvb_sat_type);
02171 query.bindValue(":DVBWAITFORSEQSTART", dvb_wait_for_seqstart);
02172 query.bindValue(":SKIPBTAUDIO", skipbtaudio);
02173 query.bindValue(":DVBONDEMAND", dvb_on_demand);
02174 query.bindValue(":DVBDISEQCTYPE", dvb_diseqc_type);
02175 query.bindValue(":FIREWIRESPEED", firewire_speed);
02176 query.bindValue(":FIREWIREMODEL", firewire_model);
02177 query.bindValue(":FIREWIRECONNECTION", firewire_connection);
02178 query.bindValue(":SIGNALTIMEOUT", signal_timeout);
02179 query.bindValue(":CHANNELTIMEOUT", channel_timeout);
02180 query.bindValue(":DVBTUNINGDELAY", dvb_tuning_delay);
02181 query.bindValue(":CONTRAST", contrast);
02182 query.bindValue(":BRIGHTNESS", brightness);
02183 query.bindValue(":COLOUR", colour);
02184 query.bindValue(":HUE", hue);
02185 query.bindValue(":DISEQCID", diseqcid);
02186 query.bindValue(":DVBEITSCAN", dvb_eitscan);
02187
02188 if (!query.exec())
02189 {
02190 MythDB::DBError("CreateCaptureCard", query);
02191 return -1;
02192 }
02193
02194 query.prepare("SELECT MAX(cardid) FROM capturecard");
02195
02196 if (!query.exec())
02197 {
02198 MythDB::DBError("CreateCaptureCard maxcard", query);
02199 return -1;
02200 }
02201
02202 uint cardid = -1;
02203
02204 if (query.next())
02205 cardid = query.value(0).toUInt();
02206
02207 return cardid;
02208 }
02209
02210 bool CardUtil::DeleteCard(uint cardid)
02211 {
02212 MSqlQuery query(MSqlQuery::InitCon());
02213 bool ok = true;
02214
02215 if (!cardid)
02216 return true;
02217
02218
02219 DiSEqCDevTree tree;
02220 tree.Load(cardid);
02221 if (!tree.Root())
02222 {
02223 tree.SetRoot(NULL);
02224 tree.Store(cardid);
02225 }
02226
02227
02228 QString rawtype = GetRawCardType(cardid);
02229 QString videodevice = GetVideoDevice(cardid);
02230 if (IsTunerSharingCapable(rawtype) && !videodevice.isEmpty())
02231 {
02232 query.prepare(
02233 "SELECT cardid "
02234 "FROM capturecard "
02235 "WHERE videodevice = :DEVICE AND "
02236 " cardid > :CARDID");
02237 query.bindValue(":DEVICE", videodevice);
02238 query.bindValue(":CARDID", cardid);
02239
02240 if (!query.exec())
02241 {
02242 MythDB::DBError("DeleteCard -- find clone cards", query);
02243 return false;
02244 }
02245
02246 while (query.next())
02247 ok &= DeleteCard(query.value(0).toUInt());
02248
02249 if (!ok)
02250 return false;
02251 }
02252
02253
02254 vector<uint> inputs = CardUtil::GetInputIDs(cardid);
02255 for (uint i = 0; i < inputs.size(); i++)
02256 ok &= CardUtil::DeleteInput(inputs[i]);
02257
02258 if (!ok)
02259 return false;
02260
02261
02262 query.prepare("DELETE FROM capturecard WHERE cardid = :CARDID");
02263 query.bindValue(":CARDID", cardid);
02264
02265 if (!query.exec())
02266 {
02267 MythDB::DBError("DeleteCard -- delete row", query);
02268 ok = false;
02269 }
02270
02271 if (ok)
02272 {
02273
02274 DeleteOrphanInputs();
02275 UnlinkInputGroup(0,0);
02276 }
02277
02278 return ok;
02279 }
02280
02281 bool CardUtil::DeleteAllCards(void)
02282 {
02283 MSqlQuery query(MSqlQuery::InitCon());
02284 return (query.exec("TRUNCATE TABLE inputgroup") &&
02285 query.exec("TRUNCATE TABLE diseqc_config") &&
02286 query.exec("TRUNCATE TABLE diseqc_tree") &&
02287 query.exec("TRUNCATE TABLE cardinput") &&
02288 query.exec("TRUNCATE TABLE capturecard"));
02289 }
02290
02291 vector<uint> CardUtil::GetCardList(void)
02292 {
02293 vector<uint> list;
02294
02295 MSqlQuery query(MSqlQuery::InitCon());
02296 query.prepare(
02297 "SELECT cardid "
02298 "FROM capturecard "
02299 "ORDER BY cardid");
02300
02301 if (!query.exec())
02302 MythDB::DBError("CardUtil::GetCardList()", query);
02303 else
02304 {
02305 while (query.next())
02306 list.push_back(query.value(0).toUInt());
02307 }
02308
02309 return list;
02310 }
02311
02312
02313 QString CardUtil::GetDeviceName(dvb_dev_type_t type, const QString &device)
02314 {
02315 QString devname = QString(device);
02316
02317 if (DVB_DEV_FRONTEND == type)
02318 return devname;
02319 else if (DVB_DEV_DVR == type)
02320 return devname.replace(devname.indexOf("frontend"), 8, "dvr");
02321 else if (DVB_DEV_DEMUX == type)
02322 return devname.replace(devname.indexOf("frontend"), 8, "demux");
02323 else if (DVB_DEV_CA == type)
02324 return devname.replace(devname.indexOf("frontend"), 8, "ca");
02325 else if (DVB_DEV_AUDIO == type)
02326 return devname.replace(devname.indexOf("frontend"), 8, "audio");
02327 else if (DVB_DEV_VIDEO == type)
02328 return devname.replace(devname.indexOf("frontend"), 8, "video");
02329
02330 return "";
02331 }
02332
02342 bool CardUtil::HDHRdoesDVB(const QString &device)
02343 {
02344 (void) device;
02345
02346 #ifdef USING_HDHOMERUN
02347 hdhomerun_device_t *hdhr;
02348 hdhr = hdhomerun_device_create_from_str(device.toAscii(), NULL);
02349 if (!hdhr)
02350 return false;
02351
02352 const char *model = hdhomerun_device_get_model_str(hdhr);
02353 if (model && strstr(model, "dvb"))
02354 return true;
02355 #endif
02356
02357 return false;
02358 }
02359
02364 QString CardUtil::GetHDHRdesc(const QString &device)
02365 {
02366 QString connectErr = QObject::tr("Unable to connect to device.");
02367
02368 #ifdef USING_HDHOMERUN
02369 bool deviceIsIP = false;
02370 uint32_t dev;
02371
02372 if (device.contains('.'))
02373 deviceIsIP = true;
02374 else
02375 {
02376 bool validID;
02377
02378 dev = device.toUInt(&validID, 16);
02379 if (!validID || !hdhomerun_discover_validate_device_id(dev))
02380 return QObject::tr("Invalid Device ID");
02381 }
02382
02383
02384 LOG(VB_GENERAL, LOG_INFO, "CardUtil::GetHDHRdescription(" + device +
02385 ") - trying to locate device");
02386
02387 hdhomerun_device_t *hdhr;
02388 hdhr = hdhomerun_device_create_from_str(device.toAscii(), NULL);
02389 if (!hdhr)
02390 return QObject::tr("Invalid Device ID or address.");
02391
02392 const char *model = hdhomerun_device_get_model_str(hdhr);
02393 if (!model)
02394 return connectErr;
02395
02396
02397 QString description = model;
02398 char *sVersion;
02399 uint32_t iVersion;
02400
02401 if (hdhomerun_device_get_version(hdhr, &sVersion, &iVersion))
02402 description += QObject::tr(", firmware: %2").arg(sVersion);
02403
02404 return description;
02405 #else
02406
02407 (void) device;
02408 return connectErr;
02409 #endif
02410 }
02411
02412 #ifdef USING_ASI
02413 static QString sys_dev(uint device_num, QString dev)
02414 {
02415 return QString("/sys/class/asi/asirx%1/%2").arg(device_num).arg(dev);
02416 }
02417
02418 static QString read_sys(QString sys_dev)
02419 {
02420 QFile f(sys_dev);
02421 f.open(QIODevice::ReadOnly);
02422 QByteArray sdba = f.readAll();
02423 f.close();
02424 return sdba;
02425 }
02426
02427 static bool write_sys(QString sys_dev, QString str)
02428 {
02429 QFile f(sys_dev);
02430 f.open(QIODevice::WriteOnly);
02431 QByteArray ba = str.toLocal8Bit();
02432 qint64 offset = 0;
02433 for (uint tries = 0; (offset < ba.size()) && tries < 5; tries++)
02434 {
02435 qint64 written = f.write(ba.data()+offset, ba.size()-offset);
02436 if (written < 0)
02437 return false;
02438 offset += written;
02439 }
02440 return true;
02441 }
02442 #endif
02443
02444 int CardUtil::GetASIDeviceNumber(const QString &device, QString *error)
02445 {
02446 #ifdef USING_ASI
02447
02448 struct stat statbuf;
02449 memset(&statbuf, 0, sizeof(statbuf));
02450 if (stat(device.toLocal8Bit().constData(), &statbuf) < 0)
02451 {
02452 if (error)
02453 *error = QString("Unable to stat '%1'").arg(device) + ENO;
02454 return -1;
02455 }
02456
02457 if (!S_ISCHR(statbuf.st_mode))
02458 {
02459 if (error)
02460 *error = QString("'%1' is not a character device").arg(device);
02461 return -1;
02462 }
02463
02464 if (!(statbuf.st_rdev & 0x0080))
02465 {
02466 if (error)
02467 *error = QString("'%1' not a DVEO ASI receiver").arg(device);
02468 return -1;
02469 }
02470
02471 int device_num = statbuf.st_rdev & 0x007f;
02472
02473
02474 QString sys_dev_contents = read_sys(sys_dev(device_num, "dev"));
02475 QStringList sys_dev_clist = sys_dev_contents.split(":");
02476 if (2 != sys_dev_clist.size())
02477 {
02478 if (error)
02479 {
02480 *error = QString("Unable to read '%1'")
02481 .arg(sys_dev(device_num, "dev"));
02482 }
02483 return -1;
02484 }
02485 if (sys_dev_clist[0].toUInt() != (statbuf.st_rdev>>8))
02486 {
02487 if (error)
02488 *error = QString("'%1' not a DVEO ASI device").arg(device);
02489 return -1;
02490 }
02491
02492 return device_num;
02493 #else
02494 (void) device;
02495 if (error)
02496 *error = "Not compiled with ASI support.";
02497 return -1;
02498 #endif
02499 }
02500
02501 uint CardUtil::GetASIBufferSize(uint device_num, QString *error)
02502 {
02503 #ifdef USING_ASI
02504
02505 QString sys_bufsize_contents = read_sys(sys_dev(device_num, "bufsize"));
02506 bool ok;
02507 uint buf_size = sys_bufsize_contents.toUInt(&ok);
02508 if (!ok)
02509 {
02510 if (error)
02511 {
02512 *error = QString("Failed to read buffer size from '%1'")
02513 .arg(sys_dev(device_num, "bufsize"));
02514 }
02515 return 0;
02516 }
02517 return buf_size;
02518 #else
02519 (void) device_num;
02520 if (error)
02521 *error = "Not compiled with ASI support.";
02522 return 0;
02523 #endif
02524 }
02525
02526 int CardUtil::GetASIMode(uint device_num, QString *error)
02527 {
02528 #ifdef USING_ASI
02529 QString sys_bufsize_contents = read_sys(sys_dev(device_num, "mode"));
02530 bool ok;
02531 uint mode = sys_bufsize_contents.toUInt(&ok);
02532 if (!ok)
02533 {
02534 if (error)
02535 {
02536 *error = QString("Failed to read mode from '%1'")
02537 .arg(sys_dev(device_num, "mode"));
02538 }
02539 return -1;
02540 }
02541 return mode;
02542 #else
02543 (void) device_num;
02544 if (error)
02545 *error = "Not compiled with ASI support.";
02546 return -1;
02547 #endif
02548 }
02549
02550 bool CardUtil::SetASIMode(uint device_num, uint mode, QString *error)
02551 {
02552 #ifdef USING_ASI
02553 QString sys_bufsize_contents = read_sys(sys_dev(device_num, "mode"));
02554 bool ok;
02555 uint old_mode = sys_bufsize_contents.toUInt(&ok);
02556 if (ok && old_mode == mode)
02557 return true;
02558 ok = write_sys(sys_dev(device_num, "mode"), QString("%1\n").arg(mode));
02559 if (!ok && error)
02560 {
02561 *error = QString("Failed to set mode to %1 using '%2'")
02562 .arg(mode).arg(sys_dev(device_num, "mode"));
02563 }
02564 return ok;
02565 #else
02566 (void) device_num;
02567 if (error)
02568 *error = "Not compiled with ASI support.";
02569 return false;
02570 #endif
02571 }