|
MythTV
0.26-pre
|
00001 // Standard UNIX C headers 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 // Qt headers 00012 #include <QMap> 00013 #include <QDir> 00014 00015 // MythTV headers 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 // if no OOB tuner, oob will be < 1. If no CC present, OOB 00112 // status will be "none." 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 // TODO FIXME implement detection of Cablecard presence 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 // Return "deviceid ipaddress" pairs 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 // TODO implement CETON probing. 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") || // munges PMT 00483 (name == "ST STV0299 DVB-S")); // munges PAT 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 // copy data from src[i] to dst[match] 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 // create new input for dst with data from src 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 // copy input group linkages 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 // clone diseqc_config (just points to the same diseqc_tree row) 00911 DiSEqCDevSettings diseqc; 00912 if (diseqc.Load(src_inputs[i])) 00913 diseqc.Store(dst_inputid); 00914 } 00915 00916 // delete extra inputs in dst 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 // Make sure the card's inputs are all in a single 01484 // input group, create one if needed. 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 // First try V4L2 query 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 // Fallback to V4L1 query 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 // V4L v2 query 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 // V4L v1 query 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 // Create an input on single input cards that don't advertise input 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 // V4L v2 query 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 // plus add one "new" input 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 // delete any DiSEqC device tree 02219 DiSEqCDevTree tree; 02220 tree.Load(cardid); 02221 if (!tree.Root()) 02222 { 02223 tree.SetRoot(NULL); 02224 tree.Store(cardid); 02225 } 02226 02227 // delete any clones 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 // delete inputs 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 // actually delete the capturecard row for this card 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 // delete any orphaned inputs & unused input groups 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('.')) // Simplistic check, but also allows DNS names 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 // basic confirmation 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 // extra confirmation 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 // get the buffer size 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 }
1.7.6.1