00001
00002
00003
00004 #include <unistd.h>
00005 #include <fcntl.h>
00006 #include <sys/types.h>
00007 #include <sys/stat.h>
00008
00009
00010 #include <algorithm>
00011 using namespace std;
00012
00013
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
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"
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
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
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
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
00723 QDir dev("/dev/v4l", "video*", QDir::Name, QDir::System);
00724 fillSelectionsFromDir(dev, minor_min, minor_max,
00725 card, driver, false);
00726
00727
00728 dev.setPath("/dev");
00729 fillSelectionsFromDir(dev, minor_min, minor_max,
00730 card, driver, false);
00731
00732
00733 dev.setPath("/dev/dtv");
00734 fillSelectionsFromDir(dev, minor_min, minor_max,
00735 card, driver, false);
00736
00737
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
00774 if (!S_ISCHR(st.st_mode))
00775 continue;
00776
00777
00778 uint minor_num = minor(st.st_rdev);
00779 if (minor_min > minor_num || minor_max < minor_num)
00780 continue;
00781
00782
00783 if (!allow_duplicates && minor_list[minor_num])
00784 continue;
00785
00786
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
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
00998 QStringList sdevs = CardUtil::ProbeVideoDevices("DVB");
00999
01000
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
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
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
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
01583 QStringList sdevs = CardUtil::ProbeVideoDevices("ASI");
01584
01585
01586 if (!current.isEmpty() &&
01587 (find(sdevs.begin(), sdevs.end(), current) == sdevs.end()))
01588 {
01589 stable_sort(sdevs.begin(), sdevs.end());
01590 }
01591
01592
01593 QStringList db = CardUtil::GetVideoDevices("ASI");
01594
01595
01596
01597
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
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
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
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
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
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
01820
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
01847
01848
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
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();
01939
01940 HDHomeRunExtra acw(*this);
01941 acw.exec();
01942 parent.SetInstanceCount(acw.GetInstanceCount());
01943 }
01944
01945
01946
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
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
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
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
02425 if (cloneCount && !CardUtil::CreateInputGroupIfNeeded(cardid))
02426 return;
02427
02428
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
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
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
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
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
02780
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();
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();
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 {
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
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
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
03216 CardUtil::DeleteOrphanInputs();
03217
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
03497
03498
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
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
03562
03563
03564
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
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
03678
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();
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();
03861
03862 DVBExtra acw(*this);
03863 acw.exec();
03864 parent.SetInstanceCount(acw.GetInstanceCount());
03865 }