|
MythTV
0.26-pre
|
00001 // -*- Mode: c++ -*- 00002 00003 // Standard UNIX C headers 00004 #include <unistd.h> 00005 #include <fcntl.h> 00006 #include <sys/types.h> 00007 #include <sys/stat.h> 00008 00009 // C++ headers 00010 #include <algorithm> 00011 using namespace std; 00012 00013 // Qt headers 00014 #include <QCoreApplication> 00015 #include <QTextStream> 00016 #include <QStringList> 00017 #include <QCursor> 00018 #include <QLayout> 00019 #include <QFile> 00020 #include <QMap> 00021 #include <QDir> 00022 #include <QDateTime> 00023 00024 // MythTV headers 00025 #include "mythconfig.h" 00026 #include "mythwidgets.h" 00027 #include "mythdialogs.h" 00028 #include "mythcorecontext.h" 00029 #include "videosource.h" 00030 #include "datadirect.h" 00031 #include "scanwizard.h" 00032 #include "cardutil.h" 00033 #include "sourceutil.h" 00034 #include "channelutil.h" 00035 #include "frequencies.h" 00036 #include "diseqcsettings.h" 00037 #include "firewiredevice.h" 00038 #include "compat.h" 00039 #include "mythdb.h" 00040 #include "mythdirs.h" 00041 #include "mythlogging.h" 00042 #include "libmythupnp/httprequest.h" // for TestMimeType() 00043 #include "mythsystem.h" 00044 #include "exitcodes.h" 00045 00046 #ifdef USING_DVB 00047 #include "dvbtypes.h" 00048 #endif 00049 00050 #ifdef USING_V4L2 00051 #include <linux/videodev2.h> 00052 #endif 00053 00054 #ifdef USING_HDHOMERUN 00055 #include "hdhomerun.h" 00056 #endif 00057 00058 static const uint kDefaultMultirecCount = 2; 00059 00060 VideoSourceSelector::VideoSourceSelector(uint _initial_sourceid, 00061 const QString &_card_types, 00062 bool _must_have_mplexid) : 00063 ComboBoxSetting(this), 00064 initial_sourceid(_initial_sourceid), 00065 card_types(_card_types), 00066 must_have_mplexid(_must_have_mplexid) 00067 { 00068 card_types.detach(); 00069 setLabel(tr("Video Source")); 00070 } 00071 00072 void VideoSourceSelector::Load(void) 00073 { 00074 MSqlQuery query(MSqlQuery::InitCon()); 00075 00076 QString querystr = 00077 "SELECT DISTINCT videosource.name, videosource.sourceid " 00078 "FROM cardinput, videosource, capturecard"; 00079 00080 querystr += (must_have_mplexid) ? ", channel " : " "; 00081 00082 querystr += 00083 "WHERE cardinput.sourceid = videosource.sourceid AND " 00084 " cardinput.cardid = capturecard.cardid AND " 00085 " capturecard.hostname = :HOSTNAME "; 00086 00087 if (!card_types.isEmpty()) 00088 { 00089 querystr += QString(" AND capturecard.cardtype in %1 ") 00090 .arg(card_types); 00091 } 00092 00093 if (must_have_mplexid) 00094 { 00095 querystr += 00096 " AND channel.sourceid = videosource.sourceid " 00097 " AND channel.mplexid != 32767 " 00098 " AND channel.mplexid != 0 "; 00099 } 00100 00101 query.prepare(querystr); 00102 query.bindValue(":HOSTNAME", gCoreContext->GetHostName()); 00103 00104 if (!query.exec() || !query.isActive() || query.size() <= 0) 00105 return; 00106 00107 uint sel = 0, cnt = 0; 00108 for (; query.next(); cnt++) 00109 { 00110 addSelection(query.value(0).toString(), 00111 query.value(1).toString()); 00112 00113 sel = (query.value(1).toUInt() == initial_sourceid) ? cnt : sel; 00114 } 00115 00116 if (initial_sourceid) 00117 { 00118 if (cnt) 00119 setValue(sel); 00120 setEnabled(false); 00121 } 00122 } 00123 00124 class InstanceCount : public TransSpinBoxSetting 00125 { 00126 public: 00127 InstanceCount(const CaptureCard &parent) : TransSpinBoxSetting(1, 5, 1) 00128 { 00129 setLabel(QObject::tr("Max recordings")); 00130 setHelpText( 00131 QObject::tr( 00132 "Maximum number of simultaneous recordings this device " 00133 "should make. Some digital transmitters transmit multiple " 00134 "programs on a multiplex, if this is set to a value greater " 00135 "than one MythTV can sometimes take advantage of this.")); 00136 uint cnt = parent.GetInstanceCount(); 00137 cnt = (!cnt) ? kDefaultMultirecCount : ((cnt < 1) ? 1 : cnt); 00138 setValue(cnt); 00139 }; 00140 }; 00141 00142 QString VideoSourceDBStorage::GetWhereClause(MSqlBindings &bindings) const 00143 { 00144 QString sourceidTag(":WHERESOURCEID"); 00145 00146 QString query("sourceid = " + sourceidTag); 00147 00148 bindings.insert(sourceidTag, m_parent.getSourceID()); 00149 00150 return query; 00151 } 00152 00153 QString VideoSourceDBStorage::GetSetClause(MSqlBindings& bindings) const 00154 { 00155 QString sourceidTag(":SETSOURCEID"); 00156 QString colTag(":SET" + GetColumnName().toUpper()); 00157 00158 QString query("sourceid = " + sourceidTag + ", " + 00159 GetColumnName() + " = " + colTag); 00160 00161 bindings.insert(sourceidTag, m_parent.getSourceID()); 00162 bindings.insert(colTag, user->GetDBValue()); 00163 00164 return query; 00165 } 00166 00167 QString CaptureCardDBStorage::GetWhereClause(MSqlBindings& bindings) const 00168 { 00169 QString cardidTag(":WHERECARDID"); 00170 00171 QString query("cardid = " + cardidTag); 00172 00173 bindings.insert(cardidTag, m_parent.getCardID()); 00174 00175 return query; 00176 } 00177 00178 QString CaptureCardDBStorage::GetSetClause(MSqlBindings& bindings) const 00179 { 00180 QString cardidTag(":SETCARDID"); 00181 QString colTag(":SET" + GetColumnName().toUpper()); 00182 00183 QString query("cardid = " + cardidTag + ", " + 00184 GetColumnName() + " = " + colTag); 00185 00186 bindings.insert(cardidTag, m_parent.getCardID()); 00187 bindings.insert(colTag, user->GetDBValue()); 00188 00189 return query; 00190 } 00191 00192 class XMLTVGrabber : public ComboBoxSetting, public VideoSourceDBStorage 00193 { 00194 public: 00195 XMLTVGrabber(const VideoSource &parent) : 00196 ComboBoxSetting(this), 00197 VideoSourceDBStorage(this, parent, "xmltvgrabber") 00198 { 00199 setLabel(QObject::tr("Listings grabber")); 00200 }; 00201 }; 00202 00203 FreqTableSelector::FreqTableSelector(const VideoSource &parent) : 00204 ComboBoxSetting(this), VideoSourceDBStorage(this, parent, "freqtable") 00205 { 00206 setLabel(QObject::tr("Channel frequency table")); 00207 addSelection("default"); 00208 00209 for (uint i = 0; chanlists[i].name; i++) 00210 addSelection(chanlists[i].name); 00211 00212 setHelpText(QObject::tr("Use default unless this source uses a " 00213 "different frequency table than the system wide table " 00214 "defined in the General settings.")); 00215 } 00216 00217 TransFreqTableSelector::TransFreqTableSelector(uint _sourceid) : 00218 ComboBoxSetting(this), sourceid(_sourceid), 00219 loaded_freq_table(QString::null) 00220 { 00221 setLabel(QObject::tr("Channel frequency table")); 00222 00223 for (uint i = 0; chanlists[i].name; i++) 00224 addSelection(chanlists[i].name); 00225 } 00226 00227 void TransFreqTableSelector::Load(void) 00228 { 00229 int idx = getValueIndex(gCoreContext->GetSetting("FreqTable")); 00230 if (idx >= 0) 00231 setValue(idx); 00232 00233 if (!sourceid) 00234 return; 00235 00236 MSqlQuery query(MSqlQuery::InitCon()); 00237 query.prepare( 00238 "SELECT freqtable " 00239 "FROM videosource " 00240 "WHERE sourceid = :SOURCEID"); 00241 query.bindValue(":SOURCEID", sourceid); 00242 00243 if (!query.exec() || !query.isActive()) 00244 { 00245 MythDB::DBError("TransFreqTableSelector::load", query); 00246 return; 00247 } 00248 00249 loaded_freq_table = QString::null; 00250 00251 if (query.next()) 00252 { 00253 loaded_freq_table = query.value(0).toString(); 00254 if (!loaded_freq_table.isEmpty() && 00255 (loaded_freq_table.toLower() != "default")) 00256 { 00257 int idx = getValueIndex(loaded_freq_table); 00258 if (idx >= 0) 00259 setValue(idx); 00260 } 00261 } 00262 } 00263 00264 void TransFreqTableSelector::Save(void) 00265 { 00266 LOG(VB_GENERAL, LOG_INFO, "TransFreqTableSelector::Save(void)"); 00267 00268 if ((loaded_freq_table == getValue()) || 00269 ((loaded_freq_table.toLower() == "default") && 00270 (getValue() == gCoreContext->GetSetting("FreqTable")))) 00271 { 00272 return; 00273 } 00274 00275 MSqlQuery query(MSqlQuery::InitCon()); 00276 query.prepare( 00277 "UPDATE videosource " 00278 "SET freqtable = :FREQTABLE " 00279 "WHERE sourceid = :SOURCEID"); 00280 00281 query.bindValue(":FREQTABLE", getValue()); 00282 query.bindValue(":SOURCEID", sourceid); 00283 00284 if (!query.exec() || !query.isActive()) 00285 { 00286 MythDB::DBError("TransFreqTableSelector::load", query); 00287 return; 00288 } 00289 } 00290 00291 void TransFreqTableSelector::SetSourceID(uint _sourceid) 00292 { 00293 sourceid = _sourceid; 00294 Load(); 00295 } 00296 00297 class UseEIT : public CheckBoxSetting, public VideoSourceDBStorage 00298 { 00299 public: 00300 UseEIT(const VideoSource &parent) : 00301 CheckBoxSetting(this), VideoSourceDBStorage(this, parent, "useeit") 00302 { 00303 setLabel(QObject::tr("Perform EIT scan")); 00304 setHelpText(QObject::tr( 00305 "If enabled, program guide data for channels on this " 00306 "source will be updated with data provided by the " 00307 "channels themselves 'Over-the-Air'.")); 00308 } 00309 }; 00310 00311 class DataDirectUserID : public LineEditSetting, public VideoSourceDBStorage 00312 { 00313 public: 00314 DataDirectUserID(const VideoSource &parent) : 00315 LineEditSetting(this), VideoSourceDBStorage(this, parent, "userid") 00316 { 00317 setLabel(QObject::tr("User ID")); 00318 } 00319 }; 00320 00321 class DataDirectPassword : public LineEditSetting, public VideoSourceDBStorage 00322 { 00323 public: 00324 DataDirectPassword(const VideoSource &parent) : 00325 LineEditSetting(this, true), 00326 VideoSourceDBStorage(this, parent, "password") 00327 { 00328 SetPasswordEcho(true); 00329 setLabel(QObject::tr("Password")); 00330 } 00331 }; 00332 00333 void DataDirectLineupSelector::fillSelections(const QString &uid, 00334 const QString &pwd, 00335 int _source) 00336 { 00337 (void) uid; 00338 (void) pwd; 00339 #ifdef USING_BACKEND 00340 if (uid.isEmpty() || pwd.isEmpty()) 00341 return; 00342 00343 qApp->processEvents(); 00344 00345 DataDirectProcessor ddp(_source, uid, pwd); 00346 QString waitMsg = tr("Fetching lineups from %1...") 00347 .arg(ddp.GetListingsProviderName()); 00348 00349 LOG(VB_GENERAL, LOG_INFO, waitMsg); 00350 MythProgressDialog *pdlg = new MythProgressDialog(waitMsg, 2); 00351 00352 clearSelections(); 00353 00354 pdlg->setProgress(1); 00355 00356 if (!ddp.GrabLineupsOnly()) 00357 { 00358 LOG(VB_GENERAL, LOG_ERR, 00359 "DDLS: fillSelections did not successfully load selections"); 00360 pdlg->deleteLater(); 00361 return; 00362 } 00363 const DDLineupList lineups = ddp.GetLineups(); 00364 00365 DDLineupList::const_iterator it; 00366 for (it = lineups.begin(); it != lineups.end(); ++it) 00367 addSelection((*it).displayname, (*it).lineupid); 00368 00369 pdlg->setProgress(2); 00370 pdlg->Close(); 00371 pdlg->deleteLater(); 00372 #else // USING_BACKEND 00373 LOG(VB_GENERAL, LOG_ERR, 00374 "You must compile the backend to set up a DataDirect line-up"); 00375 #endif // USING_BACKEND 00376 } 00377 00378 void DataDirect_config::Load() 00379 { 00380 VerticalConfigurationGroup::Load(); 00381 bool is_sd_userid = userid->getValue().contains('@') > 0; 00382 bool match = ((is_sd_userid && (source == DD_SCHEDULES_DIRECT)) || 00383 (!is_sd_userid && (source == DD_ZAP2IT))); 00384 if (((userid->getValue() != lastloadeduserid) || 00385 (password->getValue() != lastloadedpassword)) && match) 00386 { 00387 lineupselector->fillSelections(userid->getValue(), 00388 password->getValue(), 00389 source); 00390 lastloadeduserid = userid->getValue(); 00391 lastloadedpassword = password->getValue(); 00392 } 00393 } 00394 00395 DataDirect_config::DataDirect_config(const VideoSource& _parent, int _source) : 00396 VerticalConfigurationGroup(false, false, false, false), 00397 parent(_parent) 00398 { 00399 source = _source; 00400 00401 HorizontalConfigurationGroup *up = 00402 new HorizontalConfigurationGroup(false, false, true, true); 00403 00404 up->addChild(userid = new DataDirectUserID(parent)); 00405 addChild(up); 00406 00407 HorizontalConfigurationGroup *lp = 00408 new HorizontalConfigurationGroup(false, false, true, true); 00409 00410 lp->addChild(password = new DataDirectPassword(parent)); 00411 lp->addChild(button = new DataDirectButton()); 00412 addChild(lp); 00413 00414 addChild(lineupselector = new DataDirectLineupSelector(parent)); 00415 addChild(new UseEIT(parent)); 00416 00417 connect(button, SIGNAL(pressed()), 00418 this, SLOT(fillDataDirectLineupSelector())); 00419 } 00420 00421 void DataDirect_config::fillDataDirectLineupSelector(void) 00422 { 00423 lineupselector->fillSelections( 00424 userid->getValue(), password->getValue(), source); 00425 } 00426 00427 XMLTV_generic_config::XMLTV_generic_config(const VideoSource& _parent, 00428 QString _grabber) : 00429 VerticalConfigurationGroup(false, false, false, false), 00430 parent(_parent), grabber(_grabber) 00431 { 00432 QString filename = QString("%1/%2.xmltv") 00433 .arg(GetConfDir()).arg(parent.getSourceName()); 00434 00435 grabberArgs.push_back("--config-file"); 00436 grabberArgs.push_back(filename); 00437 grabberArgs.push_back("--configure"); 00438 00439 addChild(new UseEIT(parent)); 00440 00441 TransButtonSetting *config = new TransButtonSetting(); 00442 config->setLabel(tr("Configure")); 00443 config->setHelpText(tr("Run XMLTV configure command.")); 00444 00445 addChild(config); 00446 00447 connect(config, SIGNAL(pressed()), SLOT(RunConfig())); 00448 } 00449 00450 void XMLTV_generic_config::Save() 00451 { 00452 VerticalConfigurationGroup::Save(); 00453 #if 0 00454 QString err_msg = QObject::tr( 00455 "You MUST run 'mythfilldatabase --manual' the first time,\n" 00456 "instead of just 'mythfilldatabase'.\nYour grabber does not provide " 00457 "channel numbers, so you have to set them manually."); 00458 00459 if (is_grabber_external(grabber)) 00460 { 00461 LOG(VB_GENERAL, LOG_ERR, err_msg); 00462 MythPopupBox::showOkPopup( 00463 GetMythMainWindow(), QObject::tr("Warning."), err_msg); 00464 } 00465 #endif 00466 } 00467 00468 void XMLTV_generic_config::RunConfig(void) 00469 { 00470 TerminalWizard *tw = new TerminalWizard(grabber, grabberArgs); 00471 tw->exec(false, true); 00472 delete tw; 00473 } 00474 00475 EITOnly_config::EITOnly_config(const VideoSource& _parent) : 00476 VerticalConfigurationGroup(false, false, true, true) 00477 { 00478 useeit = new UseEIT(_parent); 00479 useeit->setValue(true); 00480 useeit->setVisible(false); 00481 addChild(useeit); 00482 00483 TransLabelSetting *label; 00484 label=new TransLabelSetting(); 00485 label->setValue(QObject::tr("Use only the transmitted guide data.")); 00486 addChild(label); 00487 label=new TransLabelSetting(); 00488 label->setValue( 00489 QObject::tr("This will usually only work with ATSC or DVB channels,")); 00490 addChild(label); 00491 label=new TransLabelSetting(); 00492 label->setValue( 00493 QObject::tr("and generally provides data only for the next few days.")); 00494 addChild(label); 00495 } 00496 00497 void EITOnly_config::Save(void) 00498 { 00499 // Force this value on 00500 useeit->setValue(true); 00501 useeit->Save(); 00502 } 00503 00504 NoGrabber_config::NoGrabber_config(const VideoSource& _parent) : 00505 VerticalConfigurationGroup(false, false, false, false) 00506 { 00507 useeit = new UseEIT(_parent); 00508 useeit->setValue(false); 00509 useeit->setVisible(false); 00510 addChild(useeit); 00511 00512 TransLabelSetting *label = new TransLabelSetting(); 00513 label->setValue(QObject::tr("Do not configure a grabber")); 00514 addChild(label); 00515 } 00516 00517 void NoGrabber_config::Save(void) 00518 { 00519 useeit->setValue(false); 00520 useeit->Save(); 00521 } 00522 00523 00524 XMLTVConfig::XMLTVConfig(const VideoSource &aparent) : 00525 TriggeredConfigurationGroup(false, true, false, false), 00526 parent(aparent), grabber(new XMLTVGrabber(parent)) 00527 { 00528 addChild(grabber); 00529 setTrigger(grabber); 00530 00531 // only save settings for the selected grabber 00532 setSaveAll(false); 00533 00534 } 00535 00536 void XMLTVConfig::Load(void) 00537 { 00538 addTarget("schedulesdirect1", 00539 new DataDirect_config(parent, DD_SCHEDULES_DIRECT)); 00540 addTarget("eitonly", new EITOnly_config(parent)); 00541 addTarget("/bin/true", new NoGrabber_config(parent)); 00542 00543 grabber->addSelection( 00544 QObject::tr("North America (SchedulesDirect.org) (Internal)"), 00545 "schedulesdirect1"); 00546 00547 grabber->addSelection( 00548 QObject::tr("Transmitted guide only (EIT)"), "eitonly"); 00549 00550 grabber->addSelection(QObject::tr("No grabber"), "/bin/true"); 00551 00552 QString validValues; 00553 validValues += "schedulesdirect1"; 00554 validValues += "eitonly"; 00555 validValues += "/bin/true"; 00556 00557 QString gname, d1, d2, d3; 00558 SourceUtil::GetListingsLoginData(parent.getSourceID(), gname, d1, d2, d3); 00559 00560 QString loc = "XMLTVConfig::Load: "; 00561 QString loc_err = "XMLTVConfig::Load, Error: "; 00562 00563 QStringList name_list; 00564 QStringList prog_list; 00565 00566 QStringList args; 00567 args += "baseline"; 00568 00569 MythSystem find_grabber_proc("tv_find_grabbers", args, 00570 kMSStdOut | kMSBuffered | kMSRunShell); 00571 find_grabber_proc.Run(25); 00572 LOG(VB_GENERAL, LOG_INFO, 00573 loc + "Running 'tv_find_grabbers " + args.join(" ") + "'."); 00574 uint status = find_grabber_proc.Wait(); 00575 00576 if (status == GENERIC_EXIT_OK) 00577 { 00578 QTextStream ostream(find_grabber_proc.ReadAll()); 00579 while (!ostream.atEnd()) 00580 { 00581 QString grabber_list(ostream.readLine()); 00582 QStringList grabber_split = 00583 grabber_list.split("|", QString::SkipEmptyParts); 00584 QString grabber_name = grabber_split[1] + " (xmltv)"; 00585 QFileInfo grabber_file(grabber_split[0]); 00586 00587 name_list.push_back(grabber_name); 00588 prog_list.push_back(grabber_file.fileName()); 00589 LOG(VB_GENERAL, LOG_DEBUG, "Found " + grabber_split[0]); 00590 } 00591 LOG(VB_GENERAL, LOG_INFO, loc + "Finished running tv_find_grabbers"); 00592 } 00593 else 00594 LOG(VB_GENERAL, LOG_ERR, loc + "Failed to run tv_find_grabbers"); 00595 00596 LoadXMLTVGrabbers(name_list, prog_list); 00597 00598 TriggeredConfigurationGroup::Load(); 00599 00600 } 00601 00602 void XMLTVConfig::LoadXMLTVGrabbers( 00603 QStringList name_list, QStringList prog_list) 00604 { 00605 if (name_list.size() != prog_list.size()) 00606 return; 00607 00608 QString selValue = grabber->getValue(); 00609 int selIndex = grabber->getValueIndex(selValue); 00610 grabber->setValue(0); 00611 00612 QString validValues; 00613 validValues += "schedulesdirect1"; 00614 validValues += "eitonly"; 00615 validValues += "/bin/true"; 00616 00617 for (uint i = 0; i < grabber->size(); i++) 00618 { 00619 if (!validValues.contains(grabber->GetValue(i))) 00620 { 00621 removeTarget(grabber->GetValue(i)); 00622 i--; 00623 } 00624 } 00625 00626 for (uint i = 0; i < (uint) name_list.size(); i++) 00627 { 00628 addTarget(prog_list[i], 00629 new XMLTV_generic_config(parent, prog_list[i])); 00630 grabber->addSelection(name_list[i], prog_list[i]); 00631 } 00632 00633 if (!selValue.isEmpty()) 00634 selIndex = grabber->getValueIndex(selValue); 00635 if (selIndex >= 0) 00636 grabber->setValue(selIndex); 00637 00638 } 00639 00640 void XMLTVConfig::Save(void) 00641 { 00642 TriggeredConfigurationGroup::Save(); 00643 MSqlQuery query(MSqlQuery::InitCon()); 00644 query.prepare( 00645 "UPDATE videosource " 00646 "SET userid=NULL, password=NULL " 00647 "WHERE xmltvgrabber NOT IN ( 'datadirect', 'technovera', " 00648 " 'schedulesdirect1' )"); 00649 if (!query.exec()) 00650 MythDB::DBError("XMLTVConfig::Save", query); 00651 } 00652 00653 VideoSource::VideoSource() 00654 { 00655 // must be first 00656 addChild(id = new ID()); 00657 00658 ConfigurationGroup *group = new VerticalConfigurationGroup(false, false); 00659 group->setLabel(QObject::tr("Video Source Setup")); 00660 group->addChild(name = new Name(*this)); 00661 group->addChild(xmltv = new XMLTVConfig(*this)); 00662 group->addChild(new FreqTableSelector(*this)); 00663 addChild(group); 00664 } 00665 00666 bool VideoSourceEditor::cardTypesInclude(const int &sourceID, 00667 const QString &thecardtype) 00668 { 00669 MSqlQuery query(MSqlQuery::InitCon()); 00670 query.prepare("SELECT count(cardtype)" 00671 " FROM cardinput,capturecard " 00672 " WHERE capturecard.cardid = cardinput.cardid " 00673 " AND cardinput.sourceid= :SOURCEID " 00674 " AND capturecard.cardtype= :CARDTYPE ;"); 00675 query.bindValue(":SOURCEID", sourceID); 00676 query.bindValue(":CARDTYPE", thecardtype); 00677 00678 if (query.exec() && query.next()) 00679 { 00680 int count = query.value(0).toInt(); 00681 00682 if (count > 0) 00683 return true; 00684 } 00685 00686 return false; 00687 } 00688 00689 void VideoSource::fillSelections(SelectSetting* setting) 00690 { 00691 MSqlQuery result(MSqlQuery::InitCon()); 00692 result.prepare("SELECT name, sourceid FROM videosource;"); 00693 00694 if (result.exec() && result.isActive() && result.size() > 0) 00695 { 00696 while (result.next()) 00697 { 00698 setting->addSelection(result.value(0).toString(), 00699 result.value(1).toString()); 00700 } 00701 } 00702 } 00703 00704 void VideoSource::loadByID(int sourceid) 00705 { 00706 id->setValue(sourceid); 00707 } 00708 00709 class VideoDevice : public PathSetting, public CaptureCardDBStorage 00710 { 00711 public: 00712 VideoDevice(const CaptureCard &parent, 00713 uint minor_min = 0, 00714 uint minor_max = UINT_MAX, 00715 QString card = QString::null, 00716 QString driver = QString::null) : 00717 PathSetting(this, true), 00718 CaptureCardDBStorage(this, parent, "videodevice") 00719 { 00720 setLabel(QObject::tr("Video device")); 00721 00722 // /dev/v4l/video* 00723 QDir dev("/dev/v4l", "video*", QDir::Name, QDir::System); 00724 fillSelectionsFromDir(dev, minor_min, minor_max, 00725 card, driver, false); 00726 00727 // /dev/video* 00728 dev.setPath("/dev"); 00729 fillSelectionsFromDir(dev, minor_min, minor_max, 00730 card, driver, false); 00731 00732 // /dev/dtv/video* 00733 dev.setPath("/dev/dtv"); 00734 fillSelectionsFromDir(dev, minor_min, minor_max, 00735 card, driver, false); 00736 00737 // /dev/dtv* 00738 dev.setPath("/dev"); 00739 dev.setNameFilters(QStringList("dtv*")); 00740 fillSelectionsFromDir(dev, minor_min, minor_max, 00741 card, driver, false); 00742 }; 00743 00744 uint fillSelectionsFromDir(const QDir& dir, 00745 uint minor_min, uint minor_max, 00746 QString card, QString driver, 00747 bool allow_duplicates) 00748 { 00749 uint cnt = 0; 00750 00751 QFileInfoList il = dir.entryInfoList(); 00752 QRegExp *driverExp = NULL; 00753 if (!driver.isEmpty()) 00754 driverExp = new QRegExp(driver); 00755 00756 for( QFileInfoList::iterator it = il.begin(); 00757 it != il.end(); 00758 ++it ) 00759 { 00760 QFileInfo &fi = *it; 00761 00762 struct stat st; 00763 QString filepath = fi.absoluteFilePath(); 00764 int err = lstat(filepath.toLocal8Bit().constData(), &st); 00765 00766 if (err) 00767 { 00768 LOG(VB_GENERAL, LOG_ERR, 00769 QString("Could not stat file: %1").arg(filepath)); 00770 continue; 00771 } 00772 00773 // is this is a character device? 00774 if (!S_ISCHR(st.st_mode)) 00775 continue; 00776 00777 // is this device is in our minor range? 00778 uint minor_num = minor(st.st_rdev); 00779 if (minor_min > minor_num || minor_max < minor_num) 00780 continue; 00781 00782 // ignore duplicates if allow_duplicates not set 00783 if (!allow_duplicates && minor_list[minor_num]) 00784 continue; 00785 00786 // if the driver returns any info add this device to our list 00787 QByteArray tmp = filepath.toAscii(); 00788 int videofd = open(tmp.constData(), O_RDWR); 00789 if (videofd >= 0) 00790 { 00791 QString cn, dn; 00792 if (CardUtil::GetV4LInfo(videofd, cn, dn) && 00793 (!driverExp || (driverExp->exactMatch(dn))) && 00794 (card.isEmpty() || (cn == card))) 00795 { 00796 addSelection(filepath); 00797 cnt++; 00798 } 00799 close(videofd); 00800 } 00801 00802 // add to list of minors discovered to avoid duplicates 00803 minor_list[minor_num] = 1; 00804 } 00805 delete driverExp; 00806 00807 return cnt; 00808 } 00809 00810 private: 00811 QMap<uint, uint> minor_list; 00812 }; 00813 00814 class VBIDevice : public PathSetting, public CaptureCardDBStorage 00815 { 00816 public: 00817 VBIDevice(const CaptureCard &parent) : 00818 PathSetting(this, true), 00819 CaptureCardDBStorage(this, parent, "vbidevice") 00820 { 00821 setLabel(QObject::tr("VBI device")); 00822 setFilter(QString::null, QString::null); 00823 }; 00824 00825 void setFilter(const QString &card, const QString &driver) 00826 { 00827 clearSelections(); 00828 QDir dev("/dev/v4l", "vbi*", QDir::Name, QDir::System); 00829 if (!fillSelectionsFromDir(dev, card, driver)) 00830 { 00831 dev.setPath("/dev"); 00832 if (!fillSelectionsFromDir(dev, card, driver) && 00833 !getValue().isEmpty()) 00834 { 00835 addSelection(getValue(),getValue(),true); 00836 } 00837 } 00838 } 00839 00840 uint fillSelectionsFromDir(const QDir &dir, const QString &card, 00841 const QString &driver) 00842 { 00843 QStringList devices; 00844 QFileInfoList il = dir.entryInfoList(); 00845 for( QFileInfoList::iterator it = il.begin(); 00846 it != il.end(); 00847 ++it ) 00848 { 00849 QFileInfo &fi = *it; 00850 00851 QString device = fi.absoluteFilePath(); 00852 QByteArray adevice = device.toAscii(); 00853 int vbifd = open(adevice.constData(), O_RDWR); 00854 if (vbifd < 0) 00855 continue; 00856 00857 QString cn, dn; 00858 if (CardUtil::GetV4LInfo(vbifd, cn, dn) && 00859 (driver.isEmpty() || (dn == driver)) && 00860 (card.isEmpty() || (cn == card))) 00861 { 00862 devices.push_back(device); 00863 } 00864 00865 close(vbifd); 00866 } 00867 00868 QString sel = getValue(); 00869 for (uint i = 0; i < (uint) devices.size(); i++) 00870 addSelection(devices[i], devices[i], devices[i] == sel); 00871 00872 return (uint) devices.size(); 00873 } 00874 }; 00875 00876 class FileDevice : public PathSetting, public CaptureCardDBStorage 00877 { 00878 public: 00879 FileDevice(const CaptureCard &parent) : 00880 PathSetting(this, false), 00881 CaptureCardDBStorage(this, parent, "videodevice") 00882 { 00883 setLabel(QObject::tr("File path")); 00884 }; 00885 }; 00886 00887 class AudioDevice : public PathSetting, public CaptureCardDBStorage 00888 { 00889 public: 00890 AudioDevice(const CaptureCard &parent) : 00891 PathSetting(this, false), 00892 CaptureCardDBStorage(this, parent, "audiodevice") 00893 { 00894 setLabel(QObject::tr("Audio device")); 00895 #if USING_OSS 00896 QDir dev("/dev", "dsp*", QDir::Name, QDir::System); 00897 fillSelectionsFromDir(dev); 00898 dev.setPath("/dev/sound"); 00899 fillSelectionsFromDir(dev); 00900 #endif 00901 #if USING_ALSA 00902 addSelection("ALSA:default", "ALSA:default"); 00903 #endif 00904 addSelection(QObject::tr("(None)"), "NULL"); 00905 }; 00906 }; 00907 00908 class SignalTimeout : public SpinBoxSetting, public CaptureCardDBStorage 00909 { 00910 public: 00911 SignalTimeout(const CaptureCard &parent, uint value, uint min_val) : 00912 SpinBoxSetting(this, min_val, 60000, 250), 00913 CaptureCardDBStorage(this, parent, "signal_timeout") 00914 { 00915 setLabel(QObject::tr("Signal timeout (ms)")); 00916 setValue(value); 00917 setHelpText(QObject::tr( 00918 "Maximum time (in milliseconds) MythTV waits for " 00919 "a signal when scanning for channels.")); 00920 }; 00921 }; 00922 00923 class ChannelTimeout : public SpinBoxSetting, public CaptureCardDBStorage 00924 { 00925 public: 00926 ChannelTimeout(const CaptureCard &parent, uint value, uint min_val) : 00927 SpinBoxSetting(this, min_val, 65000, 250), 00928 CaptureCardDBStorage(this, parent, "channel_timeout") 00929 { 00930 setLabel(QObject::tr("Tuning timeout (ms)")); 00931 setValue(value); 00932 setHelpText(QObject::tr( 00933 "Maximum time (in milliseconds) MythTV waits for " 00934 "a channel lock when scanning for channels during setup, or for " 00935 "issuing a warning in Live TV mode.")); 00936 }; 00937 }; 00938 00939 class AudioRateLimit : public ComboBoxSetting, public CaptureCardDBStorage 00940 { 00941 public: 00942 AudioRateLimit(const CaptureCard &parent) : 00943 ComboBoxSetting(this), 00944 CaptureCardDBStorage(this, parent, "audioratelimit") 00945 { 00946 setLabel(QObject::tr("Force audio sampling rate")); 00947 setHelpText( 00948 QObject::tr("If non-zero, override the audio sampling " 00949 "rate in the recording profile when this card is " 00950 "used. Use this if your capture card does not " 00951 "support all of the standard rates.")); 00952 addSelection(QObject::tr("(None)"), "0"); 00953 addSelection("32000"); 00954 addSelection("44100"); 00955 addSelection("48000"); 00956 }; 00957 }; 00958 00959 class SkipBtAudio : public CheckBoxSetting, public CaptureCardDBStorage 00960 { 00961 public: 00962 SkipBtAudio(const CaptureCard &parent) : 00963 CheckBoxSetting(this), 00964 CaptureCardDBStorage(this, parent, "skipbtaudio") 00965 { 00966 setLabel(QObject::tr("Do not adjust volume")); 00967 setHelpText( 00968 QObject::tr("Enable this option for budget BT878 based " 00969 "DVB-T cards such as the AverTV DVB-T which " 00970 "require the audio volume to be left alone.")); 00971 }; 00972 }; 00973 00974 class DVBCardNum : public ComboBoxSetting, public CaptureCardDBStorage 00975 { 00976 public: 00977 DVBCardNum(const CaptureCard &parent) : 00978 ComboBoxSetting(this), 00979 CaptureCardDBStorage(this, parent, "videodevice") 00980 { 00981 setLabel(QObject::tr("DVB device")); 00982 setHelpText( 00983 QObject::tr("When you change this setting, the text below " 00984 "should change to the name and type of your card. " 00985 "If the card cannot be opened, an error message " 00986 "will be displayed.")); 00987 fillSelections(QString::null); 00988 }; 00989 00993 void fillSelections(const QString ¤t) 00994 { 00995 clearSelections(); 00996 00997 // Get devices from filesystem 00998 QStringList sdevs = CardUtil::ProbeVideoDevices("DVB"); 00999 01000 // Add current if needed 01001 if (!current.isEmpty() && 01002 (find(sdevs.begin(), sdevs.end(), current) == sdevs.end())) 01003 { 01004 stable_sort(sdevs.begin(), sdevs.end()); 01005 } 01006 01007 QStringList db = CardUtil::GetVideoDevices("DVB"); 01008 01009 QMap<QString,bool> in_use; 01010 QString sel = current; 01011 for (uint i = 0; i < (uint)sdevs.size(); i++) 01012 { 01013 const QString dev = sdevs[i]; 01014 in_use[sdevs[i]] = find(db.begin(), db.end(), dev) != db.end(); 01015 if (sel.isEmpty() && !in_use[sdevs[i]]) 01016 sel = dev; 01017 } 01018 01019 if (sel.isEmpty() && sdevs.size()) 01020 sel = sdevs[0]; 01021 01022 QString usestr = QString(" -- "); 01023 usestr += QObject::tr("Warning: already in use"); 01024 01025 for (uint i = 0; i < (uint)sdevs.size(); i++) 01026 { 01027 const QString dev = sdevs[i]; 01028 QString desc = dev + (in_use[sdevs[i]] ? usestr : ""); 01029 desc = (current == sdevs[i]) ? dev : desc; 01030 addSelection(desc, dev, dev == sel); 01031 } 01032 } 01033 01034 virtual void Load(void) 01035 { 01036 clearSelections(); 01037 addSelection(QString::null); 01038 01039 CaptureCardDBStorage::Load(); 01040 01041 QString dev = CardUtil::GetDeviceName(DVB_DEV_FRONTEND, getValue()); 01042 fillSelections(dev); 01043 } 01044 }; 01045 01046 class DVBCardType : public TransLabelSetting 01047 { 01048 public: 01049 DVBCardType() 01050 { 01051 setLabel(QObject::tr("Subtype")); 01052 }; 01053 }; 01054 01055 class DVBCardName : public TransLabelSetting 01056 { 01057 public: 01058 DVBCardName() 01059 { 01060 setLabel(QObject::tr("Frontend ID")); 01061 }; 01062 }; 01063 01064 class DVBNoSeqStart : public CheckBoxSetting, public CaptureCardDBStorage 01065 { 01066 public: 01067 DVBNoSeqStart(const CaptureCard &parent) : 01068 CheckBoxSetting(this), 01069 CaptureCardDBStorage(this, parent, "dvb_wait_for_seqstart") 01070 { 01071 setLabel(QObject::tr("Wait for SEQ start header.")); 01072 setValue(true); 01073 setHelpText( 01074 QObject::tr("If enabled, drop packets from the start of a DVB " 01075 "recording until a sequence start header is seen.")); 01076 }; 01077 }; 01078 01079 class DVBOnDemand : public CheckBoxSetting, public CaptureCardDBStorage 01080 { 01081 public: 01082 DVBOnDemand(const CaptureCard &parent) : 01083 CheckBoxSetting(this), 01084 CaptureCardDBStorage(this, parent, "dvb_on_demand") 01085 { 01086 setLabel(QObject::tr("Open DVB card on demand")); 01087 setValue(true); 01088 setHelpText( 01089 QObject::tr("If enabled, only open the DVB card when required, " 01090 "leaving it free for other programs at other times.")); 01091 }; 01092 }; 01093 01094 class DVBEITScan : public CheckBoxSetting, public CaptureCardDBStorage 01095 { 01096 public: 01097 DVBEITScan(const CaptureCard &parent) : 01098 CheckBoxSetting(this), 01099 CaptureCardDBStorage(this, parent, "dvb_eitscan") 01100 { 01101 setLabel(QObject::tr("Use DVB card for active EIT scan")); 01102 setValue(true); 01103 setHelpText( 01104 QObject::tr("If enabled, activate active scanning for " 01105 "program data (EIT). When this option is enabled " 01106 "the DVB card is constantly in-use.")); 01107 }; 01108 }; 01109 01110 class DVBTuningDelay : public SpinBoxSetting, public CaptureCardDBStorage 01111 { 01112 public: 01113 DVBTuningDelay(const CaptureCard &parent) : 01114 SpinBoxSetting(this, 0, 2000, 25), 01115 CaptureCardDBStorage(this, parent, "dvb_tuning_delay") 01116 { 01117 setLabel(QObject::tr("DVB tuning delay (ms)")); 01118 setHelpText( 01119 QObject::tr("Some Linux DVB drivers, in particular for the " 01120 "Hauppauge Nova-T, require that we slow down " 01121 "the tuning process by specifying a delay " 01122 "(in milliseconds).")); 01123 }; 01124 }; 01125 01126 class FirewireGUID : public ComboBoxSetting, public CaptureCardDBStorage 01127 { 01128 public: 01129 FirewireGUID(const CaptureCard &parent) : 01130 ComboBoxSetting(this), 01131 CaptureCardDBStorage(this, parent, "videodevice") 01132 { 01133 setLabel(QObject::tr("GUID")); 01134 #ifdef USING_FIREWIRE 01135 vector<AVCInfo> list = FirewireDevice::GetSTBList(); 01136 for (uint i = 0; i < list.size(); i++) 01137 { 01138 QString guid = list[i].GetGUIDString(); 01139 guid_to_avcinfo[guid] = list[i]; 01140 addSelection(guid); 01141 } 01142 #endif // USING_FIREWIRE 01143 } 01144 01145 AVCInfo GetAVCInfo(const QString &guid) const 01146 { return guid_to_avcinfo[guid]; } 01147 01148 private: 01149 QMap<QString,AVCInfo> guid_to_avcinfo; 01150 }; 01151 01152 FirewireModel::FirewireModel(const CaptureCard &parent, 01153 const FirewireGUID *_guid) : 01154 ComboBoxSetting(this), 01155 CaptureCardDBStorage(this, parent, "firewire_model"), 01156 guid(_guid) 01157 { 01158 setLabel(QObject::tr("Cable box model")); 01159 addSelection(QObject::tr("Motorola Generic"), "MOTO GENERIC"); 01160 addSelection(QObject::tr("SA/Cisco Generic"), "SA GENERIC"); 01161 addSelection("DCH-3200"); 01162 addSelection("DCX-3200"); 01163 addSelection("DCT-3412"); 01164 addSelection("DCT-3416"); 01165 addSelection("DCT-6200"); 01166 addSelection("DCT-6212"); 01167 addSelection("DCT-6216"); 01168 addSelection("QIP-6200"); 01169 addSelection("QIP-7100"); 01170 addSelection("PACE-550"); 01171 addSelection("PACE-779"); 01172 addSelection("SA3250HD"); 01173 addSelection("SA4200HD"); 01174 addSelection("SA4250HDC"); 01175 addSelection("SA8300HD"); 01176 QString help = QObject::tr( 01177 "Choose the model that most closely resembles your set top box. " 01178 "Depending on firmware revision SA4200HD may work better for a " 01179 "SA3250HD box."); 01180 setHelpText(help); 01181 } 01182 01183 void FirewireModel::SetGUID(const QString &_guid) 01184 { 01185 (void) _guid; 01186 01187 #ifdef USING_FIREWIRE 01188 AVCInfo info = guid->GetAVCInfo(_guid); 01189 QString model = FirewireDevice::GetModelName(info.vendorid, info.modelid); 01190 setValue(max(getValueIndex(model), 0)); 01191 #endif // USING_FIREWIRE 01192 } 01193 01194 void FirewireDesc::SetGUID(const QString &_guid) 01195 { 01196 (void) _guid; 01197 01198 setLabel(tr("Description")); 01199 01200 #ifdef USING_FIREWIRE 01201 QString name = guid->GetAVCInfo(_guid).product_name; 01202 name.replace("Scientific-Atlanta", "SA"); 01203 name.replace(", Inc.", ""); 01204 name.replace("Explorer(R)", ""); 01205 name = name.simplified(); 01206 setValue((name.isEmpty()) ? "" : name); 01207 #endif // USING_FIREWIRE 01208 } 01209 01210 class FirewireConnection : public ComboBoxSetting, public CaptureCardDBStorage 01211 { 01212 public: 01213 FirewireConnection(const CaptureCard &parent) : 01214 ComboBoxSetting(this), 01215 CaptureCardDBStorage(this, parent, "firewire_connection") 01216 { 01217 setLabel(QObject::tr("Connection Type")); 01218 addSelection(QObject::tr("Point to Point"),"0"); 01219 addSelection(QObject::tr("Broadcast"),"1"); 01220 } 01221 }; 01222 01223 class FirewireSpeed : public ComboBoxSetting, public CaptureCardDBStorage 01224 { 01225 public: 01226 FirewireSpeed(const CaptureCard &parent) : 01227 ComboBoxSetting(this), 01228 CaptureCardDBStorage(this, parent, "firewire_speed") 01229 { 01230 setLabel(QObject::tr("Speed")); 01231 addSelection(QObject::tr("100Mbps"),"0"); 01232 addSelection(QObject::tr("200Mbps"),"1"); 01233 addSelection(QObject::tr("400Mbps"),"2"); 01234 addSelection(QObject::tr("800Mbps"),"3"); 01235 } 01236 }; 01237 01238 class FirewireConfigurationGroup : public VerticalConfigurationGroup 01239 { 01240 public: 01241 FirewireConfigurationGroup(CaptureCard& a_parent) : 01242 VerticalConfigurationGroup(false, true, false, false), 01243 parent(a_parent), 01244 dev(new FirewireGUID(parent)), 01245 desc(new FirewireDesc(dev)), 01246 model(new FirewireModel(parent, dev)) 01247 { 01248 addChild(dev); 01249 addChild(new EmptyAudioDevice(parent)); 01250 addChild(new EmptyVBIDevice(parent)); 01251 addChild(desc); 01252 addChild(model); 01253 01254 #ifdef USING_LINUX_FIREWIRE 01255 addChild(new FirewireConnection(parent)); 01256 addChild(new FirewireSpeed(parent)); 01257 #endif // USING_LINUX_FIREWIRE 01258 01259 addChild(new SignalTimeout(parent, 2000, 1000)); 01260 addChild(new ChannelTimeout(parent, 9000, 1750)); 01261 01262 model->SetGUID(dev->getValue()); 01263 desc->SetGUID(dev->getValue()); 01264 connect(dev, SIGNAL(valueChanged(const QString&)), 01265 model, SLOT( SetGUID( const QString&))); 01266 connect(dev, SIGNAL(valueChanged(const QString&)), 01267 desc, SLOT( SetGUID( const QString&))); 01268 }; 01269 01270 private: 01271 CaptureCard &parent; 01272 FirewireGUID *dev; 01273 FirewireDesc *desc; 01274 FirewireModel *model; 01275 }; 01276 01277 // ----------------------- 01278 // HDHomeRun Configuration 01279 // ----------------------- 01280 01281 HDHomeRunIP::HDHomeRunIP() 01282 { 01283 setLabel(QObject::tr("IP Address")); 01284 setEnabled(false); 01285 connect(this, SIGNAL(valueChanged( const QString&)), 01286 this, SLOT( UpdateDevices(const QString&))); 01287 _oldValue=""; 01288 }; 01289 01290 void HDHomeRunIP::setEnabled(bool e) 01291 { 01292 TransLineEditSetting::setEnabled(e); 01293 if (e) 01294 { 01295 if (!_oldValue.isEmpty()) 01296 setValue(_oldValue); 01297 emit NewIP(getValue()); 01298 } 01299 else 01300 { 01301 _oldValue = getValue(); 01302 _oldValue.detach(); 01303 } 01304 } 01305 01306 void HDHomeRunIP::UpdateDevices(const QString &v) 01307 { 01308 if (isEnabled()) 01309 { 01310 #if 0 01311 LOG(VB_GENERAL, LOG_DEBUG, QString("Emitting NewIP(%1)").arg(v)); 01312 #endif 01313 emit NewIP(v); 01314 } 01315 } 01316 01317 HDHomeRunTunerIndex::HDHomeRunTunerIndex() 01318 { 01319 setLabel(QObject::tr("Tuner")); 01320 setEnabled(false); 01321 connect(this, SIGNAL(valueChanged( const QString&)), 01322 this, SLOT( UpdateDevices(const QString&))); 01323 _oldValue = ""; 01324 }; 01325 01326 void HDHomeRunTunerIndex::setEnabled(bool e) 01327 { 01328 TransLineEditSetting::setEnabled(e); 01329 if (e) { 01330 if (!_oldValue.isEmpty()) 01331 setValue(_oldValue); 01332 emit NewTuner(getValue()); 01333 } 01334 else 01335 { 01336 _oldValue = getValue(); 01337 } 01338 } 01339 01340 void HDHomeRunTunerIndex::UpdateDevices(const QString &v) 01341 { 01342 if (isEnabled()) 01343 { 01344 #if 0 01345 LOG(VB_GENERAL, LOG_DEBUG, QString("Emitting NewTuner(%1)").arg(v)); 01346 #endif 01347 emit NewTuner(v); 01348 } 01349 } 01350 01351 HDHomeRunDeviceID::HDHomeRunDeviceID(const CaptureCard &parent) : 01352 LabelSetting(this), 01353 CaptureCardDBStorage(this, parent, "videodevice"), 01354 _ip(QString::null), 01355 _tuner(QString::null), 01356 _overridedeviceid(QString::null) 01357 { 01358 setLabel(tr("Device ID")); 01359 setHelpText(tr("Device ID of HDHomeRun device")); 01360 } 01361 01362 void HDHomeRunDeviceID::SetIP(const QString &ip) 01363 { 01364 #if 0 01365 LOG(VB_GENERAL, LOG_DEBUG, QString("Setting IP to %1").arg(ip)); 01366 #endif 01367 _ip = ip; 01368 setValue(QString("%1-%2").arg(_ip).arg(_tuner)); 01369 #if 0 01370 LOG(VB_GENERAL, LOG_DEBUG, QString("Done Setting IP to %1").arg(ip)); 01371 #endif 01372 } 01373 01374 void HDHomeRunDeviceID::SetTuner(const QString &tuner) 01375 { 01376 #if 0 01377 LOG(VB_GENERAL, LOG_DEBUG, QString("Setting Tuner to %1").arg(tuner)); 01378 #endif 01379 _tuner = tuner; 01380 setValue(QString("%1-%2").arg(_ip).arg(_tuner)); 01381 #if 0 01382 LOG(VB_GENERAL, LOG_DEBUG, QString("Done Setting Tuner to %1").arg(tuner)); 01383 #endif 01384 } 01385 01386 void HDHomeRunDeviceID::SetOverrideDeviceID(const QString &deviceid) 01387 { 01388 _overridedeviceid = deviceid; 01389 setValue(deviceid); 01390 } 01391 01392 void HDHomeRunDeviceID::Load(void) 01393 { 01394 CaptureCardDBStorage::Load(); 01395 if (!_overridedeviceid.isEmpty()) 01396 { 01397 setValue(_overridedeviceid); 01398 _overridedeviceid = QString::null; 01399 } 01400 } 01401 01402 HDHomeRunDeviceIDList::HDHomeRunDeviceIDList( 01403 HDHomeRunDeviceID *deviceid, 01404 TransLabelSetting *desc, 01405 HDHomeRunIP *cardip, 01406 HDHomeRunTunerIndex *cardtuner, 01407 HDHomeRunDeviceList *devicelist) : 01408 _deviceid(deviceid), 01409 _desc(desc), 01410 _cardip(cardip), 01411 _cardtuner(cardtuner), 01412 _devicelist(devicelist) 01413 { 01414 setLabel(QObject::tr("Available devices")); 01415 setHelpText( 01416 QObject::tr( 01417 "Device ID and Tuner Number of available HDHomeRun devices.")); 01418 01419 connect(this, SIGNAL(valueChanged( const QString&)), 01420 this, SLOT( UpdateDevices(const QString&))); 01421 01422 _oldValue = ""; 01423 }; 01424 01428 void HDHomeRunDeviceIDList::fillSelections(const QString &cur) 01429 { 01430 clearSelections(); 01431 01432 vector<QString> devs; 01433 QMap<QString, bool> in_use; 01434 01435 QString current = cur; 01436 01437 #if 0 01438 LOG(VB_GENERAL, LOG_DEBUG, QString("Filling List, current = '%1'") 01439 .arg(current)); 01440 #endif 01441 01442 HDHomeRunDeviceList::iterator it = _devicelist->begin(); 01443 for (; it != _devicelist->end(); ++it) 01444 { 01445 if ((*it).discovered) 01446 { 01447 devs.push_back(it.key()); 01448 in_use[it.key()] = (*it).inuse; 01449 } 01450 } 01451 01452 QString man_addr = HDHomeRunDeviceIDList::tr("Manually Enter IP Address"); 01453 QString sel = man_addr; 01454 devs.push_back(sel); 01455 01456 if (3 == devs.size() && current.left(8).toUpper() == "FFFFFFFF") 01457 { 01458 current = sel = (current.right(1) == "0") ? 01459 *(devs.begin()) : *(++devs.begin()); 01460 } 01461 else 01462 { 01463 vector<QString>::const_iterator it = devs.begin(); 01464 for (; it != devs.end(); ++it) 01465 sel = (current == *it) ? *it : sel; 01466 } 01467 01468 QString usestr = QString(" -- "); 01469 usestr += QObject::tr("Warning: already in use"); 01470 01471 for (uint i = 0; i < devs.size(); i++) 01472 { 01473 const QString dev = devs[i]; 01474 QString desc = dev + (in_use[devs[i]] ? usestr : ""); 01475 desc = (current == devs[i]) ? dev : desc; 01476 addSelection(desc, dev, dev == sel); 01477 } 01478 01479 if (current != cur) 01480 { 01481 _deviceid->SetOverrideDeviceID(current); 01482 } 01483 else if (sel == man_addr && !current.isEmpty()) 01484 { 01485 // Populate the proper values for IP address and tuner 01486 QStringList selection = current.split("-"); 01487 01488 _cardip->SetOldValue(selection.first()); 01489 _cardtuner->SetOldValue(selection.last()); 01490 01491 _cardip->setValue(selection.first()); 01492 _cardtuner->setValue(selection.last()); 01493 } 01494 } 01495 01496 void HDHomeRunDeviceIDList::Load(void) 01497 { 01498 clearSelections(); 01499 01500 fillSelections(_deviceid->getValue()); 01501 } 01502 01503 void HDHomeRunDeviceIDList::UpdateDevices(const QString &v) 01504 { 01505 #if 0 01506 LOG(VB_GENERAL, LOG_DEBUG, QString("Got signal with %1").arg(v)); 01507 #endif 01508 if (v == HDHomeRunDeviceIDList::tr("Manually Enter IP Address")) 01509 { 01510 _cardip->setEnabled(true); 01511 _cardtuner->setEnabled(true); 01512 #if 0 01513 LOG(VB_GENERAL, LOG_DEBUG, "Done"); 01514 #endif 01515 } 01516 else if (!v.isEmpty()) 01517 { 01518 if (_oldValue == HDHomeRunDeviceIDList::tr("Manually Enter IP Address")) 01519 { 01520 _cardip->setEnabled(false); 01521 _cardtuner->setEnabled(false); 01522 } 01523 _deviceid->setValue(v); 01524 01525 // Update _cardip and cardtuner 01526 _cardip->setValue((*_devicelist)[v].cardip); 01527 _cardtuner->setValue(QString("%1").arg((*_devicelist)[v].cardtuner)); 01528 _desc->setValue((*_devicelist)[v].desc); 01529 } 01530 _oldValue = v; 01531 }; 01532 01533 class IPTVHost : public LineEditSetting, public CaptureCardDBStorage 01534 { 01535 public: 01536 IPTVHost(const CaptureCard &parent) : 01537 LineEditSetting(this), 01538 CaptureCardDBStorage(this, parent, "videodevice") 01539 { 01540 setValue("http://mafreebox.freebox.fr/freeboxtv/playlist.m3u"); 01541 setLabel(QObject::tr("M3U URL")); 01542 setHelpText(QObject::tr("URL of M3U containing IPTV channel URLs.")); 01543 } 01544 }; 01545 01546 class IPTVConfigurationGroup : public VerticalConfigurationGroup 01547 { 01548 public: 01549 IPTVConfigurationGroup(CaptureCard& a_parent): 01550 VerticalConfigurationGroup(false, true, false, false), 01551 parent(a_parent) 01552 { 01553 setUseLabel(false); 01554 addChild(new IPTVHost(parent)); 01555 addChild(new ChannelTimeout(parent, 3000, 1750)); 01556 addChild(new EmptyAudioDevice(parent)); 01557 addChild(new EmptyVBIDevice(parent)); 01558 }; 01559 01560 private: 01561 CaptureCard &parent; 01562 }; 01563 01564 class ASIDevice : public ComboBoxSetting, public CaptureCardDBStorage 01565 { 01566 public: 01567 ASIDevice(const CaptureCard &parent) : 01568 ComboBoxSetting(this, true), 01569 CaptureCardDBStorage(this, parent, "videodevice") 01570 { 01571 setLabel(QObject::tr("ASI device")); 01572 fillSelections(QString::null); 01573 }; 01574 01578 void fillSelections(const QString ¤t) 01579 { 01580 clearSelections(); 01581 01582 // Get devices from filesystem 01583 QStringList sdevs = CardUtil::ProbeVideoDevices("ASI"); 01584 01585 // Add current if needed 01586 if (!current.isEmpty() && 01587 (find(sdevs.begin(), sdevs.end(), current) == sdevs.end())) 01588 { 01589 stable_sort(sdevs.begin(), sdevs.end()); 01590 } 01591 01592 // Get devices from DB 01593 QStringList db = CardUtil::GetVideoDevices("ASI"); 01594 01595 // Figure out which physical devices are already in use 01596 // by another card defined in the DB, and select a device 01597 // for new configs (preferring non-conflicing devices). 01598 QMap<QString,bool> in_use; 01599 QString sel = current; 01600 for (uint i = 0; i < (uint)sdevs.size(); i++) 01601 { 01602 const QString dev = sdevs[i]; 01603 in_use[sdevs[i]] = find(db.begin(), db.end(), dev) != db.end(); 01604 if (sel.isEmpty() && !in_use[sdevs[i]]) 01605 sel = dev; 01606 } 01607 01608 // Unfortunately all devices are conflicted, select first device. 01609 if (sel.isEmpty() && sdevs.size()) 01610 sel = sdevs[0]; 01611 01612 QString usestr = QString(" -- "); 01613 usestr += QObject::tr("Warning: already in use"); 01614 01615 // Add the devices to the UI 01616 bool found = false; 01617 for (uint i = 0; i < (uint)sdevs.size(); i++) 01618 { 01619 const QString dev = sdevs[i]; 01620 QString desc = dev + (in_use[sdevs[i]] ? usestr : ""); 01621 desc = (current == sdevs[i]) ? dev : desc; 01622 addSelection(desc, dev, dev == sel); 01623 found |= (dev == sel); 01624 } 01625 01626 // If a configured device isn't on the list, add it with warning 01627 if (!found && !current.isEmpty()) 01628 { 01629 QString desc = current + " -- " + 01630 QObject::tr("Warning: unable to open"); 01631 addSelection(desc, current, true); 01632 } 01633 } 01634 01635 virtual void Load(void) 01636 { 01637 clearSelections(); 01638 addSelection(QString::null); 01639 CaptureCardDBStorage::Load(); 01640 fillSelections(getValue()); 01641 } 01642 }; 01643 01644 ASIConfigurationGroup::ASIConfigurationGroup(CaptureCard& a_parent): 01645 VerticalConfigurationGroup(false, true, false, false), 01646 parent(a_parent), 01647 device(new ASIDevice(parent)), 01648 cardinfo(new TransLabelSetting()), 01649 instances(new InstanceCount(parent)) 01650 { 01651 addChild(device); 01652 addChild(new EmptyAudioDevice(parent)); 01653 addChild(new EmptyVBIDevice(parent)); 01654 addChild(cardinfo); 01655 addChild(instances); 01656 01657 connect(device, SIGNAL(valueChanged(const QString&)), 01658 this, SLOT( probeCard( const QString&))); 01659 connect(instances, SIGNAL(valueChanged(int)), 01660 &parent, SLOT( SetInstanceCount(int))); 01661 01662 probeCard(device->getValue()); 01663 }; 01664 01665 void ASIConfigurationGroup::probeCard(const QString &device) 01666 { 01667 #ifdef USING_ASI 01668 if (device.isEmpty()) 01669 { 01670 cardinfo->setValue(""); 01671 return; 01672 } 01673 01674 if (parent.getCardID() && parent.GetRawCardType() != "ASI") 01675 { 01676 cardinfo->setValue(""); 01677 return; 01678 } 01679 01680 QString error; 01681 int device_num = CardUtil::GetASIDeviceNumber(device, &error); 01682 if (device_num < 0) 01683 { 01684 cardinfo->setValue(tr("Not a valid DVEO ASI card")); 01685 LOG(VB_GENERAL, LOG_WARNING, 01686 "ASIConfigurationGroup::probeCard(), Warning: " + error); 01687 return; 01688 } 01689 cardinfo->setValue(tr("Valid DVEO ASI card")); 01690 #else 01691 cardinfo->setValue(QString("Not compiled with ASI support")); 01692 #endif 01693 } 01694 01695 ImportConfigurationGroup::ImportConfigurationGroup(CaptureCard& a_parent): 01696 VerticalConfigurationGroup(false, true, false, false), 01697 parent(a_parent), 01698 info(new TransLabelSetting()), size(new TransLabelSetting()) 01699 { 01700 FileDevice *device = new FileDevice(parent); 01701 device->setHelpText(tr("A local file used to simulate a recording." 01702 " Leave empty to use MythEvents to trigger an" 01703 " external program to import recording files.")); 01704 addChild(device); 01705 01706 addChild(new EmptyAudioDevice(parent)); 01707 addChild(new EmptyVBIDevice(parent)); 01708 01709 info->setLabel(tr("File info")); 01710 addChild(info); 01711 01712 size->setLabel(tr("File size")); 01713 addChild(size); 01714 01715 connect(device, SIGNAL(valueChanged(const QString&)), 01716 this, SLOT( probeCard( const QString&))); 01717 01718 probeCard(device->getValue()); 01719 }; 01720 01721 void ImportConfigurationGroup::probeCard(const QString &device) 01722 { 01723 QString ci, cs; 01724 QFileInfo fileInfo(device); 01725 01726 // For convenience, ImportRecorder allows both formats: 01727 if (device.toLower().startsWith("file:")) 01728 fileInfo.setFile(device.mid(5)); 01729 01730 if (fileInfo.exists()) 01731 { 01732 if (fileInfo.isReadable() && (fileInfo.isFile())) 01733 { 01734 ci = HTTPRequest::TestMimeType(fileInfo.absoluteFilePath()); 01735 cs = tr("%1 MB").arg(fileInfo.size() / 1024 / 1024); 01736 } 01737 else 01738 ci = tr("File not readable"); 01739 } 01740 else 01741 { 01742 ci = tr("File %1 does not exist").arg(device); 01743 } 01744 01745 info->setValue(ci); 01746 size->setValue(cs); 01747 } 01748 01749 class HDHomeRunExtra : public ConfigurationWizard 01750 { 01751 public: 01752 HDHomeRunExtra(HDHomeRunConfigurationGroup &parent); 01753 uint GetInstanceCount(void) const 01754 { 01755 return (uint) count->intValue(); 01756 } 01757 01758 private: 01759 InstanceCount *count; 01760 }; 01761 01762 HDHomeRunExtra::HDHomeRunExtra(HDHomeRunConfigurationGroup &parent) : 01763 count(new InstanceCount(parent.parent)) 01764 { 01765 VerticalConfigurationGroup* rec = new VerticalConfigurationGroup(false); 01766 rec->setLabel(QObject::tr("Recorder Options")); 01767 rec->setUseLabel(false); 01768 01769 rec->addChild(new SignalTimeout(parent.parent, 1000, 250)); 01770 rec->addChild(new ChannelTimeout(parent.parent, 3000, 1750)); 01771 rec->addChild(count); 01772 01773 addChild(rec); 01774 } 01775 01776 HDHomeRunConfigurationGroup::HDHomeRunConfigurationGroup 01777 (CaptureCard& a_parent) : 01778 VerticalConfigurationGroup(false, true, false, false), 01779 parent(a_parent) 01780 { 01781 setUseLabel(false); 01782 01783 // Fill Device list 01784 FillDeviceList(); 01785 01786 deviceid = new HDHomeRunDeviceID(parent); 01787 desc = new TransLabelSetting(); 01788 desc->setLabel(tr("Description")); 01789 cardip = new HDHomeRunIP(); 01790 cardtuner = new HDHomeRunTunerIndex(); 01791 deviceidlist = new HDHomeRunDeviceIDList( 01792 deviceid, desc, cardip, cardtuner, &devicelist); 01793 01794 addChild(deviceidlist); 01795 addChild(new EmptyAudioDevice(parent)); 01796 addChild(new EmptyVBIDevice(parent)); 01797 addChild(deviceid); 01798 addChild(desc); 01799 addChild(cardip); 01800 addChild(cardtuner); 01801 01802 TransButtonSetting *buttonRecOpt = new TransButtonSetting(); 01803 buttonRecOpt->setLabel(tr("Recording Options")); 01804 addChild(buttonRecOpt); 01805 01806 connect(buttonRecOpt, SIGNAL(pressed()), 01807 this, SLOT( HDHomeRunExtraPanel())); 01808 01809 connect(cardip, SIGNAL(NewIP(const QString&)), 01810 deviceid, SLOT( SetIP(const QString&))); 01811 connect(cardtuner, SIGNAL(NewTuner(const QString&)), 01812 deviceid, SLOT( SetTuner(const QString&))); 01813 }; 01814 01815 void HDHomeRunConfigurationGroup::FillDeviceList(void) 01816 { 01817 devicelist.clear(); 01818 01819 // Find physical devices first 01820 // ProbeVideoDevices returns "deviceid ip" pairs 01821 QStringList devs = CardUtil::ProbeVideoDevices("HDHOMERUN"); 01822 01823 QStringList::const_iterator it; 01824 01825 for (it = devs.begin(); it != devs.end(); ++it) 01826 { 01827 QString dev = *it; 01828 QStringList devinfo = dev.split(" "); 01829 QString devid = devinfo.at(0); 01830 QString devip = devinfo.at(1); 01831 QString devtuner = devinfo.at(2); 01832 01833 HDHomeRunDevice tmpdevice; 01834 tmpdevice.deviceid = devid; 01835 tmpdevice.desc = CardUtil::GetHDHRdesc(devid); 01836 tmpdevice.cardip = devip; 01837 tmpdevice.inuse = false; 01838 tmpdevice.discovered = true; 01839 tmpdevice.cardtuner = devtuner; 01840 tmpdevice.mythdeviceid = 01841 tmpdevice.deviceid + "-" + tmpdevice.cardtuner; 01842 devicelist[tmpdevice.mythdeviceid] = tmpdevice; 01843 } 01844 uint found_device_count = devicelist.size(); 01845 01846 // Now find configured devices 01847 01848 // returns "xxxxxxxx-n" or "ip.ip.ip.ip-n" values 01849 QStringList db = CardUtil::GetVideoDevices("HDHOMERUN"); 01850 01851 for (it = db.begin(); it != db.end(); ++it) 01852 { 01853 QMap<QString, HDHomeRunDevice>::iterator dit; 01854 01855 dit = devicelist.find(*it); 01856 01857 if (dit == devicelist.end()) 01858 { 01859 if ((*it).toUpper() == "FFFFFFFF-0" && 2 == found_device_count) 01860 dit = devicelist.begin(); 01861 01862 if ((*it).toUpper() == "FFFFFFFF-1" && 2 == found_device_count) 01863 { 01864 dit = devicelist.begin(); 01865 ++dit; 01866 } 01867 } 01868 01869 if (dit != devicelist.end()) 01870 { 01871 (*dit).inuse = true; 01872 continue; 01873 } 01874 01875 HDHomeRunDevice tmpdevice; 01876 tmpdevice.mythdeviceid = *it; 01877 tmpdevice.inuse = true; 01878 tmpdevice.discovered = false; 01879 01880 if (ProbeCard(tmpdevice)) 01881 devicelist[tmpdevice.mythdeviceid] = tmpdevice; 01882 } 01883 01884 #if 0 01885 // Debug dump of cards 01886 QMap<QString, HDHomeRunDevice>::iterator debugit; 01887 for (debugit = devicelist.begin(); debugit != devicelist.end(); ++debugit) 01888 { 01889 LOG(VB_GENERAL, LOG_DEBUG, QString("%1: %2 %3 %4 %5 %6 %7") 01890 .arg(debugit.key()) 01891 .arg((*debugit).mythdeviceid) 01892 .arg((*debugit).deviceid) 01893 .arg((*debugit).cardip) 01894 .arg((*debugit).cardtuner) 01895 .arg((*debugit).inuse) 01896 .arg((*debugit).discovered)); 01897 } 01898 #endif 01899 } 01900 01901 bool HDHomeRunConfigurationGroup::ProbeCard(HDHomeRunDevice &tmpdevice) 01902 { 01903 #ifdef USING_HDHOMERUN 01904 hdhomerun_device_t *thisdevice = 01905 hdhomerun_device_create_from_str( 01906 tmpdevice.mythdeviceid.toLocal8Bit().constData(), NULL); 01907 01908 if (thisdevice) 01909 { 01910 uint device_id = hdhomerun_device_get_device_id(thisdevice); 01911 uint device_ip = hdhomerun_device_get_device_ip(thisdevice); 01912 uint tuner = hdhomerun_device_get_tuner(thisdevice); 01913 hdhomerun_device_destroy(thisdevice); 01914 01915 if (device_id == 0) 01916 tmpdevice.deviceid = "NOTFOUND"; 01917 else 01918 { 01919 tmpdevice.deviceid = QString("%1").arg(device_id, 8, 16); 01920 tmpdevice.desc = CardUtil::GetHDHRdesc(tmpdevice.deviceid); 01921 } 01922 01923 tmpdevice.deviceid = tmpdevice.deviceid.toUpper(); 01924 01925 tmpdevice.cardip = QString("%1.%2.%3.%4") 01926 .arg((device_ip>>24) & 0xFF).arg((device_ip>>16) & 0xFF) 01927 .arg((device_ip>> 8) & 0xFF).arg((device_ip>> 0) & 0xFF); 01928 01929 tmpdevice.cardtuner = QString("%1").arg(tuner); 01930 return true; 01931 } 01932 #endif // USING_HDHOMERUN 01933 return false; 01934 } 01935 01936 void HDHomeRunConfigurationGroup::HDHomeRunExtraPanel(void) 01937 { 01938 parent.reload(); // ensure card id is valid 01939 01940 HDHomeRunExtra acw(*this); 01941 acw.exec(); 01942 parent.SetInstanceCount(acw.GetInstanceCount()); 01943 } 01944 01945 // ----------------------- 01946 // Ceton Configuration 01947 // ----------------------- 01948 01949 CetonSetting::CetonSetting(const char* label, const char* helptext) 01950 { 01951 setLabel(QObject::tr(label)); 01952 setHelpText(tr(helptext)); 01953 connect(this, SIGNAL(valueChanged( const QString&)), 01954 this, SLOT( UpdateDevices(const QString&))); 01955 } 01956 01957 void CetonSetting::UpdateDevices(const QString &v) 01958 { 01959 if (isEnabled()) 01960 emit NewValue(v); 01961 } 01962 01963 void CetonSetting::LoadValue(const QString &value) 01964 { 01965 setValue(value); 01966 } 01967 01968 CetonDeviceID::CetonDeviceID(const CaptureCard &parent) : 01969 LabelSetting(this), 01970 CaptureCardDBStorage(this, parent, "videodevice"), 01971 _ip(), _card(), _tuner() 01972 { 01973 setLabel(tr("Device ID")); 01974 setHelpText(tr("Device ID of Ceton device")); 01975 } 01976 01977 void CetonDeviceID::SetIP(const QString &ip) 01978 { 01979 QString regexp = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){4}$"; 01980 if (QRegExp(regexp).exactMatch(ip + ".")) 01981 { 01982 _ip = ip; 01983 setValue(QString("%1-%2.%3").arg(_ip).arg(_card).arg(_tuner)); 01984 } 01985 } 01986 01987 void CetonDeviceID::SetCard(const QString &card) 01988 { 01989 if (QRegExp("^(\\d|RTP)$").exactMatch(card)) 01990 { 01991 _card = card; 01992 setValue(QString("%1-%2.%3").arg(_ip).arg(_card).arg(_tuner)); 01993 } 01994 } 01995 01996 void CetonDeviceID::SetTuner(const QString &tuner) 01997 { 01998 if (QRegExp("^\\d$").exactMatch(tuner)) 01999 { 02000 _tuner = tuner; 02001 setValue(QString("%1-%2.%3").arg(_ip).arg(_card).arg(_tuner)); 02002 } 02003 } 02004 02005 void CetonDeviceID::Load(void) 02006 { 02007 CaptureCardDBStorage::Load(); 02008 UpdateValues(); 02009 } 02010 02011 void CetonDeviceID::UpdateValues(void) 02012 { 02013 QRegExp newstyle("^([0-9.]+)-(\\d|RTP)\\.(\\d)$"); 02014 if (newstyle.exactMatch(getValue())) 02015 { 02016 emit LoadedIP(newstyle.cap(1)); 02017 emit LoadedCard(newstyle.cap(2)); 02018 emit LoadedTuner(newstyle.cap(3)); 02019 } 02020 } 02021 02022 CetonConfigurationGroup::CetonConfigurationGroup 02023 (CaptureCard& a_parent) : 02024 VerticalConfigurationGroup(false, true, false, false), 02025 parent(a_parent) 02026 { 02027 setUseLabel(false); 02028 02029 deviceid = new CetonDeviceID(parent); 02030 desc = new TransLabelSetting(); 02031 desc->setLabel(tr("Description")); 02032 ip = new CetonSetting( 02033 "IP Address", 02034 "IP Address of the Ceton device (192.168.200.1 by default)"); 02035 card = new CetonSetting( 02036 "Card Number", 02037 "Number of the installed Ceton card. Use 0 if only 1 Ceton card is " 02038 "installed in your system. Use the value RTP if this is a Ceton " 02039 "card installed in a remote system"); 02040 tuner = new CetonSetting( 02041 "Tuner", 02042 "Number of the tuner on the Ceton device (first tuner is number 0)"); 02043 02044 addChild(ip); 02045 addChild(card); 02046 addChild(tuner); 02047 addChild(deviceid); 02048 addChild(desc); 02049 02050 connect(ip, SIGNAL(NewValue(const QString&)), 02051 deviceid, SLOT( SetIP(const QString&))); 02052 connect(card, SIGNAL(NewValue(const QString&)), 02053 deviceid, SLOT( SetCard(const QString&))); 02054 connect(tuner, SIGNAL(NewValue(const QString&)), 02055 deviceid, SLOT( SetTuner(const QString&))); 02056 02057 connect(deviceid, SIGNAL(LoadedIP(const QString&)), 02058 ip, SLOT( LoadValue(const QString&))); 02059 connect(deviceid, SIGNAL(LoadedCard(const QString&)), 02060 card, SLOT( LoadValue(const QString&))); 02061 connect(deviceid, SIGNAL(LoadedTuner(const QString&)), 02062 tuner, SLOT( LoadValue(const QString&))); 02063 02064 }; 02065 02066 V4LConfigurationGroup::V4LConfigurationGroup(CaptureCard& a_parent) : 02067 VerticalConfigurationGroup(false, true, false, false), 02068 parent(a_parent), 02069 cardinfo(new TransLabelSetting()), vbidev(new VBIDevice(parent)) 02070 { 02071 QString drv = "(?!ivtv|hdpvr|(saa7164(.*)))"; 02072 VideoDevice *device = new VideoDevice(parent, 0, 15, QString::null, drv); 02073 HorizontalConfigurationGroup *audgrp = 02074 new HorizontalConfigurationGroup(false, false, true, true); 02075 02076 cardinfo->setLabel(tr("Probed info")); 02077 audgrp->addChild(new AudioRateLimit(parent)); 02078 audgrp->addChild(new SkipBtAudio(parent)); 02079 02080 addChild(device); 02081 addChild(cardinfo); 02082 addChild(vbidev); 02083 addChild(new AudioDevice(parent)); 02084 addChild(audgrp); 02085 02086 connect(device, SIGNAL(valueChanged(const QString&)), 02087 this, SLOT( probeCard( const QString&))); 02088 02089 probeCard(device->getValue()); 02090 }; 02091 02092 void V4LConfigurationGroup::probeCard(const QString &device) 02093 { 02094 QString cn = tr("Failed to open"), ci = cn, dn = QString::null; 02095 02096 QByteArray adevice = device.toAscii(); 02097 int videofd = open(adevice.constData(), O_RDWR); 02098 if (videofd >= 0) 02099 { 02100 if (!CardUtil::GetV4LInfo(videofd, cn, dn)) 02101 ci = cn = tr("Failed to probe"); 02102 else if (!dn.isEmpty()) 02103 ci = cn + " [" + dn + "]"; 02104 close(videofd); 02105 } 02106 02107 cardinfo->setValue(ci); 02108 vbidev->setFilter(cn, dn); 02109 } 02110 02111 02112 MPEGConfigurationGroup::MPEGConfigurationGroup(CaptureCard &a_parent) : 02113 VerticalConfigurationGroup(false, true, false, false), 02114 parent(a_parent), 02115 device(NULL), vbidevice(NULL), 02116 cardinfo(new TransLabelSetting()) 02117 { 02118 QString drv = "ivtv|(saa7164(.*))"; 02119 device = new VideoDevice(parent, 0, 15, QString::null, drv); 02120 vbidevice = new VBIDevice(parent); 02121 vbidevice->setVisible(false); 02122 02123 cardinfo->setLabel(tr("Probed info")); 02124 02125 addChild(device); 02126 addChild(vbidevice); 02127 addChild(cardinfo); 02128 addChild(new ChannelTimeout(parent, 12000, 2000)); 02129 02130 connect(device, SIGNAL(valueChanged(const QString&)), 02131 this, SLOT( probeCard( const QString&))); 02132 02133 probeCard(device->getValue()); 02134 } 02135 02136 void MPEGConfigurationGroup::probeCard(const QString &device) 02137 { 02138 QString cn = tr("Failed to open"), ci = cn, dn = QString::null; 02139 02140 QByteArray adevice = device.toAscii(); 02141 int videofd = open(adevice.constData(), O_RDWR); 02142 if (videofd >= 0) 02143 { 02144 if (!CardUtil::GetV4LInfo(videofd, cn, dn)) 02145 ci = cn = tr("Failed to probe"); 02146 else if (!dn.isEmpty()) 02147 ci = cn + " [" + dn + "]"; 02148 close(videofd); 02149 } 02150 02151 cardinfo->setValue(ci); 02152 vbidevice->setVisible(dn!="ivtv"); 02153 vbidevice->setFilter(cn, dn); 02154 } 02155 02156 DemoConfigurationGroup::DemoConfigurationGroup(CaptureCard &a_parent) : 02157 VerticalConfigurationGroup(false, true, false, false), 02158 parent(a_parent), 02159 info(new TransLabelSetting()), size(new TransLabelSetting()) 02160 { 02161 FileDevice *device = new FileDevice(parent); 02162 device->setHelpText(tr("A local MPEG file used to simulate a recording." 02163 " Must be entered as file:/path/movie.mpg")); 02164 device->addSelection("file:/"); 02165 addChild(device); 02166 02167 addChild(new EmptyAudioDevice(parent)); 02168 addChild(new EmptyVBIDevice(parent)); 02169 02170 info->setLabel(tr("File info")); 02171 addChild(info); 02172 02173 size->setLabel(tr("File size")); 02174 addChild(size); 02175 02176 connect(device, SIGNAL(valueChanged(const QString&)), 02177 this, SLOT( probeCard( const QString&))); 02178 02179 probeCard(device->getValue()); 02180 } 02181 02182 void DemoConfigurationGroup::probeCard(const QString &device) 02183 { 02184 if (!device.startsWith("file:", Qt::CaseInsensitive)) 02185 { 02186 info->setValue(""); 02187 size->setValue(""); 02188 return; 02189 } 02190 02191 02192 QString ci, cs; 02193 QFileInfo fileInfo(device.mid(5)); 02194 if (fileInfo.exists()) 02195 { 02196 if (fileInfo.isReadable() && (fileInfo.isFile())) 02197 { 02198 ci = HTTPRequest::TestMimeType(fileInfo.absoluteFilePath()); 02199 cs = tr("%1 MB").arg(fileInfo.size() / 1024 / 1024); 02200 } 02201 else 02202 ci = tr("File not readable"); 02203 } 02204 else 02205 { 02206 ci = tr("File does not exist"); 02207 } 02208 02209 info->setValue(ci); 02210 size->setValue(cs); 02211 } 02212 02213 HDPVRConfigurationGroup::HDPVRConfigurationGroup(CaptureCard &a_parent) : 02214 VerticalConfigurationGroup(false, true, false, false), 02215 parent(a_parent), cardinfo(new TransLabelSetting()), 02216 audioinput(new TunerCardAudioInput(parent, QString::null, "HDPVR")) 02217 { 02218 VideoDevice *device = 02219 new VideoDevice(parent, 0, 15, QString::null, "hdpvr"); 02220 02221 cardinfo->setLabel(tr("Probed info")); 02222 02223 addChild(device); 02224 addChild(new EmptyAudioDevice(parent)); 02225 addChild(new EmptyVBIDevice(parent)); 02226 addChild(cardinfo); 02227 addChild(audioinput); 02228 addChild(new ChannelTimeout(parent, 12000, 2000)); 02229 02230 connect(device, SIGNAL(valueChanged(const QString&)), 02231 this, SLOT( probeCard( const QString&))); 02232 02233 probeCard(device->getValue()); 02234 } 02235 02236 void HDPVRConfigurationGroup::probeCard(const QString &device) 02237 { 02238 QString cn = tr("Failed to open"), ci = cn, dn = QString::null; 02239 02240 int videofd = open(device.toLocal8Bit().constData(), O_RDWR); 02241 if (videofd >= 0) 02242 { 02243 if (!CardUtil::GetV4LInfo(videofd, cn, dn)) 02244 ci = cn = tr("Failed to probe"); 02245 else if (!dn.isEmpty()) 02246 ci = cn + " [" + dn + "]"; 02247 close(videofd); 02248 } 02249 02250 cardinfo->setValue(ci); 02251 audioinput->fillSelections(device); 02252 } 02253 02254 CaptureCardGroup::CaptureCardGroup(CaptureCard &parent) : 02255 TriggeredConfigurationGroup(true, true, false, false) 02256 { 02257 setLabel(QObject::tr("Capture Card Setup")); 02258 02259 CardType* cardtype = new CardType(parent); 02260 addChild(cardtype); 02261 02262 setTrigger(cardtype); 02263 setSaveAll(false); 02264 02265 #ifdef USING_V4L2 02266 addTarget("V4L", new V4LConfigurationGroup(parent)); 02267 # ifdef USING_IVTV 02268 addTarget("MPEG", new MPEGConfigurationGroup(parent)); 02269 # endif // USING_IVTV 02270 # ifdef USING_HDPVR 02271 addTarget("HDPVR", new HDPVRConfigurationGroup(parent)); 02272 # endif // USING_HDPVR 02273 #endif // USING_V4L2 02274 02275 #ifdef USING_DVB 02276 addTarget("DVB", new DVBConfigurationGroup(parent)); 02277 #endif // USING_DVB 02278 02279 #ifdef USING_FIREWIRE 02280 addTarget("FIREWIRE", new FirewireConfigurationGroup(parent)); 02281 #endif // USING_FIREWIRE 02282 02283 #ifdef USING_HDHOMERUN 02284 addTarget("HDHOMERUN", new HDHomeRunConfigurationGroup(parent)); 02285 #endif // USING_HDHOMERUN 02286 02287 #ifdef USING_IPTV 02288 addTarget("FREEBOX", new IPTVConfigurationGroup(parent)); 02289 #endif // USING_IPTV 02290 02291 #ifdef USING_ASI 02292 addTarget("ASI", new ASIConfigurationGroup(parent)); 02293 #endif // USING_ASI 02294 02295 #ifdef USING_CETON 02296 addTarget("CETON", new CetonConfigurationGroup(parent)); 02297 #endif // USING_CETON 02298 02299 // for testing without any actual tuner hardware: 02300 addTarget("IMPORT", new ImportConfigurationGroup(parent)); 02301 addTarget("DEMO", new DemoConfigurationGroup(parent)); 02302 } 02303 02304 void CaptureCardGroup::triggerChanged(const QString& value) 02305 { 02306 QString own = (value == "MJPEG" || value == "GO7007") ? "V4L" : value; 02307 TriggeredConfigurationGroup::triggerChanged(own); 02308 } 02309 02310 CaptureCard::CaptureCard(bool use_card_group) 02311 : id(new ID), instance_count(0) 02312 { 02313 addChild(id); 02314 if (use_card_group) 02315 addChild(new CaptureCardGroup(*this)); 02316 addChild(new Hostname(*this)); 02317 } 02318 02319 QString CaptureCard::GetRawCardType(void) const 02320 { 02321 int cardid = getCardID(); 02322 if (cardid <= 0) 02323 return QString::null; 02324 return CardUtil::GetRawCardType(cardid); 02325 } 02326 02327 void CaptureCard::fillSelections(SelectSetting *setting) 02328 { 02329 MSqlQuery query(MSqlQuery::InitCon()); 02330 QString qstr = 02331 "SELECT cardid, videodevice, cardtype " 02332 "FROM capturecard " 02333 "WHERE hostname = :HOSTNAME " 02334 "ORDER BY cardid"; 02335 02336 query.prepare(qstr); 02337 query.bindValue(":HOSTNAME", gCoreContext->GetHostName()); 02338 02339 if (!query.exec()) 02340 { 02341 MythDB::DBError("CaptureCard::fillSelections", query); 02342 return; 02343 } 02344 02345 QMap<QString, uint> device_refs; 02346 while (query.next()) 02347 { 02348 uint cardid = query.value(0).toUInt(); 02349 QString videodevice = query.value(1).toString(); 02350 QString cardtype = query.value(2).toString(); 02351 02352 bool sharable = CardUtil::IsTunerSharingCapable(cardtype.toUpper()); 02353 02354 if (sharable && (1 != ++device_refs[videodevice])) 02355 continue; 02356 02357 QString label = CardUtil::GetDeviceLabel(cardtype, videodevice); 02358 setting->addSelection(label, QString::number(cardid)); 02359 } 02360 } 02361 02362 void CaptureCard::loadByID(int cardid) 02363 { 02364 id->setValue(cardid); 02365 Load(); 02366 02367 // Update instance count for cloned cards. 02368 uint new_cnt = 0; 02369 if (cardid > 0) 02370 { 02371 QString type = CardUtil::GetRawCardType(cardid); 02372 if (CardUtil::IsTunerSharingCapable(type)) 02373 { 02374 QString dev = CardUtil::GetVideoDevice(cardid); 02375 vector<uint> cardids = CardUtil::GetCardIDs(dev, type); 02376 new_cnt = cardids.size(); 02377 } 02378 } 02379 instance_count = new_cnt; 02380 } 02381 02382 void CaptureCard::Save(void) 02383 { 02384 uint init_cardid = getCardID(); 02385 02387 02388 ConfigurationWizard::Save(); 02389 02391 02392 uint cardid = getCardID(); 02393 QString type = CardUtil::GetRawCardType(cardid); 02394 if (!CardUtil::IsTunerSharingCapable(type)) 02395 return; 02396 02397 QString init_dev = CardUtil::GetVideoDevice(cardid); 02398 if (init_dev.isEmpty()) 02399 { 02400 LOG(VB_GENERAL, LOG_ERR, 02401 QString("Cannot clone card #%1 with empty videodevice") 02402 .arg(cardid)); 02403 return; 02404 } 02405 vector<uint> cardids = CardUtil::GetCardIDs(init_dev, type); 02406 02407 if (!instance_count) 02408 { 02409 instance_count = (init_cardid) ? 02410 max((size_t)1, cardids.size()) : kDefaultMultirecCount; 02411 } 02412 uint cloneCount = instance_count - 1; 02413 02414 if (!init_cardid) 02415 init_cardid = cardid; 02416 02417 // Delete old clone cards as required. 02418 for (uint i = cardids.size() - 1; (i > cloneCount) && cardids.size(); i--) 02419 { 02420 CardUtil::DeleteCard(cardids.back()); 02421 cardids.pop_back(); 02422 } 02423 02424 // Make sure clones & original all share an input group 02425 if (cloneCount && !CardUtil::CreateInputGroupIfNeeded(cardid)) 02426 return; 02427 02428 // Clone this config to existing clone cards. 02429 for (uint i = 0; i < cardids.size(); i++) 02430 { 02431 if (cardids[i] != init_cardid) 02432 CardUtil::CloneCard(init_cardid, cardids[i]); 02433 } 02434 02435 // Create new clone cards as required. 02436 for (uint i = cardids.size(); i < cloneCount + 1; i++) 02437 { 02438 CardUtil::CloneCard(init_cardid, 0); 02439 } 02440 } 02441 02442 void CaptureCard::reload(void) 02443 { 02444 if (getCardID() == 0) 02445 { 02446 Save(); 02447 Load(); 02448 } 02449 } 02450 02451 CardType::CardType(const CaptureCard &parent) : 02452 ComboBoxSetting(this), 02453 CaptureCardDBStorage(this, parent, "cardtype") 02454 { 02455 setLabel(QObject::tr("Card type")); 02456 setHelpText(QObject::tr("Change the cardtype to the appropriate type for " 02457 "the capture card you are configuring.")); 02458 fillSelections(this); 02459 } 02460 02461 void CardType::fillSelections(SelectSetting* setting) 02462 { 02463 #ifdef USING_V4L2 02464 setting->addSelection( 02465 QObject::tr("Analog V4L capture card"), "V4L"); 02466 setting->addSelection( 02467 QObject::tr("MJPEG capture card (Matrox G200, DC10)"), "MJPEG"); 02468 # ifdef USING_IVTV 02469 setting->addSelection( 02470 QObject::tr("MPEG-2 encoder card"), "MPEG"); 02471 # endif // USING_IVTV 02472 # ifdef USING_HDPVR 02473 setting->addSelection( 02474 QObject::tr("H.264 encoder card (HD-PVR)"), "HDPVR"); 02475 # endif // USING_HDPVR 02476 #endif // USING_V4L2 02477 02478 #ifdef USING_DVB 02479 setting->addSelection( 02480 QObject::tr("DVB DTV capture card (v3.x)"), "DVB"); 02481 #endif // USING_DVB 02482 02483 #ifdef USING_FIREWIRE 02484 setting->addSelection( 02485 QObject::tr("FireWire cable box"), "FIREWIRE"); 02486 #endif // USING_FIREWIRE 02487 02488 #ifdef USING_V4L2 02489 setting->addSelection( 02490 QObject::tr("USB MPEG-4 encoder box (Plextor ConvertX, etc)"), 02491 "GO7007"); 02492 #endif // USING_V4L2 02493 02494 #ifdef USING_HDHOMERUN 02495 setting->addSelection( 02496 QObject::tr("HDHomeRun DTV tuner box"), "HDHOMERUN"); 02497 #endif // USING_HDHOMERUN 02498 02499 #ifdef USING_IPTV 02500 setting->addSelection(QObject::tr("Network recorder"), "FREEBOX"); 02501 #endif // USING_IPTV 02502 02503 #ifdef USING_ASI 02504 setting->addSelection(QObject::tr("DVEO ASI recorder"), "ASI"); 02505 #endif 02506 02507 #ifdef USING_CETON 02508 setting->addSelection( 02509 QObject::tr("Ceton Cablecard tuner "), "CETON"); 02510 #endif // USING_CETON 02511 02512 setting->addSelection(QObject::tr("Import test recorder"), "IMPORT"); 02513 setting->addSelection(QObject::tr("Demo test recorder"), "DEMO"); 02514 } 02515 02516 class CardID : public SelectLabelSetting, public CardInputDBStorage 02517 { 02518 public: 02519 CardID(const CardInput &parent) : 02520 SelectLabelSetting(this), CardInputDBStorage(this, parent, "cardid") 02521 { 02522 setLabel(QObject::tr("Capture device")); 02523 }; 02524 02525 virtual void Load(void) 02526 { 02527 fillSelections(); 02528 CardInputDBStorage::Load(); 02529 }; 02530 02531 void fillSelections() { 02532 CaptureCard::fillSelections(this); 02533 }; 02534 }; 02535 02536 class InputDisplayName : public LineEditSetting, public CardInputDBStorage 02537 { 02538 public: 02539 InputDisplayName(const CardInput &parent) : 02540 LineEditSetting(this), 02541 CardInputDBStorage(this, parent, "displayname") 02542 { 02543 setLabel(QObject::tr("Display name (optional)")); 02544 setHelpText(QObject::tr( 02545 "This name is displayed on screen when Live TV begins " 02546 "and when changing the selected input or card. If you " 02547 "use this, make sure the information is unique for " 02548 "each input.")); 02549 }; 02550 }; 02551 02552 class SourceID : public ComboBoxSetting, public CardInputDBStorage 02553 { 02554 public: 02555 SourceID(const CardInput &parent) : 02556 ComboBoxSetting(this), CardInputDBStorage(this, parent, "sourceid") 02557 { 02558 setLabel(QObject::tr("Video source")); 02559 addSelection(QObject::tr("(None)"), "0"); 02560 }; 02561 02562 virtual void Load(void) 02563 { 02564 fillSelections(); 02565 CardInputDBStorage::Load(); 02566 }; 02567 02568 void fillSelections() { 02569 clearSelections(); 02570 addSelection(QObject::tr("(None)"), "0"); 02571 VideoSource::fillSelections(this); 02572 }; 02573 }; 02574 02575 class InputName : public LabelSetting, public CardInputDBStorage 02576 { 02577 public: 02578 InputName(const CardInput &parent) : 02579 LabelSetting(this), CardInputDBStorage(this, parent, "inputname") 02580 { 02581 setLabel(QObject::tr("Input")); 02582 }; 02583 }; 02584 02585 class InputGroup : public TransComboBoxSetting 02586 { 02587 public: 02588 InputGroup(const CardInput &parent, uint group_num) : 02589 TransComboBoxSetting(false), cardinput(parent), 02590 groupnum(group_num), groupid(0) 02591 { 02592 setLabel(QObject::tr("Input group") + 02593 QString(" %1").arg(groupnum + 1)); 02594 setHelpText(QObject::tr( 02595 "Leave as 'Generic' unless this input is shared with " 02596 "another device. Only one of the inputs in an input " 02597 "group will be allowed to record at any given time.")); 02598 } 02599 02600 virtual void Load(void); 02601 02602 virtual void Save(void) 02603 { 02604 uint inputid = cardinput.getInputID(); 02605 uint new_groupid = getValue().toUInt(); 02606 02607 if (groupid) 02608 CardUtil::UnlinkInputGroup(inputid, groupid); 02609 02610 if (new_groupid) 02611 { 02612 if (CardUtil::UnlinkInputGroup(inputid, new_groupid)) 02613 CardUtil::LinkInputGroup(inputid, new_groupid); 02614 } 02615 } 02616 02617 private: 02618 const CardInput &cardinput; 02619 uint groupnum; 02620 uint groupid; 02621 }; 02622 02623 void InputGroup::Load(void) 02624 { 02625 #if 0 02626 LOG(VB_GENERAL, LOG_DEBUG, QString("InputGroup::Load() %1 %2") 02627 .arg(groupnum).arg(cardinput.getInputID())); 02628 #endif 02629 02630 uint inputid = cardinput.getInputID(); 02631 QMap<uint, uint> grpcnt; 02632 vector<QString> names; 02633 vector<uint> grpid; 02634 vector<uint> selected_groupids; 02635 02636 names.push_back(QObject::tr("Generic")); 02637 grpid.push_back(0); 02638 grpcnt[0]++; 02639 02640 MSqlQuery query(MSqlQuery::InitCon()); 02641 query.prepare( 02642 "SELECT cardinputid, inputgroupid, inputgroupname " 02643 "FROM inputgroup " 02644 "ORDER BY inputgroupid, cardinputid, inputgroupname"); 02645 02646 if (!query.exec()) 02647 { 02648 MythDB::DBError("InputGroup::Load()", query); 02649 } 02650 else 02651 { 02652 while (query.next()) 02653 { 02654 uint groupid = query.value(1).toUInt(); 02655 if (inputid && (query.value(0).toUInt() == inputid)) 02656 selected_groupids.push_back(groupid); 02657 02658 grpcnt[groupid]++; 02659 02660 if (grpcnt[groupid] == 1) 02661 { 02662 names.push_back(query.value(2).toString()); 02663 grpid.push_back(groupid); 02664 } 02665 } 02666 } 02667 02668 // makes sure we select something 02669 groupid = 0; 02670 if (groupnum < selected_groupids.size()) 02671 groupid = selected_groupids[groupnum]; 02672 02673 #if 0 02674 LOG(VB_GENERAL, LOG_DEBUG, QString("Group num: %1 id: %2") 02675 .arg(groupnum).arg(groupid)); 02676 { 02677 QString msg; 02678 for (uint i = 0; i < selected_groupids.size(); i++) 02679 msg += QString("%1 ").arg(selected_groupids[i]); 02680 LOG(VB_GENERAL, LOG_DEBUG, msg); 02681 } 02682 #endif 02683 02684 // add selections to combobox 02685 clearSelections(); 02686 uint index = 0; 02687 for (uint i = 0; i < names.size(); i++) 02688 { 02689 bool sel = (groupid == grpid[i]); 02690 index = (sel) ? i : index; 02691 02692 #if 0 02693 LOG(VB_GENERAL, LOG_DEBUG, QString("grpid %1, name '%2', i %3, s %4") 02694 .arg(grpid[i]).arg(names[i]) .arg(index).arg(sel ? "T" : "F")); 02695 #endif 02696 02697 addSelection(names[i], QString::number(grpid[i]), sel); 02698 } 02699 02700 #if 0 02701 LOG(VB_GENERAL, LOG_DEBUG, QString("Group index: %1").arg(index)); 02702 #endif 02703 02704 if (!names.empty()) 02705 setValue(index); 02706 } 02707 02708 class QuickTune : public ComboBoxSetting, public CardInputDBStorage 02709 { 02710 public: 02711 QuickTune(const CardInput &parent) : 02712 ComboBoxSetting(this), CardInputDBStorage(this, parent, "quicktune") 02713 { 02714 setLabel(QObject::tr("Use quick tuning")); 02715 addSelection(QObject::tr("Never"), "0", true); 02716 addSelection(QObject::tr("Live TV only"), "1", false); 02717 addSelection(QObject::tr("Always"), "2", false); 02718 setHelpText(QObject::tr( 02719 "If enabled, MythTV will tune using only the " 02720 "MPEG program number. The program numbers " 02721 "change more often than DVB or ATSC tuning " 02722 "parameters, so this is slightly less reliable. " 02723 "This will also inhibit EIT gathering during " 02724 "Live TV and recording.")); 02725 }; 02726 }; 02727 02728 class ExternalChannelCommand : 02729 public LineEditSetting, public CardInputDBStorage 02730 { 02731 public: 02732 ExternalChannelCommand(const CardInput &parent) : 02733 LineEditSetting(this), 02734 CardInputDBStorage(this, parent, "externalcommand") 02735 { 02736 setLabel(QObject::tr("External channel change command")); 02737 setValue(""); 02738 setHelpText(QObject::tr("If specified, this command will be run to " 02739 "change the channel for inputs which have an external " 02740 "tuner device such as a cable box. The first argument " 02741 "will be the channel number.")); 02742 }; 02743 }; 02744 02745 class PresetTuner : public LineEditSetting, public CardInputDBStorage 02746 { 02747 public: 02748 PresetTuner(const CardInput &parent) : 02749 LineEditSetting(this), 02750 CardInputDBStorage(this, parent, "tunechan") 02751 { 02752 setLabel(QObject::tr("Preset tuner to channel")); 02753 setValue(""); 02754 setHelpText(QObject::tr("Leave this blank unless you have an external " 02755 "tuner that is connected to the tuner input of your card. " 02756 "If so, you will need to specify the preset channel for " 02757 "the signal (normally 3 or 4).")); 02758 }; 02759 }; 02760 02761 void StartingChannel::SetSourceID(const QString &sourceid) 02762 { 02763 clearSelections(); 02764 if (sourceid.isEmpty() || !sourceid.toUInt()) 02765 return; 02766 02767 // Get the existing starting channel 02768 QString startChan = CardUtil::GetStartingChannel(getInputID()); 02769 02770 DBChanList channels = ChannelUtil::GetAllChannels(sourceid.toUInt()); 02771 02772 if (channels.empty()) 02773 { 02774 addSelection(tr("Please add channels to this source"), 02775 startChan.isEmpty() ? "" : startChan); 02776 return; 02777 } 02778 02779 // If there are channels sort them, then add theme 02780 // (selecting the old start channel if it is there). 02781 QString order = gCoreContext->GetSetting("ChannelOrdering", "channum"); 02782 ChannelUtil::SortChannels(channels, order); 02783 bool has_visible = false, found_existing = false; 02784 for (uint i = 0; i < channels.size() && !has_visible; i++) 02785 has_visible |= channels[i].visible; 02786 02787 for (uint i = 0; i < channels.size(); i++) 02788 { 02789 const QString channum = channels[i].channum; 02790 bool sel = channum == startChan; 02791 if (!has_visible || channels[i].visible || sel) 02792 { 02793 addSelection(channum, channum, sel); 02794 found_existing |= sel; 02795 } 02796 } 02797 } 02798 02799 class InputPriority : public SpinBoxSetting, public CardInputDBStorage 02800 { 02801 public: 02802 InputPriority(const CardInput &parent) : 02803 SpinBoxSetting(this, -99, 99, 1), 02804 CardInputDBStorage(this, parent, "recpriority") 02805 { 02806 setLabel(QObject::tr("Input priority")); 02807 setValue(0); 02808 setHelpText(QObject::tr("If the input priority is not equal for " 02809 "all inputs, the scheduler may choose to record a show " 02810 "at a later time so that it can record on an input with " 02811 "a higher value.")); 02812 }; 02813 }; 02814 02815 class ScheduleOrder : public SpinBoxSetting, public CardInputDBStorage 02816 { 02817 public: 02818 ScheduleOrder(const CardInput &parent, int _value) : 02819 SpinBoxSetting(this, 0, 99, 1), 02820 CardInputDBStorage(this, parent, "schedorder") 02821 { 02822 setLabel(QObject::tr("Schedule order")); 02823 setValue(_value); 02824 setHelpText(QObject::tr("If priorities and other factors are equal " 02825 "the scheduler will choose the available " 02826 "input with the lowest, non-zero value. " 02827 "Setting this value to zero will make the " 02828 "input unavailable to the scheduler.")); 02829 }; 02830 }; 02831 02832 class LiveTVOrder : public SpinBoxSetting, public CardInputDBStorage 02833 { 02834 public: 02835 LiveTVOrder(const CardInput &parent, int _value) : 02836 SpinBoxSetting(this, 0, 99, 1), 02837 CardInputDBStorage(this, parent, "livetvorder") 02838 { 02839 setLabel(QObject::tr("Live TV order")); 02840 setValue(_value); 02841 setHelpText(QObject::tr("When entering Live TV, the available, local " 02842 "input with the lowest, non-zero value will " 02843 "be used. If no local inputs are available, " 02844 "the available, remote input with the lowest, " 02845 "non-zero value will be used. " 02846 "Setting this value to zero will make the " 02847 "input unavailable to live TV.")); 02848 }; 02849 }; 02850 02851 class DishNetEIT : public CheckBoxSetting, public CardInputDBStorage 02852 { 02853 public: 02854 DishNetEIT(const CardInput &parent) : 02855 CheckBoxSetting(this), 02856 CardInputDBStorage(this, parent, "dishnet_eit") 02857 { 02858 setLabel(QObject::tr("Use DishNet long-term EIT data")); 02859 setValue(false); 02860 setHelpText( 02861 QObject::tr( 02862 "If you point your satellite dish toward DishNet's birds, " 02863 "you may wish to enable this feature. For best results, " 02864 "enable general EIT collection as well.")); 02865 }; 02866 }; 02867 02868 CardInput::CardInput(bool isDTVcard, bool isDVBcard, 02869 bool isNewInput, int _cardid) : 02870 id(new ID()), 02871 cardid(new CardID(*this)), 02872 inputname(new InputName(*this)), 02873 sourceid(new SourceID(*this)), 02874 startchan(new StartingChannel(*this)), 02875 scan(new TransButtonSetting()), 02876 srcfetch(new TransButtonSetting()), 02877 externalInputSettings(new DiSEqCDevSettings()), 02878 inputgrp0(new InputGroup(*this, 0)), 02879 inputgrp1(new InputGroup(*this, 1)) 02880 { 02881 addChild(id); 02882 02883 if (CardUtil::IsInNeedOfExternalInputConf(_cardid)) 02884 { 02885 addChild(new DTVDeviceConfigGroup(*externalInputSettings, 02886 _cardid, isNewInput)); 02887 } 02888 02889 ConfigurationGroup *basic = 02890 new VerticalConfigurationGroup(false, false, true, true); 02891 02892 basic->setLabel(QObject::tr("Connect source to input")); 02893 02894 basic->addChild(cardid); 02895 basic->addChild(inputname); 02896 basic->addChild(new InputDisplayName(*this)); 02897 basic->addChild(sourceid); 02898 02899 if (!isDTVcard) 02900 { 02901 basic->addChild(new ExternalChannelCommand(*this)); 02902 basic->addChild(new PresetTuner(*this)); 02903 } 02904 else 02905 { 02906 ConfigurationGroup *chgroup = 02907 new HorizontalConfigurationGroup(false, false, true, true); 02908 chgroup->addChild(new QuickTune(*this)); 02909 if (isDVBcard) 02910 chgroup->addChild(new DishNetEIT(*this)); 02911 basic->addChild(chgroup); 02912 } 02913 02914 scan->setLabel(tr("Scan for channels")); 02915 scan->setHelpText( 02916 tr("Use channel scanner to find channels for this input.")); 02917 02918 srcfetch->setLabel(tr("Fetch channels from listings source")); 02919 srcfetch->setHelpText( 02920 tr("This uses the listings data source to " 02921 "provide the channels for this input.") + " " + 02922 tr("This can take a long time to run.")); 02923 02924 ConfigurationGroup *sgrp = 02925 new HorizontalConfigurationGroup(false, false, true, true); 02926 sgrp->addChild(scan); 02927 sgrp->addChild(srcfetch); 02928 basic->addChild(sgrp); 02929 02930 basic->addChild(startchan); 02931 02932 addChild(basic); 02933 02934 ConfigurationGroup *interact = 02935 new VerticalConfigurationGroup(false, false, true, true); 02936 02937 interact->setLabel(QObject::tr("Interactions between inputs")); 02938 interact->addChild(new InputPriority(*this)); 02939 interact->addChild(new ScheduleOrder(*this, _cardid)); 02940 interact->addChild(new LiveTVOrder(*this, _cardid)); 02941 02942 TransButtonSetting *ingrpbtn = new TransButtonSetting("newgroup"); 02943 ingrpbtn->setLabel(QObject::tr("Create a New Input Group")); 02944 ingrpbtn->setHelpText( 02945 QObject::tr("Input groups are only needed when two or more cards " 02946 "share the same resource such as a FireWire card and " 02947 "an analog card input controlling the same set top box.")); 02948 interact->addChild(ingrpbtn); 02949 interact->addChild(inputgrp0); 02950 interact->addChild(inputgrp1); 02951 02952 addChild(interact); 02953 02954 setObjectName("CardInput"); 02955 SetSourceID("-1"); 02956 02957 connect(scan, SIGNAL(pressed()), SLOT(channelScanner())); 02958 connect(srcfetch, SIGNAL(pressed()), SLOT(sourceFetch())); 02959 connect(sourceid, SIGNAL(valueChanged(const QString&)), 02960 startchan,SLOT( SetSourceID (const QString&))); 02961 connect(sourceid, SIGNAL(valueChanged(const QString&)), 02962 this, SLOT( SetSourceID (const QString&))); 02963 connect(ingrpbtn, SIGNAL(pressed(QString)), 02964 this, SLOT( CreateNewInputGroup())); 02965 } 02966 02967 CardInput::~CardInput() 02968 { 02969 if (externalInputSettings) 02970 { 02971 delete externalInputSettings; 02972 externalInputSettings = NULL; 02973 } 02974 } 02975 02976 void CardInput::SetSourceID(const QString &sourceid) 02977 { 02978 uint cid = cardid->getValue().toUInt(); 02979 QString raw_card_type = CardUtil::GetRawCardType(cid); 02980 bool enable = (sourceid.toInt() > 0); 02981 scan->setEnabled(enable && !raw_card_type.isEmpty() && 02982 !CardUtil::IsUnscanable(raw_card_type)); 02983 srcfetch->setEnabled(enable); 02984 } 02985 02986 QString CardInput::getSourceName(void) const 02987 { 02988 return sourceid->getSelectionLabel(); 02989 } 02990 02991 void CardInput::CreateNewInputGroup(void) 02992 { 02993 QString new_name = QString::null; 02994 QString tmp_name = QString::null; 02995 02996 inputgrp0->Save(); 02997 inputgrp1->Save(); 02998 02999 while (true) 03000 { 03001 tmp_name = ""; 03002 bool ok = MythPopupBox::showGetTextPopup( 03003 GetMythMainWindow(), tr("Create Input Group"), 03004 tr("Enter new group name"), tmp_name); 03005 03006 new_name = tmp_name; 03007 03008 if (!ok) 03009 return; 03010 03011 if (new_name.isEmpty()) 03012 { 03013 MythPopupBox::showOkPopup( 03014 GetMythMainWindow(), tr("Error"), 03015 tr("Sorry, this Input Group name cannot be blank.")); 03016 continue; 03017 } 03018 03019 MSqlQuery query(MSqlQuery::InitCon()); 03020 query.prepare( 03021 "SELECT inputgroupname " 03022 "FROM inputgroup " 03023 "WHERE inputgroupname = :GROUPNAME"); 03024 query.bindValue(":GROUPNAME", new_name); 03025 03026 if (!query.exec()) 03027 { 03028 MythDB::DBError("CreateNewInputGroup 1", query); 03029 return; 03030 } 03031 03032 if (query.next()) 03033 { 03034 MythPopupBox::showOkPopup( 03035 GetMythMainWindow(), tr("Error"), 03036 tr("Sorry, this Input Group name is already in use.")); 03037 continue; 03038 } 03039 03040 break; 03041 } 03042 03043 uint inputgroupid = CardUtil::CreateInputGroup(new_name); 03044 03045 inputgrp0->Load(); 03046 inputgrp1->Load(); 03047 03048 if (!inputgrp0->getValue().toUInt()) 03049 { 03050 inputgrp0->setValue( 03051 inputgrp0->getValueIndex(QString::number(inputgroupid))); 03052 } 03053 else 03054 { 03055 inputgrp1->setValue( 03056 inputgrp1->getValueIndex(QString::number(inputgroupid))); 03057 } 03058 } 03059 03060 void CardInput::channelScanner(void) 03061 { 03062 uint srcid = sourceid->getValue().toUInt(); 03063 uint crdid = cardid->getValue().toUInt(); 03064 QString in = inputname->getValue(); 03065 03066 #ifdef USING_BACKEND 03067 uint num_channels_before = SourceUtil::GetChannelCount(srcid); 03068 03069 Save(); // save info for scanner. 03070 03071 QString cardtype = CardUtil::GetRawCardType(crdid); 03072 if (CardUtil::IsUnscanable(cardtype)) 03073 { 03074 LOG(VB_GENERAL, LOG_ERR, 03075 QString("Sorry, %1 cards do not yet support scanning.") 03076 .arg(cardtype)); 03077 return; 03078 } 03079 03080 ScanWizard *scanwizard = new ScanWizard(srcid, crdid, in); 03081 scanwizard->exec(false, true); 03082 scanwizard->deleteLater(); 03083 03084 if (SourceUtil::GetChannelCount(srcid)) 03085 startchan->SetSourceID(QString::number(srcid)); 03086 if (num_channels_before) 03087 { 03088 startchan->Load(); 03089 startchan->Save(); 03090 } 03091 #else 03092 LOG(VB_GENERAL, LOG_ERR, "You must compile the backend " 03093 "to be able to scan for channels"); 03094 #endif 03095 } 03096 03097 void CardInput::sourceFetch(void) 03098 { 03099 uint srcid = sourceid->getValue().toUInt(); 03100 uint crdid = cardid->getValue().toUInt(); 03101 03102 uint num_channels_before = SourceUtil::GetChannelCount(srcid); 03103 03104 if (crdid && srcid) 03105 { 03106 Save(); // save info for fetch.. 03107 03108 QString cardtype = CardUtil::GetRawCardType(crdid); 03109 03110 if (!CardUtil::IsCableCardPresent(crdid, cardtype) && 03111 !CardUtil::IsUnscanable(cardtype) && 03112 !CardUtil::IsEncoder(cardtype) && 03113 !num_channels_before) 03114 { 03115 LOG(VB_GENERAL, LOG_ERR, "Skipping channel fetch, you need to " 03116 "scan for channels first."); 03117 return; 03118 } 03119 03120 SourceUtil::UpdateChannelsFromListings(srcid, cardtype); 03121 } 03122 03123 if (SourceUtil::GetChannelCount(srcid)) 03124 startchan->SetSourceID(QString::number(srcid)); 03125 if (num_channels_before) 03126 { 03127 startchan->Load(); 03128 startchan->Save(); 03129 } 03130 } 03131 03132 QString CardInputDBStorage::GetWhereClause(MSqlBindings &bindings) const 03133 { 03134 QString cardinputidTag(":WHERECARDINPUTID"); 03135 03136 QString query("cardinputid = " + cardinputidTag); 03137 03138 bindings.insert(cardinputidTag, m_parent.getInputID()); 03139 03140 return query; 03141 } 03142 03143 QString CardInputDBStorage::GetSetClause(MSqlBindings &bindings) const 03144 { 03145 QString cardinputidTag(":SETCARDINPUTID"); 03146 QString colTag(":SET" + GetColumnName().toUpper()); 03147 03148 QString query("cardinputid = " + cardinputidTag + ", " + 03149 GetColumnName() + " = " + colTag); 03150 03151 bindings.insert(cardinputidTag, m_parent.getInputID()); 03152 bindings.insert(colTag, user->GetDBValue()); 03153 03154 return query; 03155 } 03156 03157 void CardInput::loadByID(int inputid) 03158 { 03159 id->setValue(inputid); 03160 externalInputSettings->Load(inputid); 03161 ConfigurationWizard::Load(); 03162 } 03163 03164 void CardInput::loadByInput(int _cardid, QString _inputname) 03165 { 03166 MSqlQuery query(MSqlQuery::InitCon()); 03167 query.prepare("SELECT cardinputid FROM cardinput " 03168 "WHERE cardid = :CARDID AND inputname = :INPUTNAME"); 03169 query.bindValue(":CARDID", _cardid); 03170 query.bindValue(":INPUTNAME", _inputname); 03171 03172 if (query.exec() && query.isActive() && query.next()) 03173 { 03174 loadByID(query.value(0).toInt()); 03175 } 03176 else 03177 { // create new input connection 03178 Load(); 03179 cardid->setValue(QString::number(_cardid)); 03180 inputname->setValue(_inputname); 03181 } 03182 } 03183 03184 void CardInput::Save(void) 03185 { 03186 03187 if (sourceid->getValue() == "0") 03188 { 03189 // "None" is represented by the lack of a row 03190 MSqlQuery query(MSqlQuery::InitCon()); 03191 query.prepare("DELETE FROM cardinput WHERE cardinputid = :INPUTID"); 03192 query.bindValue(":INPUTID", getInputID()); 03193 if (!query.exec()) 03194 MythDB::DBError("CardInput::Save", query); 03195 } 03196 else 03197 { 03198 ConfigurationWizard::Save(); 03199 externalInputSettings->Store(getInputID()); 03200 } 03201 03202 // Handle any cloning we may need to do 03203 uint src_cardid = cardid->getValue().toUInt(); 03204 QString type = CardUtil::GetRawCardType(src_cardid); 03205 if (CardUtil::IsTunerSharingCapable(type)) 03206 { 03207 vector<uint> clones = CardUtil::GetCloneCardIDs(src_cardid); 03208 if (clones.size() && CardUtil::CreateInputGroupIfNeeded(src_cardid)) 03209 { 03210 for (uint i = 0; i < clones.size(); i++) 03211 CardUtil::CloneCard(src_cardid, clones[i]); 03212 } 03213 } 03214 03215 // Delete any orphaned inputs 03216 CardUtil::DeleteOrphanInputs(); 03217 // Delete any unused input groups 03218 CardUtil::UnlinkInputGroup(0,0); 03219 } 03220 03221 int CardInputDBStorage::getInputID(void) const 03222 { 03223 return m_parent.getInputID(); 03224 } 03225 03226 int CaptureCardDBStorage::getCardID(void) const 03227 { 03228 return m_parent.getCardID(); 03229 } 03230 03231 CaptureCardEditor::CaptureCardEditor() : listbox(new ListBoxSetting(this)) 03232 { 03233 listbox->setLabel(tr("Capture cards")); 03234 addChild(listbox); 03235 } 03236 03237 DialogCode CaptureCardEditor::exec(void) 03238 { 03239 while (ConfigurationDialog::exec() == kDialogCodeAccepted) 03240 edit(); 03241 03242 return kDialogCodeRejected; 03243 } 03244 03245 void CaptureCardEditor::Load(void) 03246 { 03247 listbox->clearSelections(); 03248 listbox->addSelection(QObject::tr("(New capture card)"), "0"); 03249 listbox->addSelection(QObject::tr("(Delete all capture cards on %1)") 03250 .arg(gCoreContext->GetHostName()), "-1"); 03251 listbox->addSelection(QObject::tr("(Delete all capture cards)"), "-2"); 03252 CaptureCard::fillSelections(listbox); 03253 } 03254 03255 MythDialog* CaptureCardEditor::dialogWidget(MythMainWindow* parent, 03256 const char* widgetName) 03257 { 03258 dialog = ConfigurationDialog::dialogWidget(parent, widgetName); 03259 connect(dialog, SIGNAL(menuButtonPressed()), this, SLOT(menu())); 03260 connect(dialog, SIGNAL(editButtonPressed()), this, SLOT(edit())); 03261 connect(dialog, SIGNAL(deleteButtonPressed()), this, SLOT(del())); 03262 return dialog; 03263 } 03264 03265 void CaptureCardEditor::menu(void) 03266 { 03267 if (!listbox->getValue().toInt()) 03268 { 03269 CaptureCard cc; 03270 cc.exec(); 03271 } 03272 else 03273 { 03274 DialogCode val = MythPopupBox::Show2ButtonPopup( 03275 GetMythMainWindow(), 03276 "", 03277 tr("Capture Card Menu"), 03278 tr("Edit..."), 03279 tr("Delete..."), 03280 kDialogCodeButton0); 03281 03282 if (kDialogCodeButton0 == val) 03283 edit(); 03284 else if (kDialogCodeButton1 == val) 03285 del(); 03286 } 03287 } 03288 03289 void CaptureCardEditor::edit(void) 03290 { 03291 const int cardid = listbox->getValue().toInt(); 03292 if (-1 == cardid) 03293 { 03294 DialogCode val = MythPopupBox::Show2ButtonPopup( 03295 GetMythMainWindow(), "", 03296 tr("Are you sure you want to delete " 03297 "ALL capture cards on %1?").arg(gCoreContext->GetHostName()), 03298 tr("Yes, delete capture cards"), 03299 tr("No, don't"), kDialogCodeButton1); 03300 03301 if (kDialogCodeButton0 == val) 03302 { 03303 MSqlQuery cards(MSqlQuery::InitCon()); 03304 03305 cards.prepare( 03306 "SELECT cardid " 03307 "FROM capturecard " 03308 "WHERE hostname = :HOSTNAME"); 03309 cards.bindValue(":HOSTNAME", gCoreContext->GetHostName()); 03310 03311 if (!cards.exec() || !cards.isActive()) 03312 { 03313 MythPopupBox::showOkPopup( 03314 GetMythMainWindow(), 03315 tr("Error getting list of cards for this host"), 03316 tr("Unable to delete capturecards for %1") 03317 .arg(gCoreContext->GetHostName())); 03318 03319 MythDB::DBError("Selecting cardids for deletion", cards); 03320 return; 03321 } 03322 03323 while (cards.next()) 03324 CardUtil::DeleteCard(cards.value(0).toUInt()); 03325 } 03326 } 03327 else if (-2 == cardid) 03328 { 03329 DialogCode val = MythPopupBox::Show2ButtonPopup( 03330 GetMythMainWindow(), "", 03331 tr("Are you sure you want to delete " 03332 "ALL capture cards?"), 03333 tr("Yes, delete capture cards"), 03334 tr("No, don't"), kDialogCodeButton1); 03335 03336 if (kDialogCodeButton0 == val) 03337 { 03338 CardUtil::DeleteAllCards(); 03339 Load(); 03340 } 03341 } 03342 else 03343 { 03344 CaptureCard cc; 03345 if (cardid) 03346 cc.loadByID(cardid); 03347 cc.exec(); 03348 } 03349 } 03350 03351 void CaptureCardEditor::del(void) 03352 { 03353 DialogCode val = MythPopupBox::Show2ButtonPopup( 03354 GetMythMainWindow(), "", 03355 tr("Are you sure you want to delete this capture card?"), 03356 tr("Yes, delete capture card"), 03357 tr("No, don't"), kDialogCodeButton1); 03358 03359 if (kDialogCodeButton0 == val) 03360 { 03361 CardUtil::DeleteCard(listbox->getValue().toUInt()); 03362 Load(); 03363 } 03364 } 03365 03366 VideoSourceEditor::VideoSourceEditor() : listbox(new ListBoxSetting(this)) 03367 { 03368 listbox->setLabel(tr("Video sources")); 03369 addChild(listbox); 03370 } 03371 03372 MythDialog* VideoSourceEditor::dialogWidget(MythMainWindow* parent, 03373 const char* widgetName) 03374 { 03375 dialog = ConfigurationDialog::dialogWidget(parent, widgetName); 03376 connect(dialog, SIGNAL(menuButtonPressed()), this, SLOT(menu())); 03377 connect(dialog, SIGNAL(editButtonPressed()), this, SLOT(edit())); 03378 connect(dialog, SIGNAL(deleteButtonPressed()), this, SLOT(del())); 03379 return dialog; 03380 } 03381 03382 DialogCode VideoSourceEditor::exec(void) 03383 { 03384 while (ConfigurationDialog::exec() == kDialogCodeAccepted) 03385 edit(); 03386 03387 return kDialogCodeRejected; 03388 } 03389 03390 void VideoSourceEditor::Load(void) 03391 { 03392 listbox->clearSelections(); 03393 listbox->addSelection(QObject::tr("(New video source)"), "0"); 03394 listbox->addSelection(QObject::tr("(Delete all video sources)"), "-1"); 03395 VideoSource::fillSelections(listbox); 03396 } 03397 03398 void VideoSourceEditor::menu(void) 03399 { 03400 if (!listbox->getValue().toInt()) 03401 { 03402 VideoSource vs; 03403 vs.exec(); 03404 } 03405 else 03406 { 03407 DialogCode val = MythPopupBox::Show2ButtonPopup( 03408 GetMythMainWindow(), 03409 "", 03410 tr("Video Source Menu"), 03411 tr("Edit..."), 03412 tr("Delete..."), 03413 kDialogCodeButton0); 03414 03415 if (kDialogCodeButton0 == val) 03416 edit(); 03417 else if (kDialogCodeButton1 == val) 03418 del(); 03419 } 03420 } 03421 03422 void VideoSourceEditor::edit(void) 03423 { 03424 const int sourceid = listbox->getValue().toInt(); 03425 if (-1 == sourceid) 03426 { 03427 DialogCode val = MythPopupBox::Show2ButtonPopup( 03428 GetMythMainWindow(), "", 03429 tr("Are you sure you want to delete " 03430 "ALL video sources?"), 03431 tr("Yes, delete video sources"), 03432 tr("No, don't"), kDialogCodeButton1); 03433 03434 if (kDialogCodeButton0 == val) 03435 { 03436 SourceUtil::DeleteAllSources(); 03437 Load(); 03438 } 03439 } 03440 else 03441 { 03442 VideoSource vs; 03443 if (sourceid) 03444 vs.loadByID(sourceid); 03445 vs.exec(); 03446 } 03447 } 03448 03449 void VideoSourceEditor::del() 03450 { 03451 DialogCode val = MythPopupBox::Show2ButtonPopup( 03452 GetMythMainWindow(), "", 03453 tr("Are you sure you want to delete " 03454 "this video source?"), 03455 tr("Yes, delete video source"), 03456 tr("No, don't"), 03457 kDialogCodeButton1); 03458 03459 if (kDialogCodeButton0 == val) 03460 { 03461 SourceUtil::DeleteSource(listbox->getValue().toUInt()); 03462 Load(); 03463 } 03464 } 03465 03466 CardInputEditor::CardInputEditor() : listbox(new ListBoxSetting(this)) 03467 { 03468 listbox->setLabel(tr("Input connections")); 03469 addChild(listbox); 03470 } 03471 03472 DialogCode CardInputEditor::exec(void) 03473 { 03474 while (ConfigurationDialog::exec() == kDialogCodeAccepted) 03475 { 03476 if (!listbox) 03477 return kDialogCodeRejected; 03478 03479 if (cardinputs.empty()) 03480 return kDialogCodeRejected; 03481 03482 int val = listbox->getValue().toInt(); 03483 03484 if (cardinputs[val]) 03485 cardinputs[val]->exec(); 03486 } 03487 03488 return kDialogCodeRejected; 03489 } 03490 03491 void CardInputEditor::Load(void) 03492 { 03493 cardinputs.clear(); 03494 listbox->clearSelections(); 03495 03496 // We do this manually because we want custom labels. If 03497 // SelectSetting provided a facility to edit the labels, we 03498 // could use CaptureCard::fillSelections 03499 03500 MSqlQuery query(MSqlQuery::InitCon()); 03501 query.prepare( 03502 "SELECT cardid, videodevice, cardtype " 03503 "FROM capturecard " 03504 "WHERE hostname = :HOSTNAME " 03505 "ORDER BY cardid"); 03506 query.bindValue(":HOSTNAME", gCoreContext->GetHostName()); 03507 03508 if (!query.exec()) 03509 { 03510 MythDB::DBError("CardInputEditor::load", query); 03511 return; 03512 } 03513 03514 uint j = 0; 03515 QMap<QString, uint> device_refs; 03516 while (query.next()) 03517 { 03518 uint cardid = query.value(0).toUInt(); 03519 QString videodevice = query.value(1).toString(); 03520 QString cardtype = query.value(2).toString(); 03521 03522 bool sharable = CardUtil::IsTunerSharingCapable(cardtype.toUpper()); 03523 03524 if (sharable && (1 != ++device_refs[videodevice])) 03525 continue; 03526 03527 QStringList inputLabels; 03528 vector<CardInput*> cardInputs; 03529 03530 CardUtil::GetCardInputs(cardid, videodevice, cardtype, 03531 inputLabels, cardInputs); 03532 03533 for (int i = 0; i < inputLabels.size(); i++, j++) 03534 { 03535 cardinputs.push_back(cardInputs[i]); 03536 listbox->addSelection(inputLabels[i], QString::number(j)); 03537 } 03538 } 03539 } 03540 03541 #ifdef USING_DVB 03542 static QString remove_chaff(const QString &name) 03543 { 03544 // Trim off some of the chaff. 03545 QString short_name = name; 03546 if (short_name.left(14) == "LG Electronics") 03547 short_name = short_name.right(short_name.length() - 15); 03548 if (short_name.left(4) == "Oren") 03549 short_name = short_name.right(short_name.length() - 5); 03550 if (short_name.left(8) == "Nextwave") 03551 short_name = short_name.right(short_name.length() - 9); 03552 if (short_name.right(8).toLower() == "frontend") 03553 short_name = short_name.left(short_name.length() - 9); 03554 if (short_name.right(7) == "VSB/QAM") 03555 short_name = short_name.left(short_name.length() - 8); 03556 if (short_name.right(3) == "VSB") 03557 short_name = short_name.left(short_name.length() - 4); 03558 if (short_name.right(5) == "DVB-T") 03559 short_name = short_name.left(short_name.length() - 6); 03560 03561 // It would be infinitely better if DVB allowed us to query 03562 // the vendor ID. But instead we have to guess based on the 03563 // demodulator name. This means cards like the Air2PC HD5000 03564 // and DViCO Fusion HDTV cards are not identified correctly. 03565 short_name = short_name.simplified(); 03566 if (short_name.left(7).toLower() == "or51211") 03567 short_name = "pcHDTV HD-2000"; 03568 else if (short_name.left(7).toLower() == "or51132") 03569 short_name = "pcHDTV HD-3000"; 03570 else if (short_name.left(7).toLower() == "bcm3510") 03571 short_name = "Air2PC v1"; 03572 else if (short_name.left(7).toLower() == "nxt2002") 03573 short_name = "Air2PC v2"; 03574 else if (short_name.left(7).toLower() == "nxt200x") 03575 short_name = "Air2PC v2"; 03576 else if (short_name.left(8).toLower() == "lgdt3302") 03577 short_name = "DViCO HDTV3"; 03578 else if (short_name.left(8).toLower() == "lgdt3303") 03579 short_name = "DViCO v2 or Air2PC v3 or pcHDTV HD-5500"; 03580 03581 return short_name; 03582 } 03583 #endif // USING_DVB 03584 03585 void DVBConfigurationGroup::probeCard(const QString &videodevice) 03586 { 03587 if (videodevice.isEmpty()) 03588 { 03589 cardname->setValue(""); 03590 cardtype->setValue(""); 03591 return; 03592 } 03593 03594 if (parent.getCardID() && parent.GetRawCardType() != "DVB") 03595 { 03596 cardname->setValue(""); 03597 cardtype->setValue(""); 03598 return; 03599 } 03600 03601 #ifdef USING_DVB 03602 QString frontend_name = CardUtil::ProbeDVBFrontendName(videodevice); 03603 QString subtype = CardUtil::ProbeDVBType(videodevice); 03604 03605 QString err_open = tr("Could not open card %1").arg(videodevice); 03606 QString err_other = tr("Could not get card info for card %1").arg(videodevice); 03607 03608 switch (CardUtil::toCardType(subtype)) 03609 { 03610 case CardUtil::ERROR_OPEN: 03611 cardname->setValue(err_open); 03612 cardtype->setValue(strerror(errno)); 03613 break; 03614 case CardUtil::ERROR_UNKNOWN: 03615 cardname->setValue(err_other); 03616 cardtype->setValue("Unknown error"); 03617 break; 03618 case CardUtil::ERROR_PROBE: 03619 cardname->setValue(err_other); 03620 cardtype->setValue(strerror(errno)); 03621 break; 03622 case CardUtil::QPSK: 03623 cardtype->setValue("DVB-S"); 03624 cardname->setValue(frontend_name); 03625 signal_timeout->setValue(7000); 03626 channel_timeout->setValue(10000); 03627 break; 03628 case CardUtil::DVBS2: 03629 cardtype->setValue("DVB-S2"); 03630 cardname->setValue(frontend_name); 03631 signal_timeout->setValue(7000); 03632 channel_timeout->setValue(10000); 03633 break; 03634 case CardUtil::QAM: 03635 cardtype->setValue("DVB-C"); 03636 cardname->setValue(frontend_name); 03637 signal_timeout->setValue(1000); 03638 channel_timeout->setValue(3000); 03639 break; 03640 case CardUtil::OFDM: 03641 { 03642 cardtype->setValue("DVB-T"); 03643 cardname->setValue(frontend_name); 03644 signal_timeout->setValue(1000); 03645 channel_timeout->setValue(3000); 03646 if (frontend_name.toLower().indexOf("usb") >= 0) 03647 { 03648 signal_timeout->setValue(40000); 03649 channel_timeout->setValue(42500); 03650 } 03651 03652 // slow down tuning for buggy drivers 03653 if ((frontend_name == "DiBcom 3000P/M-C DVB-T") || 03654 (frontend_name == 03655 "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver")) 03656 { 03657 tuning_delay->setValue(200); 03658 } 03659 03660 #if 0 // frontends on hybrid DVB-T/Analog cards 03661 QString short_name = remove_chaff(frontend_name); 03662 buttonAnalog->setVisible( 03663 short_name.left(15).toLower() == "zarlink zl10353" || 03664 short_name.toLower() == "wintv hvr 900 m/r: 65008/a1c0" || 03665 short_name.left(17).toLower() == "philips tda10046h"); 03666 #endif 03667 } 03668 break; 03669 case CardUtil::ATSC: 03670 { 03671 QString short_name = remove_chaff(frontend_name); 03672 cardtype->setValue("ATSC"); 03673 cardname->setValue(short_name); 03674 signal_timeout->setValue(500); 03675 channel_timeout->setValue(3000); 03676 03677 // According to #1779 and #1935 the AverMedia 180 needs 03678 // a 3000 ms signal timeout, at least for QAM tuning. 03679 if (frontend_name == "Nextwave NXT200X VSB/QAM frontend") 03680 { 03681 signal_timeout->setValue(3000); 03682 channel_timeout->setValue(5500); 03683 } 03684 03685 #if 0 // frontends on hybrid DVB-T/Analog cards 03686 if (frontend_name.toLower().indexOf("usb") < 0) 03687 { 03688 buttonAnalog->setVisible( 03689 short_name.left(6).toLower() == "pchdtv" || 03690 short_name.left(5).toLower() == "dvico" || 03691 short_name.left(8).toLower() == "nextwave"); 03692 } 03693 #endif 03694 } 03695 break; 03696 default: 03697 break; 03698 } 03699 #else 03700 cardtype->setValue(QString("Recompile with DVB-Support!")); 03701 #endif 03702 } 03703 03704 TunerCardAudioInput::TunerCardAudioInput(const CaptureCard &parent, 03705 QString dev, QString type) : 03706 ComboBoxSetting(this), CaptureCardDBStorage(this, parent, "audiodevice"), 03707 last_device(dev), last_cardtype(type) 03708 { 03709 setLabel(QObject::tr("Audio input")); 03710 int cardid = parent.getCardID(); 03711 if (cardid <= 0) 03712 return; 03713 03714 last_cardtype = CardUtil::GetRawCardType(cardid); 03715 last_device = CardUtil::GetAudioDevice(cardid); 03716 } 03717 03718 void TunerCardAudioInput::fillSelections(const QString &device) 03719 { 03720 clearSelections(); 03721 03722 if (device.isEmpty()) 03723 return; 03724 03725 last_device = device; 03726 QStringList inputs = 03727 CardUtil::ProbeAudioInputs(device, last_cardtype); 03728 03729 for (uint i = 0; i < (uint)inputs.size(); i++) 03730 { 03731 addSelection(inputs[i], QString::number(i), 03732 last_device == QString::number(i)); 03733 } 03734 } 03735 03736 class DVBExtra : public ConfigurationWizard 03737 { 03738 public: 03739 DVBExtra(DVBConfigurationGroup &parent); 03740 uint GetInstanceCount(void) const 03741 { 03742 return (uint) count->intValue(); 03743 } 03744 03745 private: 03746 InstanceCount *count; 03747 }; 03748 03749 DVBExtra::DVBExtra(DVBConfigurationGroup &parent) 03750 : count(new InstanceCount(parent.parent)) 03751 { 03752 VerticalConfigurationGroup* rec = new VerticalConfigurationGroup(false); 03753 rec->setLabel(QObject::tr("Recorder Options")); 03754 rec->setUseLabel(false); 03755 03756 rec->addChild(count); 03757 rec->addChild(new DVBNoSeqStart(parent.parent)); 03758 rec->addChild(new DVBOnDemand(parent.parent)); 03759 rec->addChild(new DVBEITScan(parent.parent)); 03760 rec->addChild(new DVBTuningDelay(parent.parent)); 03761 03762 addChild(rec); 03763 } 03764 03765 DVBConfigurationGroup::DVBConfigurationGroup(CaptureCard& a_parent) : 03766 VerticalConfigurationGroup(false, true, false, false), 03767 parent(a_parent), 03768 diseqc_tree(new DiSEqCDevTree()) 03769 { 03770 cardnum = new DVBCardNum(parent); 03771 cardname = new DVBCardName(); 03772 cardtype = new DVBCardType(); 03773 03774 signal_timeout = new SignalTimeout(parent, 500, 250); 03775 channel_timeout = new ChannelTimeout(parent, 3000, 1750); 03776 03777 addChild(cardnum); 03778 03779 HorizontalConfigurationGroup *hg0 = 03780 new HorizontalConfigurationGroup(false, false, true, true); 03781 hg0->addChild(cardname); 03782 hg0->addChild(cardtype); 03783 addChild(hg0); 03784 03785 addChild(signal_timeout); 03786 addChild(channel_timeout); 03787 03788 addChild(new EmptyAudioDevice(parent)); 03789 addChild(new EmptyVBIDevice(parent)); 03790 03791 TransButtonSetting *buttonRecOpt = new TransButtonSetting(); 03792 buttonRecOpt->setLabel(tr("Recording Options")); 03793 03794 HorizontalConfigurationGroup *advcfg = 03795 new HorizontalConfigurationGroup(false, false, true, true); 03796 advcfg->addChild(buttonRecOpt); 03797 addChild(advcfg); 03798 03799 diseqc_btn = new TransButtonSetting(); 03800 diseqc_btn->setLabel(tr("DiSEqC (Switch, LNB, and Rotor Configuration)")); 03801 diseqc_btn->setHelpText(tr("Input and satellite settings.")); 03802 03803 HorizontalConfigurationGroup *diseqc_cfg = 03804 new HorizontalConfigurationGroup(false, false, true, true); 03805 diseqc_cfg->addChild(diseqc_btn); 03806 diseqc_btn->setVisible(false); 03807 addChild(diseqc_cfg); 03808 03809 tuning_delay = new DVBTuningDelay(parent); 03810 addChild(tuning_delay); 03811 tuning_delay->setVisible(false); 03812 03813 connect(cardnum, SIGNAL(valueChanged(const QString&)), 03814 this, SLOT( probeCard (const QString&))); 03815 connect(diseqc_btn, SIGNAL(pressed()), 03816 this, SLOT( DiSEqCPanel())); 03817 connect(buttonRecOpt, SIGNAL(pressed()), 03818 this, SLOT( DVBExtraPanel())); 03819 } 03820 03821 DVBConfigurationGroup::~DVBConfigurationGroup() 03822 { 03823 if (diseqc_tree) 03824 { 03825 delete diseqc_tree; 03826 diseqc_tree = NULL; 03827 } 03828 } 03829 03830 void DVBConfigurationGroup::DiSEqCPanel() 03831 { 03832 parent.reload(); // ensure card id is valid 03833 03834 DTVDeviceTreeWizard diseqcWiz(*diseqc_tree); 03835 diseqcWiz.exec(); 03836 } 03837 03838 void DVBConfigurationGroup::Load(void) 03839 { 03840 VerticalConfigurationGroup::Load(); 03841 diseqc_tree->Load(parent.getCardID()); 03842 if (cardtype->getValue() == "DVB-S" || 03843 cardtype->getValue() == "DVB-S2" || 03844 DiSEqCDevTree::Exists(parent.getCardID())) 03845 { 03846 diseqc_btn->setVisible(true); 03847 } 03848 } 03849 03850 void DVBConfigurationGroup::Save(void) 03851 { 03852 VerticalConfigurationGroup::Save(); 03853 diseqc_tree->Store(parent.getCardID()); 03854 DiSEqCDev trees; 03855 trees.InvalidateTrees(); 03856 } 03857 03858 void DVBConfigurationGroup::DVBExtraPanel(void) 03859 { 03860 parent.reload(); // ensure card id is valid 03861 03862 DVBExtra acw(*this); 03863 acw.exec(); 03864 parent.SetInstanceCount(acw.GetInstanceCount()); 03865 }
1.7.6.1