MythTV master
videosource.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
2
3#include <QtGlobal>
4#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
5#include <QtSystemDetection>
6#endif
7
8// Standard UNIX C headers
9#include <unistd.h>
10#include <fcntl.h>
11#if defined(Q_OS_BSD4) || defined(Q_OS_WINDOWS)
12#include <sys/types.h>
13#else
14#include <sys/sysmacros.h>
15#endif
16#include <sys/stat.h>
17
18// C++ headers
19#include <algorithm>
20
21// Qt headers
22#include <QCoreApplication>
23#include <QCursor>
24#include <QDateTime>
25#include <QDir>
26#include <QFile>
27#include <QLayout>
28#include <QMap>
29#include <QStringList>
30#include <QTextStream>
31#include <utility>
32
33// MythTV headers
34#include "libmythbase/compat.h"
36#include "libmythbase/mythconfig.h"
38#include "libmythbase/mythdb.h"
44#include "libmythupnp/httprequest.h" // for TestMimeType()
45
46#include "cardutil.h"
47#include "channelinfo.h"
48#include "channelutil.h"
49#include "diseqcsettings.h"
50#include "frequencies.h"
52#include "scanwizard.h"
53#include "sourceutil.h"
54#include "v4l2util.h"
55#include "videosource.h"
56
57#if CONFIG_DVB
58#include "recorders/dvbtypes.h"
59#endif
60
61#if CONFIG_VBOX
62#include "recorders/vboxutils.h"
63#endif
64
65#if CONFIG_HDHOMERUN
66#include HDHOMERUN_HEADERFILE
67#endif
68
70 QString _card_types,
71 bool _must_have_mplexid) :
72 m_initialSourceId(_initial_sourceid),
73 m_cardTypes(std::move(_card_types)),
74 m_mustHaveMplexId(_must_have_mplexid)
75{
76 setLabel(tr("Video Source"));
78 QObject::tr(
79 "Select a video source that is connected to one "
80 "or more capture cards. Default is the video source "
81 "selected in the Channel Editor page."
82 ));
83}
84
86{
88
89 QString querystr =
90 "SELECT DISTINCT videosource.name, videosource.sourceid "
91 "FROM capturecard, videosource";
92
93 querystr += (m_mustHaveMplexId) ? ", channel " : " ";
94
95 querystr +=
96 "WHERE capturecard.sourceid = videosource.sourceid AND "
97 " capturecard.hostname = :HOSTNAME ";
98
99 if (!m_cardTypes.isEmpty())
100 {
101 querystr += QString(" AND capturecard.cardtype in %1 ")
102 .arg(m_cardTypes);
103 }
104
106 {
107 querystr +=
108 " AND channel.sourceid = videosource.sourceid "
109 " AND channel.mplexid != 32767 "
110 " AND channel.mplexid != 0 ";
111 }
112
113 query.prepare(querystr);
114 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
115
116 if (!query.exec() || !query.isActive() || query.size() <= 0)
117 return;
118
119 uint sel = 0;
120 uint cnt = 0;
121 for (; query.next(); cnt++)
122 {
123 addSelection(query.value(0).toString(),
124 query.value(1).toString());
125
126 sel = (query.value(1).toUInt() == m_initialSourceId) ? cnt : sel;
127 }
128
130 {
131 if (cnt)
132 setValue(sel);
133 }
134
136}
137
139 m_initialSourceId(_initial_sourceid)
140{
141 setLabel(tr("Video Source"));
143 QObject::tr(
144 "The video source that is "
145 "selected in the Channel Editor page."
146 ));
147}
148
150{
152
153 QString querystr =
154 "SELECT DISTINCT videosource.name, videosource.sourceid "
155 "FROM capturecard, videosource "
156 "WHERE capturecard.sourceid = videosource.sourceid AND "
157 " capturecard.hostname = :HOSTNAME AND "
158 " videosource.sourceid = :SOURCEID ";
159
160 query.prepare(querystr);
161 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
162 query.bindValue(":SOURCEID", m_initialSourceId);
163
164 if (!query.exec() || !query.isActive())
165 {
166 MythDB::DBError("VideoSourceShow::Load", query);
167 return;
168 }
169
170 if (query.next())
171 {
172 setValue(query.value(0).toString());
173 }
174}
175
177{
178 public:
179 explicit InstanceCount(const CardInput &parent) :
180 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "reclimit"),
181 1, 10, 1)
182 {
183 setLabel(QObject::tr("Max recordings"));
184 setValue(1);
186 QObject::tr(
187 "Maximum number of simultaneous recordings MythTV will "
188 "attempt using this device. If set to a value other than "
189 "1, MythTV can sometimes record multiple programs on "
190 "the same multiplex or overlapping copies of the same "
191 "program on a single channel."
192 ));
193 };
194
195 ~InstanceCount() override
196 {
197 delete GetStorage();
198 }
199};
200
202{
203 public:
204 explicit SchedGroup(const CardInput &parent) :
205 MythUICheckBoxSetting(new CardInputDBStorage(this, parent, "schedgroup"))
206 {
207 setLabel(QObject::tr("Schedule as group"));
208 setValue(true);
210 QObject::tr(
211 "Schedule all virtual inputs on this device as a group. "
212 "This is more efficient than scheduling each input "
213 "individually. Additional, virtual inputs will be "
214 "automatically added as needed to fulfill the recording "
215 "load."
216 ));
217 };
218
219 ~SchedGroup() override
220 {
221 delete GetStorage();
222 }
223};
224
226{
227 QString sourceidTag(":WHERESOURCEID");
228
229 QString query("sourceid = " + sourceidTag);
230
231 bindings.insert(sourceidTag, m_parent.getSourceID());
232
233 return query;
234}
235
237{
238 QString sourceidTag(":SETSOURCEID");
239 QString colTag(":SET" + GetColumnName().toUpper());
240
241 QString query("sourceid = " + sourceidTag + ", " +
242 GetColumnName() + " = " + colTag);
243
244 bindings.insert(sourceidTag, m_parent.getSourceID());
245 bindings.insert(colTag, m_user->GetDBValue());
246
247 return query;
248}
249
251{
252 QString cardidTag(":WHERECARDID");
253
254 QString query("cardid = " + cardidTag);
255
256 bindings.insert(cardidTag, m_parent.getCardID());
257
258 return query;
259}
260
262{
263 QString cardidTag(":SETCARDID");
264 QString colTag(":SET" + GetColumnName().toUpper());
265
266 QString query("cardid = " + cardidTag + ", " +
267 GetColumnName() + " = " + colTag);
268
269 bindings.insert(cardidTag, m_parent.getCardID());
270 bindings.insert(colTag, m_user->GetDBValue());
271
272 return query;
273}
274
276{
277 public:
278 explicit XMLTVGrabber(const VideoSource &parent) :
280 "xmltvgrabber")),
281 m_parent(parent)
282 {
283 setLabel(QObject::tr("Listings grabber"));
284 };
285
286 ~XMLTVGrabber() override
287 {
288 delete GetStorage();
289 }
290
291 void Load(void) override // StandardSetting
292 {
293 addTargetedChild("eitonly", new EITOnly_config(m_parent, this));
294 addTargetedChild("/bin/true", new NoGrabber_config(m_parent));
295
297 QObject::tr("Transmitted guide only (EIT)"), "eitonly");
298
299 addSelection(QObject::tr("No grabber"), "/bin/true");
300
301 QString gname;
302 QString d1;
303 QString d2;
304 QString d3;
306
307 QString loc = QString("XMLTVGrabber::Load(%1): ").arg(m_parent.getSourceName());
308
309 QMutexLocker lock(&m_lock);
310 if (m_nameList.isEmpty())
311 {
312 QStringList args;
313 args += "baseline";
314
315 MythSystemLegacy find_grabber_proc("tv_find_grabbers", args,
317 find_grabber_proc.Run(25s);
318 LOG(VB_GENERAL, LOG_INFO,
319 loc + "Running 'tv_find_grabbers " + args.join(" ") + "'.");
320 uint status = find_grabber_proc.Wait();
321
322 if (status == GENERIC_EXIT_OK)
323 {
324 QTextStream ostream(find_grabber_proc.ReadAll());
325 while (!ostream.atEnd())
326 {
327 QString grabber_list(ostream.readLine());
328 QStringList grabber_split =
329 grabber_list.split("|", Qt::SkipEmptyParts);
330 QString grabber_name = grabber_split[1] + " (xmltv)";
331 QFileInfo grabber_file(grabber_split[0]);
332
333 m_nameList.push_back(grabber_name);
334 m_progList.push_back(grabber_file.fileName());
335 LOG(VB_GENERAL, LOG_DEBUG, "Found " + grabber_split[0]);
336 }
337 LOG(VB_GENERAL, LOG_INFO, loc + "Finished running tv_find_grabbers");
338 }
339 else
340 {
341 LOG(VB_GENERAL, LOG_ERR, loc + "Failed to run tv_find_grabbers");
342 }
343 }
344 else
345 {
346 LOG(VB_GENERAL, LOG_INFO, loc + "Loading results of tv_find_grabbers");
347 }
348
351 }
352
353 void Save(void) override // StandardSetting
354 {
356
358 query.prepare(
359 "UPDATE videosource "
360 "SET userid=NULL, password=NULL "
361 "WHERE xmltvgrabber NOT IN ( 'technovera' )");
362 if (!query.exec())
363 MythDB::DBError("XMLTVGrabber::Save", query);
364 }
365
366 void LoadXMLTVGrabbers(QStringList name_list, QStringList prog_list)
367 {
368 if (name_list.size() != prog_list.size())
369 return;
370
371 QString selValue = getValue();
372 int selIndex = getValueIndex(selValue);
373 setValue(0);
374
375 for (uint i = 0; i < (uint) name_list.size(); i++)
376 {
377 addTargetedChild(prog_list[i],
378 new XMLTV_generic_config(m_parent, prog_list[i],
379 this));
380 addSelection(name_list[i], prog_list[i]);
381 }
382
383 if (!selValue.isEmpty())
384 selIndex = getValueIndex(selValue);
385 if (selIndex >= 0)
386 setValue(selIndex);
387 }
388private:
390
391private:
392 static QMutex m_lock;
393 static QStringList m_nameList;
394 static QStringList m_progList;
395};
396
397// Results of search for XMLTV grabbers
399QStringList XMLTVGrabber::m_nameList;
400QStringList XMLTVGrabber::m_progList;
401
403{
404 public:
406 std::chrono::milliseconds min_val,
407 std::chrono::milliseconds max_val,
408 std::chrono::milliseconds step,
409 const QString &setting) :
410 MythUISpinBoxSetting(new CaptureCardDBStorage(this, parent, setting),
411 min_val.count(), max_val.count(), step.count())
412 {
413 }
414
416 {
417 delete GetStorage();
418 }
419 // Handles integer milliseconds (compiler converts seconds to milliseconds)
420 void setValueMs (std::chrono::milliseconds newValue)
421 { setValue(newValue.count()); }
422 // Handle non-integer seconds
423 template<typename T, typename = std::enable_if_t<!std::is_integral<T>()>>
424 void setValueMs (std::chrono::duration<T> newSecs)
425 { setValueMs(duration_cast<std::chrono::milliseconds>(newSecs)); }
426};
427
429{
430 public:
432 const QString &setting) :
433 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, setting))
434 {
435 }
436
438 {
439 delete GetStorage();
440 }
441};
442
444{
445 public:
446 explicit ScanFrequencyStart(const VideoSource &parent) :
447 MythUITextEditSetting(new VideoSourceDBStorage(this, parent, "scanfrequency"))
448 {
449 setLabel(QObject::tr("Scan Frequency"));
450 setHelpText(QObject::tr("The frequency to start scanning this video source. "
451 "This is then default for 'Full Scan (Tuned)' channel scanning. "
452 "Frequency value in Hz for DVB-T/T2/C, in kHz for DVB-S/S2. "
453 "Leave at 0 if not known. "));
454 };
455
457 {
458 delete GetStorage();
459 }
460};
461
463{
464 public:
465 DVBNetID(const VideoSource &parent, signed int value, signed int min_val) :
466 MythUISpinBoxSetting(new VideoSourceDBStorage(this, parent, "dvb_nit_id"),
467 min_val, 0xffff, 1)
468 {
469 setLabel(QObject::tr("Network ID"));
470 //: Network_ID is the name of an identifier in the DVB's Service
471 //: Information standard specification.
472 setHelpText(QObject::tr("If your provider has asked you to configure a "
473 "specific network identifier (Network_ID), "
474 "enter it here. Leave it at -1 otherwise."));
475 setValue(value);
476 };
477
478 ~DVBNetID() override
479 {
480 delete GetStorage();
481 }
482};
483
485{
486 public:
487 BouquetID(const VideoSource &parent, signed int value, signed int min_val) :
488 MythUISpinBoxSetting(new VideoSourceDBStorage(this, parent, "bouquet_id"),
489 min_val, 0xffff, 1)
490 {
491 setLabel(QObject::tr("Bouquet ID"));
492 setHelpText(QObject::tr("Bouquet ID for Freesat or Sky on satellite Astra-2 28.2E. "
493 "Leave this at 0 if you do not receive this satellite. "
494 "This is needed to get the Freesat and Sky channel numbers. "
495 "Value 272 selects Freesat bouquet 'England HD'. "
496 "See the MythTV Wiki https://www.mythtv.org/wiki/DVB_UK."));
497 setValue(value);
498 };
499
500 ~BouquetID() override
501 {
502 delete GetStorage();
503 }
504};
505
507{
508 public:
509 RegionID(const VideoSource &parent, signed int value, signed int min_val) :
510 MythUISpinBoxSetting(new VideoSourceDBStorage(this, parent, "region_id"),
511 min_val, 100, 1)
512 {
513 setLabel(QObject::tr("Region ID"));
514 setHelpText(QObject::tr("Region ID for Freesat or Sky on satellite Astra-2 28.2E. "
515 "Leave this at 0 you do not receive this satellite. "
516 "This is needed to get the Freesat and Sky channel numbers. "
517 "Value 1 selects region London. "
518 "See the MythTV Wiki https://www.mythtv.org/wiki/DVB_UK."));
519 setValue(value);
520 };
521
522 ~RegionID() override
523 {
524 delete GetStorage();
525 }
526};
527
529{
530 public:
531 LCNOffset(const VideoSource &parent, signed int value, signed int min_val) :
532 MythUISpinBoxSetting(new VideoSourceDBStorage(this, parent, "lcnoffset"),
533 min_val, 20000, 100)
534 {
535 setLabel(QObject::tr("Logical Channel Number Offset"));
536 setHelpText(QObject::tr("The offset is added to each logical channel number found "
537 "during a scan of a DVB video source. This makes it possible "
538 "to give different video sources a non-overlapping range "
539 "of channel numbers. Leave at 0 if you have only one video source "
540 "or if the video sources do not have DVB logical channel numbers."));
541 setValue(value);
542 };
543
544 ~LCNOffset() override
545 {
546 delete GetStorage();
547 }
548};
549
551 MythUIComboBoxSetting(new VideoSourceDBStorage(this, parent, "freqtable"))
552{
553 setLabel(QObject::tr("Channel frequency table"));
554 addSelection("default");
555
556 for (const auto & chanlist : gChanLists)
557 addSelection(chanlist.name);
558
559 setHelpText(QObject::tr("Use default unless this source uses a "
560 "different frequency table than the system wide table "
561 "defined in the General settings."));
562}
563
565{
566 delete GetStorage();
567}
568
570 m_sourceId(_sourceid)
571{
572 setLabel(QObject::tr("Channel frequency table"));
573
574 for (const auto & chanlist : gChanLists)
575 addSelection(chanlist.name);
576}
577
579{
580 int idx1 = getValueIndex(gCoreContext->GetSetting("FreqTable"));
581 if (idx1 >= 0)
582 setValue(idx1);
583
584 if (!m_sourceId)
585 return;
586
588 query.prepare(
589 "SELECT freqtable "
590 "FROM videosource "
591 "WHERE sourceid = :SOURCEID");
592 query.bindValue(":SOURCEID", m_sourceId);
593
594 if (!query.exec() || !query.isActive())
595 {
596 MythDB::DBError("TransFreqTableSelector::load", query);
597 return;
598 }
599
600 m_loadedFreqTable.clear();
601
602 if (query.next())
603 {
604 m_loadedFreqTable = query.value(0).toString();
605 if (!m_loadedFreqTable.isEmpty() &&
606 (m_loadedFreqTable.toLower() != "default"))
607 {
609 if (idx2 >= 0)
610 setValue(idx2);
611 }
612 }
613}
614
616{
617 LOG(VB_GENERAL, LOG_INFO, "TransFreqTableSelector::Save(void)");
618
619 if ((m_loadedFreqTable == getValue()) ||
620 ((m_loadedFreqTable.toLower() == "default") &&
621 (getValue() == gCoreContext->GetSetting("FreqTable"))))
622 {
623 return;
624 }
625
627 query.prepare(
628 "UPDATE videosource "
629 "SET freqtable = :FREQTABLE "
630 "WHERE sourceid = :SOURCEID");
631
632 query.bindValue(":FREQTABLE", getValue());
633 query.bindValue(":SOURCEID", m_sourceId);
634
635 if (!query.exec() || !query.isActive())
636 {
637 MythDB::DBError("TransFreqTableSelector::load", query);
638 return;
639 }
640}
641
643{
644 m_sourceId = sourceid;
645 Load();
646}
647
649{
650 public:
651 explicit UseEIT(const VideoSource &parent) :
652 MythUICheckBoxSetting(new VideoSourceDBStorage(this, parent, "useeit"))
653 {
654 setLabel(QObject::tr("Perform EIT scan"));
655 setHelpText(QObject::tr(
656 "If enabled, program guide data for channels on this "
657 "source will be updated with data provided by the "
658 "channels themselves 'Over-the-Air'."));
659 }
660
661 ~UseEIT() override
662 {
663 delete GetStorage();
664 }
665};
666
668 const QString& _grabber,
669 StandardSetting *_setting) :
670 m_parent(_parent), m_grabber(_grabber)
671{
672 setVisible(false);
673
674 QString filename = QString("%1/%2.xmltv")
676
677 m_grabberArgs.push_back("--config-file");
678 m_grabberArgs.push_back(filename);
679 m_grabberArgs.push_back("--configure");
680
681 _setting->addTargetedChild(_grabber, new UseEIT(m_parent));
682
683 auto *config = new ButtonStandardSetting(tr("Configure"));
684 config->setHelpText(tr("Run XMLTV configure command."));
685
686 _setting->addTargetedChild(_grabber, config);
687
689}
690
692{
694#if 0
695 QString err_msg = QObject::tr(
696 "You MUST run 'mythfilldatabase --manual' the first time,\n"
697 "instead of just 'mythfilldatabase'.\nYour grabber does not provide "
698 "channel numbers, so you have to set them manually.");
699
701 {
702 LOG(VB_GENERAL, LOG_ERR, err_msg);
703 ShowOkPopup(err_msg);
704 }
705#endif
706}
707
709{
711 MythScreenType *ssd =
712 new MythTerminal(mainStack, m_grabber, m_grabberArgs);
713
714 if (ssd->Create())
715 mainStack->AddScreen(ssd);
716 else
717 delete ssd;
718}
719
721 : m_useEit(new UseEIT(_parent))
722{
723 setVisible(false);
724
725 m_useEit->setValue(true);
726 m_useEit->setVisible(false);
728
729 auto *label=new TransTextEditSetting();
730 label->setValue(QObject::tr("Use only the transmitted guide data."));
731 label->setHelpText(
732 QObject::tr("This will usually only work with ATSC or DVB channels, "
733 "and generally provides data only for the next few days."));
734 _setting->addTargetedChild("eitonly", label);
735}
736
738{
739 // Force this value on
740 m_useEit->setValue(true);
741 m_useEit->Save();
742}
743
745 : m_useEit(new UseEIT(_parent))
746{
747 m_useEit->setValue(false);
748 m_useEit->setVisible(false);
750
751 auto *label = new TransTextEditSetting();
752 label->setValue(QObject::tr("Do not configure a grabber"));
753 addTargetedChild("/bin/true", label);
754}
755
757{
758 m_useEit->setValue(false);
759 m_useEit->Save();
760}
761
763 // must be first
764 : m_id(new ID())
765{
766 addChild(m_id = new ID());
767
768 setLabel(QObject::tr("Video Source Setup"));
769 addChild(m_name = new Name(*this));
770 addChild(new XMLTVGrabber(*this));
771 addChild(new FreqTableSelector(*this));
772 addChild(new ScanFrequencyStart(*this));
773 addChild(new DVBNetID(*this, -1, -1));
774 addChild(new BouquetID(*this, 0, 0));
775 addChild(new RegionID(*this, 0, 0));
776 addChild(new LCNOffset(*this, 0, 0));
777}
778
780{
781 return true;
782}
783
785{
787}
788
790 const QString &thecardtype)
791{
793 query.prepare("SELECT count(cardtype)"
794 " FROM capturecard "
795 " WHERE capturecard.sourceid = :SOURCEID "
796 " AND capturecard.cardtype = :CARDTYPE ;");
797 query.bindValue(":SOURCEID", sourceID);
798 query.bindValue(":CARDTYPE", thecardtype);
799
800 if (query.exec() && query.next())
801 {
802 int count = query.value(0).toInt();
803
804 if (count > 0)
805 return true;
806 }
807
808 return false;
809}
810
812{
814 result.prepare("SELECT name, sourceid FROM videosource;");
815
816 if (result.exec() && result.isActive() && result.size() > 0)
817 {
818 while (result.next())
819 {
820 auto* source = new VideoSource();
821 source->setLabel(result.value(0).toString());
822 source->loadByID(result.value(1).toInt());
823 setting->addChild(source);
824 }
825 }
826}
827
829{
831 result.prepare("SELECT name, sourceid FROM videosource;");
832
833 if (result.exec() && result.isActive() && result.size() > 0)
834 {
835 while (result.next())
836 {
837 setting->addSelection(result.value(0).toString(),
838 result.value(1).toString());
839 }
840 }
841}
842
843void VideoSource::loadByID(int sourceid)
844{
845 m_id->setValue(sourceid);
846}
847
849{
850 public:
851 explicit VideoDevice(const CaptureCard &parent,
852 uint minor_min = 0,
853 uint minor_max = UINT_MAX,
854 const QString& card = QString(),
855 const QRegularExpression& driver = QRegularExpression()) :
856 CaptureCardComboBoxSetting(parent, true, "videodevice")
857 {
858 setLabel(QObject::tr("Video device"));
859
860 // /dev/v4l/video*
861 QDir dev("/dev/v4l", "video*", QDir::Name, QDir::System);
862 fillSelectionsFromDir(dev, minor_min, minor_max,
863 card, driver, false);
864
865 // /dev/video*
866 dev.setPath("/dev");
867 fillSelectionsFromDir(dev, minor_min, minor_max,
868 card, driver, false);
869
870 // /dev/dtv/video*
871 dev.setPath("/dev/dtv");
872 fillSelectionsFromDir(dev, minor_min, minor_max,
873 card, driver, false);
874
875 // /dev/dtv*
876 dev.setPath("/dev");
877 dev.setNameFilters(QStringList("dtv*"));
878 fillSelectionsFromDir(dev, minor_min, minor_max,
879 card, driver, false);
880 };
881
886 void fillSelectionsFromDir(const QDir &dir,
887 [[maybe_unused]] bool absPath = true)
888 {
889 fillSelectionsFromDir(dir, 0, 255, QString(), QRegularExpression(), false);
890 }
891
893 uint minor_min, uint minor_max,
894 const QString& card, const QRegularExpression& driver,
895 bool allow_duplicates)
896 {
897 uint cnt = 0;
898 QFileInfoList entries = dir.entryInfoList();
899 for (const auto & fi : std::as_const(entries))
900 {
901 struct stat st {};
902 QString filepath = fi.absoluteFilePath();
903 int err = lstat(filepath.toLocal8Bit().constData(), &st);
904
905 if (err)
906 {
907 LOG(VB_GENERAL, LOG_ERR,
908 QString("Could not stat file: %1").arg(filepath));
909 continue;
910 }
911
912 // is this is a character device?
913 if (!S_ISCHR(st.st_mode))
914 continue;
915
916 // is this device is in our minor range?
917 uint minor_num = minor(st.st_rdev);
918 if (minor_min > minor_num || minor_max < minor_num)
919 continue;
920
921 // ignore duplicates if allow_duplicates not set
922 if (!allow_duplicates && m_minorList[minor_num])
923 continue;
924
925 // if the driver returns any info add this device to our list
926 QByteArray tmp = filepath.toLatin1();
927 int videofd = open(tmp.constData(), O_RDWR);
928 if (videofd >= 0)
929 {
930 QString card_name;
931 QString driver_name;
932 if (CardUtil::GetV4LInfo(videofd, card_name, driver_name))
933 {
934 auto match = driver.match(driver_name);
935 if ((!driver.pattern().isEmpty() || match.hasMatch()) &&
936 (card.isEmpty() || (card_name == card)))
937 {
938 addSelection(filepath);
939 cnt++;
940 }
941 }
942 close(videofd);
943 }
944
945 // add to list of minors discovered to avoid duplicates
946 m_minorList[minor_num] = 1;
947 }
948
949 return cnt;
950 }
951
952 QString Driver(void) const { return m_driverName; }
953 QString Card(void) const { return m_cardName; }
954
955 private:
956 QMap<uint, uint> m_minorList;
957 QString m_cardName;
959};
960
962{
963 public:
964 explicit VBIDevice(const CaptureCard &parent) :
965 CaptureCardComboBoxSetting(parent, true /*, mustexist true */,
966 "vbidevice")
967 {
968 setLabel(QObject::tr("VBI device"));
969 setFilter(QString(), QString());
970 setHelpText(QObject::tr("Device to read VBI (captions) from."));
971 };
972
973 uint setFilter(const QString &card, const QString &driver)
974 {
976 QDir dev("/dev/v4l", "vbi*", QDir::Name, QDir::System);
977 uint count = fillSelectionsFromDir(dev, card, driver);
978 if (count == 0)
979 {
980 dev.setPath("/dev");
981 count = fillSelectionsFromDir(dev, card, driver);
982 if ((count == 0U) && !getValue().isEmpty())
983 {
985 }
986 }
987
988 return count;
989 }
990
995 void fillSelectionsFromDir(const QDir &dir,
996 [[maybe_unused]] bool absPath = true)
997 {
998 fillSelectionsFromDir(dir, QString(), QString());
999 }
1000
1001 uint fillSelectionsFromDir(const QDir &dir, const QString &card,
1002 const QString &driver)
1003 {
1004 QStringList devices;
1005 QFileInfoList entries = dir.entryInfoList();
1006 for (const auto & fi : std::as_const(entries))
1007 {
1008 QString device = fi.absoluteFilePath();
1009 QByteArray adevice = device.toLatin1();
1010 int vbifd = open(adevice.constData(), O_RDWR);
1011 if (vbifd < 0)
1012 continue;
1013
1014 QString cn;
1015 QString dn;
1016 if (CardUtil::GetV4LInfo(vbifd, cn, dn) &&
1017 (driver.isEmpty() || (dn == driver)) &&
1018 (card.isEmpty() || (cn == card)))
1019 {
1020 devices.push_back(device);
1021 }
1022
1023 close(vbifd);
1024 }
1025
1026 QString sel = getValue();
1027 for (const QString& device : std::as_const(devices))
1028 addSelection(device, device, device == sel);
1029
1030 return (uint) devices.size();
1031 }
1032};
1033
1035{
1036 public:
1037 explicit CommandPath(const CaptureCard &parent) :
1039 "videodevice"))
1040 {
1041 setLabel(QObject::tr(""));
1042 setValue("");
1043 setHelpText(QObject::tr("Specify the command to run, with any "
1044 "needed arguments."));
1045 };
1046
1047 ~CommandPath() override
1048 {
1049 delete GetStorage();
1050 }
1051};
1052
1054{
1055 public:
1056 explicit FileDevice(const CaptureCard &parent) :
1058 new CaptureCardDBStorage(this, parent, "videodevice")
1059 /* mustexist, false */)
1060 {
1061 setLabel(QObject::tr("File path"));
1062 };
1063
1064 ~FileDevice() override
1065 {
1066 delete GetStorage();
1067 }
1068};
1069
1071{
1072 public:
1073 explicit AudioDevice(const CaptureCard &parent) :
1074 CaptureCardComboBoxSetting(parent, true /* mustexist false */,
1075 "audiodevice")
1076 {
1077 setLabel(QObject::tr("Audio device"));
1078#if CONFIG_AUDIO_OSS
1079 QDir dev("/dev", "dsp*", QDir::Name, QDir::System);
1081 dev.setPath("/dev/sound");
1083#endif
1084#if CONFIG_AUDIO_ALSA
1085 addSelection("ALSA:default", "ALSA:default");
1086#endif
1087 addSelection(QObject::tr("(None)"), "NULL");
1088 setHelpText(QObject::tr("Device to read audio from, "
1089 "if audio is separate from the video."));
1090 };
1091};
1092
1094{
1095 public:
1096 // Handles integer milliseconds (compiler converts seconds to milliseconds)
1097 SignalTimeout(const CaptureCard &parent, std::chrono::milliseconds value,
1098 std::chrono::milliseconds min_val) :
1099 CaptureCardSpinBoxSetting(parent, min_val, 60s, 250ms, "signal_timeout")
1100 {
1101 setLabel(QObject::tr("Signal timeout (ms)"));
1102 setValueMs(value);
1103 setHelpText(QObject::tr(
1104 "Maximum time (in milliseconds) MythTV waits for "
1105 "a signal when scanning for channels."));
1106 };
1107 // Handle non-integer seconds
1108 template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T>> >
1109 SignalTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::duration<T> min_secs) :
1110 SignalTimeout(parent, value, duration_cast<std::chrono::milliseconds>(min_secs)) {};
1111 template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T>> >
1112 SignalTimeout(const CaptureCard &parent, std::chrono::duration<T> value, std::chrono::duration<T> min_secs) :
1113 SignalTimeout(parent,
1114 duration_cast<std::chrono::milliseconds>(value),
1115 duration_cast<std::chrono::milliseconds>(min_secs)) {};
1116};
1117
1119{
1120 public:
1121 // Handles integer milliseconds (compiler converts seconds to milliseconds)
1122 ChannelTimeout(const CaptureCard &parent, std::chrono::milliseconds value,
1123 std::chrono::milliseconds min_val) :
1124 CaptureCardSpinBoxSetting(parent, min_val, 65s, 250ms, "channel_timeout")
1125 {
1126 setLabel(QObject::tr("Tuning timeout (ms)"));
1127 setValueMs(value);
1128 setHelpText(QObject::tr(
1129 "Maximum time (in milliseconds) MythTV waits for "
1130 "a channel lock. For recordings, if this time is "
1131 "exceeded, the recording will be marked as failed."));
1132 };
1133 // Handle non-integer seconds
1134 template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T>> >
1135 ChannelTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::duration<T> min_secs) :
1136 ChannelTimeout(parent, value, duration_cast<std::chrono::milliseconds>(min_secs)) {};
1137 template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T>> >
1138 ChannelTimeout(const CaptureCard &parent, std::chrono::duration<T> value, std::chrono::duration<T> min_secs) :
1139 ChannelTimeout(parent, value, duration_cast<std::chrono::milliseconds>(min_secs)) {};
1140};
1141
1143{
1144 public:
1145 explicit AudioRateLimit(const CaptureCard &parent) :
1146 CaptureCardComboBoxSetting(parent, false, "audioratelimit")
1147 {
1148 setLabel(QObject::tr("Force audio sampling rate"));
1150 QObject::tr("If non-zero, override the audio sampling "
1151 "rate in the recording profile when this card is "
1152 "used. Use this if your capture card does not "
1153 "support all of the standard rates."));
1154 addSelection(QObject::tr("(None)"), "0");
1155 addSelection("32000");
1156 addSelection("44100");
1157 addSelection("48000");
1158 };
1159};
1160
1162{
1163 public:
1164 explicit SkipBtAudio(const CaptureCard &parent) :
1166 "skipbtaudio"))
1167 {
1168 setLabel(QObject::tr("Do not adjust volume"));
1170 QObject::tr("Enable this option for budget BT878 based "
1171 "DVB-T cards such as the AverTV DVB-T which "
1172 "require the audio volume to be left alone."));
1173 };
1174
1175 ~SkipBtAudio() override
1176 {
1177 delete GetStorage();
1178 }
1179};
1180
1182{
1183 public:
1184 explicit DVBCardNum(const CaptureCard &parent) :
1185 CaptureCardComboBoxSetting(parent, true, "videodevice")
1186 {
1187 setLabel(QObject::tr("DVB device"));
1189 QObject::tr("When you change this setting, the text below "
1190 "should change to the name and type of your card. "
1191 "If the card cannot be opened, an error message "
1192 "will be displayed."));
1193 fillSelections(QString());
1194 };
1195
1199 void fillSelections(const QString &current)
1200 {
1202
1203 // Get devices from filesystem
1204 QStringList sdevs = CardUtil::ProbeVideoDevices("DVB");
1205
1206 // Add current if needed
1207 if (!current.isEmpty() &&
1208 (std::find(sdevs.begin(), sdevs.end(), current) == sdevs.end()))
1209 {
1210 std::stable_sort(sdevs.begin(), sdevs.end());
1211 }
1212
1213 QStringList db = CardUtil::GetVideoDevices("DVB");
1214
1215 QMap<QString,bool> in_use;
1216 QString sel = current;
1217 for (const QString& dev : std::as_const(sdevs))
1218 {
1219 in_use[dev] = std::find(db.begin(), db.end(), dev) != db.end();
1220 if (sel.isEmpty() && !in_use[dev])
1221 sel = dev;
1222 }
1223
1224 if (sel.isEmpty() && !sdevs.empty())
1225 sel = sdevs[0];
1226
1227 QString usestr = QString(" -- ");
1228 usestr += QObject::tr("Warning: already in use");
1229
1230 for (const QString& dev : std::as_const(sdevs))
1231 {
1232 QString desc = dev + (in_use[dev] ? usestr : "");
1233 desc = (current == dev) ? dev : desc;
1234 addSelection(desc, dev, dev == sel);
1235 }
1236 }
1237
1238 void Load(void) override // StandardSetting
1239 {
1241 addSelection(QString());
1242
1244
1246 fillSelections(dev);
1247 }
1248};
1249
1250// Use capturecard/inputname to store the delivery system selection of the card
1252{
1253 public:
1254 explicit DVBCardType(const CaptureCard &parent) :
1255 CaptureCardComboBoxSetting(parent, false, "inputname")
1256 {
1257 setLabel(QObject::tr("Delivery system"));
1259 QObject::tr("If your card supports more than one delivery system "
1260 "then you can select here the one that you want to use."));
1261 };
1262};
1263
1265{
1266 public:
1268 {
1269 setLabel(QObject::tr("Frontend ID"));
1271 QObject::tr("Identification string reported by the card. "
1272 "If the message \"Could not get card info...\" appears "
1273 "the card can be in use by another program."));
1274 };
1275};
1276
1278{
1279 public:
1280 explicit DVBNoSeqStart(const CaptureCard &parent) :
1282 new CaptureCardDBStorage(this, parent, "dvb_wait_for_seqstart"))
1283 {
1284 setLabel(QObject::tr("Wait for SEQ start header"));
1285 setValue(true);
1287 QObject::tr("If enabled, drop packets from the start of a DVB "
1288 "recording until a sequence start header is seen."));
1289 };
1290
1292 {
1293 delete GetStorage();
1294 }
1295};
1296
1298{
1299 public:
1300 explicit DVBOnDemand(const CaptureCard &parent) :
1302 new CaptureCardDBStorage(this, parent, "dvb_on_demand"))
1303 {
1304 setLabel(QObject::tr("Open DVB card on demand"));
1305 setValue(true);
1307 QObject::tr("If enabled, only open the DVB card when required, "
1308 "leaving it free for other programs at other times."));
1309 };
1310
1311 ~DVBOnDemand() override
1312 {
1313 delete GetStorage();
1314 }
1315};
1316
1318{
1319 public:
1320 explicit DVBEITScan(const CaptureCard &parent) :
1322 new CaptureCardDBStorage(this, parent, "dvb_eitscan"))
1323 {
1324 setLabel(QObject::tr("Use DVB card for active EIT scan"));
1325 setValue(true);
1327 QObject::tr("If enabled, activate active scanning for "
1328 "program data (EIT). When this option is enabled "
1329 "the DVB card is constantly in use."));
1330 };
1331
1332 ~DVBEITScan() override
1333 {
1334 delete GetStorage();
1335 }
1336};
1337
1339{
1340 public:
1341 explicit DVBTuningDelay(const CaptureCard &parent) :
1342 CaptureCardSpinBoxSetting(parent, 0ms, 2s, 25ms, "dvb_tuning_delay")
1343 {
1344 setLabel(QObject::tr("DVB tuning delay (ms)"));
1345 setValueMs(0ms);
1347 QObject::tr("Some Linux DVB drivers, in particular for the "
1348 "Hauppauge Nova-T, require that we slow down "
1349 "the tuning process by specifying a delay "
1350 "(in milliseconds)."));
1351 };
1352};
1353
1355{
1356 public:
1357 explicit FirewireGUID(const CaptureCard &parent) :
1358 CaptureCardComboBoxSetting(parent, false, "videodevice")
1359 {
1360 setLabel(QObject::tr("GUID"));
1361#if CONFIG_FIREWIRE
1362 std::vector<AVCInfo> list = FirewireDevice::GetSTBList();
1363 for (auto & i : list)
1364 {
1365 QString guid = i.GetGUIDString();
1366 m_guidToAvcInfo[guid] = i;
1367 addSelection(guid);
1368 }
1369#endif // CONFIG_FIREWIRE
1370 }
1371
1372 AVCInfo GetAVCInfo(const QString &guid) const
1373 { return m_guidToAvcInfo[guid]; }
1374
1375 private:
1376 QMap<QString,AVCInfo> m_guidToAvcInfo;
1377};
1378
1380 const FirewireGUID *_guid) :
1381 CaptureCardComboBoxSetting(parent, false, "firewire_model"),
1382 m_guid(_guid)
1383{
1384 setLabel(QObject::tr("Cable box model"));
1385 addSelection(QObject::tr("Motorola Generic"), "MOTO GENERIC");
1386 addSelection(QObject::tr("SA/Cisco Generic"), "SA GENERIC");
1387 addSelection("DCH-3200");
1388 addSelection("DCX-3200");
1389 addSelection("DCT-3412");
1390 addSelection("DCT-3416");
1391 addSelection("DCT-6200");
1392 addSelection("DCT-6212");
1393 addSelection("DCT-6216");
1394 addSelection("QIP-6200");
1395 addSelection("QIP-7100");
1396 addSelection("PACE-550");
1397 addSelection("PACE-779");
1398 addSelection("SA3250HD");
1399 addSelection("SA4200HD");
1400 addSelection("SA4250HDC");
1401 addSelection("SA8300HD");
1402 QString help = QObject::tr(
1403 "Choose the model that most closely resembles your set top box. "
1404 "Depending on firmware revision SA4200HD may work better for a "
1405 "SA3250HD box.");
1407}
1408
1409void FirewireModel::SetGUID([[maybe_unused]] const QString &_guid)
1410{
1411#if CONFIG_FIREWIRE
1412 AVCInfo info = m_guid->GetAVCInfo(_guid);
1413 QString model = FirewireDevice::GetModelName(info.m_vendorid, info.m_modelid);
1414 setValue(std::max(getValueIndex(model), 0));
1415#endif // CONFIG_FIREWIRE
1416}
1417
1418void FirewireDesc::SetGUID([[maybe_unused]] const QString &_guid)
1419{
1420 setLabel(tr("Description"));
1421
1422#if CONFIG_FIREWIRE
1423 QString name = m_guid->GetAVCInfo(_guid).m_product_name;
1424 name.replace("Scientific-Atlanta", "SA");
1425 name.replace(", Inc.", "");
1426 name.replace("Explorer(R)", "");
1427 name = name.simplified();
1428 setValue((name.isEmpty()) ? "" : name);
1429#endif // CONFIG_FIREWIRE
1430}
1431
1433{
1434 public:
1435 explicit FirewireConnection(const CaptureCard &parent) :
1437 "firewire_connection"))
1438 {
1439 setLabel(QObject::tr("Connection Type"));
1440 addSelection(QObject::tr("Point to Point"),"0");
1441 addSelection(QObject::tr("Broadcast"),"1");
1442 }
1443
1445 {
1446 delete GetStorage();
1447 }
1448};
1449
1451{
1452 public:
1453 explicit FirewireSpeed(const CaptureCard &parent) :
1455 "firewire_speed"))
1456 {
1457 setLabel(QObject::tr("Speed"));
1458 addSelection(QObject::tr("100Mbps"),"0");
1459 addSelection(QObject::tr("200Mbps"),"1");
1460 addSelection(QObject::tr("400Mbps"),"2");
1461 addSelection(QObject::tr("800Mbps"),"3");
1462 }
1463
1465 {
1466 delete GetStorage();
1467 }
1468};
1469
1470#if CONFIG_FIREWIRE
1471static void FirewireConfigurationGroup(CaptureCard& parent, CardType& cardtype)
1472{
1473 auto *dev(new FirewireGUID(parent));
1474 auto *desc(new FirewireDesc(dev));
1475 auto *model(new FirewireModel(parent, dev));
1476 cardtype.addTargetedChild("FIREWIRE", dev);
1477 cardtype.addTargetedChild("FIREWIRE", new EmptyAudioDevice(parent));
1478 cardtype.addTargetedChild("FIREWIRE", new EmptyVBIDevice(parent));
1479 cardtype.addTargetedChild("FIREWIRE", desc);
1480 cardtype.addTargetedChild("FIREWIRE", model);
1481
1482#if CONFIG_FIREWIRE_LINUX
1483 cardtype.addTargetedChild("FIREWIRE", new FirewireConnection(parent));
1484 cardtype.addTargetedChild("FIREWIRE", new FirewireSpeed(parent));
1485#endif // CONFIG_FIREWIRE_LINUX
1486
1487 cardtype.addTargetedChild("FIREWIRE", new SignalTimeout(parent, 2s, 1s));
1488 cardtype.addTargetedChild("FIREWIRE", new ChannelTimeout(parent, 9s, 1.75s));
1489
1490 model->SetGUID(dev->getValue());
1491 desc->SetGUID(dev->getValue());
1492 QObject::connect(dev, qOverload<const QString&>(&StandardSetting::valueChanged),
1493 model, &FirewireModel::SetGUID);
1494 QObject::connect(dev, qOverload<const QString&>(&StandardSetting::valueChanged),
1495 desc, &FirewireDesc::SetGUID);
1496}
1497#endif
1498
1499#if CONFIG_HDHOMERUN
1500
1501// -----------------------
1502// HDHomeRun Configuration
1503// -----------------------
1504
1505HDHomeRunDeviceID::HDHomeRunDeviceID(const CaptureCard &parent,
1506 HDHomeRunConfigurationGroup &_group) :
1508 new CaptureCardDBStorage(this, parent, "videodevice")),
1509 m_group(_group)
1510{
1511 setVisible(false);
1512};
1513
1514HDHomeRunDeviceID::~HDHomeRunDeviceID()
1515{
1516 delete GetStorage();
1517}
1518
1519void HDHomeRunDeviceID::Load(void)
1520{
1522 m_group.SetDeviceCheckBoxes(getValue());
1523}
1524
1525void HDHomeRunDeviceID::Save(void)
1526{
1527 setValue(m_group.GetDeviceCheckBoxes());
1529}
1530
1531class HDHomeRunEITScan : public MythUICheckBoxSetting
1532{
1533 public:
1534 explicit HDHomeRunEITScan(const CaptureCard &parent) :
1536 new CaptureCardDBStorage(this, parent, "dvb_eitscan"))
1537 {
1538 setLabel(QObject::tr("Use HDHomeRun for active EIT scan"));
1539 setValue(true);
1541 QObject::tr("If enabled, activate active scanning for "
1542 "program data (EIT). When this option is enabled "
1543 "the HDHomeRun is constantly in use."));
1544 };
1545
1546 ~HDHomeRunEITScan() override
1547 {
1548 delete GetStorage();
1549 }
1550};
1551
1552
1553class UseHDHomeRunDevice : public TransMythUICheckBoxSetting
1554{
1555 public:
1556 explicit UseHDHomeRunDevice(QString &deviceid, QString &model,
1557 QString &ipaddr)
1558 {
1559 setLabel(QObject::tr("Use HDHomeRun %1 (%2 %3)")
1560 .arg(deviceid, model, ipaddr));
1561 setValue(false);
1563 QObject::tr("If enabled, use tuners from this HDHomeRun "
1564 "device."));
1565 };
1566};
1567
1568HDHomeRunConfigurationGroup::HDHomeRunConfigurationGroup
1569 (CaptureCard& a_parent, CardType &a_cardtype) :
1570 m_parent(a_parent),
1571 m_deviceId(new HDHomeRunDeviceID(a_parent, *this))
1572{
1573 setVisible(false);
1574
1575 // Fill Device list
1576 FillDeviceList();
1577
1578 QMap<QString, HDHomeRunDevice>::iterator dit;
1579 for (dit = m_deviceList.begin(); dit != m_deviceList.end(); ++dit)
1580 {
1581 HDHomeRunDevice &dev = *dit;
1582 dev.m_checkbox = new UseHDHomeRunDevice(
1583 dev.m_deviceId, dev.m_model, dev.m_cardIp);
1584 a_cardtype.addTargetedChild("HDHOMERUN", dev.m_checkbox);
1585 }
1586 a_cardtype.addTargetedChild("HDHOMERUN", new EmptyAudioDevice(m_parent));
1587 a_cardtype.addTargetedChild("HDHOMERUN", new EmptyVBIDevice(m_parent));
1588 a_cardtype.addTargetedChild("HDHOMERUN", m_deviceId);
1589
1590 auto *buttonRecOpt = new GroupSetting();
1591 buttonRecOpt->setLabel(tr("Recording Options"));
1592 buttonRecOpt->addChild(new SignalTimeout(m_parent, 3s, 0.25s));
1593 buttonRecOpt->addChild(new ChannelTimeout(m_parent, 6s, 1.75s));
1594 buttonRecOpt->addChild(new HDHomeRunEITScan(m_parent));
1595 a_cardtype.addTargetedChild("HDHOMERUN", buttonRecOpt);
1596};
1597
1598void HDHomeRunConfigurationGroup::FillDeviceList(void)
1599{
1600 m_deviceList.clear();
1601
1602 // Find physical devices first
1603 // ProbeVideoDevices returns "deviceid ip" pairs
1604 QStringList devs = CardUtil::ProbeVideoDevices("HDHOMERUN");
1605
1606 for (const auto & dev : std::as_const(devs))
1607 {
1608 QStringList devinfo = dev.split(" ");
1609 const QString& devid = devinfo.at(0);
1610 const QString& devip = devinfo.at(1);
1611 const QString& model = devinfo.at(2);
1612
1613 HDHomeRunDevice tmpdevice;
1614 tmpdevice.m_model = model;
1615 tmpdevice.m_cardIp = devip;
1616 tmpdevice.m_deviceId = devid;
1617 // Fully specify object. Checkboxes will be added later when
1618 // the configuration group is created.
1619 tmpdevice.m_checkbox = nullptr;
1620 m_deviceList[tmpdevice.m_deviceId] = tmpdevice;
1621 }
1622
1623#if 0
1624 // Debug dump of cards
1625 QMap<QString, HDHomeRunDevice>::iterator debugit;
1626 for (debugit = m_deviceList.begin(); debugit != m_deviceList.end(); ++debugit)
1627 {
1628 LOG(VB_GENERAL, LOG_DEBUG, QString("%1: %2 %3")
1629 .arg(debugit.key()).arg((*debugit).model)
1630 .arg((*debugit).cardip));
1631 }
1632#endif
1633}
1634
1635void HDHomeRunConfigurationGroup::SetDeviceCheckBoxes(const QString& devices)
1636{
1637 QStringList devstrs = devices.split(",");
1638 for (const QString& devstr : std::as_const(devstrs))
1639 {
1640 // Get the HDHomeRun device ID using libhdhomerun. We need to
1641 // do it this way because legacy configurations could use an
1642 // IP address and a tuner nubmer.
1643 QByteArray ba = devstr.toUtf8();
1644 hdhomerun_device_t *device = hdhomerun_device_create_from_str(
1645 ba.data(), nullptr);
1646 if (!device)
1647 continue;
1648 QString devid = QString("%1").arg(
1649 hdhomerun_device_get_device_id(device), 8, 16).toUpper();
1650 hdhomerun_device_destroy(device);
1651
1652 // If we know about this device, set its checkbox to on.
1653 QMap<QString, HDHomeRunDevice>::iterator dit;
1654 dit = m_deviceList.find(devid);
1655 if (dit != m_deviceList.end())
1656 (*dit).m_checkbox->setValue(true);
1657 }
1658}
1659
1660QString HDHomeRunConfigurationGroup::GetDeviceCheckBoxes(void)
1661{
1662 // Return a string listing each HDHomeRun device with its checbox
1663 // turned on.
1664 QStringList devstrs;
1665 QMap<QString, HDHomeRunDevice>::iterator dit;
1666 for (dit = m_deviceList.begin(); dit != m_deviceList.end(); ++dit)
1667 {
1668 if ((*dit).m_checkbox->boolValue())
1669 devstrs << (*dit).m_deviceId;
1670 }
1671 QString devices = devstrs.join(",");
1672 return devices;
1673}
1674
1675#endif
1676
1677// -----------------------
1678// VBOX Configuration
1679// -----------------------
1680
1682{
1683 setLabel(QObject::tr("IP Address"));
1684 setHelpText(QObject::tr("Device IP or ID of a VBox device. eg. '192.168.1.100' or 'vbox_3718'"));
1685 VBoxIP::setEnabled(false);
1686 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
1687 this, &VBoxIP::UpdateDevices);
1688};
1689
1691{
1693 if (e)
1694 {
1695 if (!m_oldValue.isEmpty())
1697 emit NewIP(getValue());
1698 }
1699 else
1700 {
1701 m_oldValue = getValue();
1702 }
1703}
1704
1705void VBoxIP::UpdateDevices(const QString &v)
1706{
1707 if (isEnabled())
1708 emit NewIP(v);
1709}
1710
1712{
1713 setLabel(QObject::tr("Tuner"));
1714 setHelpText(QObject::tr("Number and type of the tuner to use. eg '1-DVBT/T2'."));
1716 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
1718};
1719
1721{
1723 if (e) {
1724 if (!m_oldValue.isEmpty())
1726 emit NewTuner(getValue());
1727 }
1728 else
1729 {
1730 m_oldValue = getValue();
1731 }
1732}
1733
1734void VBoxTunerIndex::UpdateDevices(const QString &v)
1735{
1736 if (isEnabled())
1737 emit NewTuner(v);
1738}
1739
1741 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, "videodevice"))
1742{
1743 setLabel(tr("Device ID"));
1744 setHelpText(tr("Device ID of VBox device"));
1745 setReadOnly(true);
1746}
1747
1749{
1750 delete GetStorage();
1751}
1752
1753void VBoxDeviceID::SetIP(const QString &ip)
1754{
1755 m_ip = ip;
1756 setValue(QString("%1-%2").arg(m_ip, m_tuner));
1757}
1758
1759void VBoxDeviceID::SetTuner(const QString &tuner)
1760{
1761 m_tuner = tuner;
1762 setValue(QString("%1-%2").arg(m_ip, m_tuner));
1763}
1764
1765void VBoxDeviceID::SetOverrideDeviceID(const QString &deviceid)
1766{
1767 m_overrideDeviceId = deviceid;
1768 setValue(deviceid);
1769}
1770
1772{
1773 GetStorage()->Load();
1774 if (!m_overrideDeviceId.isEmpty())
1775 {
1777 m_overrideDeviceId.clear();
1778 }
1779}
1780
1782 VBoxDeviceID *deviceid,
1783 StandardSetting *desc,
1784 VBoxIP *cardip,
1785 VBoxTunerIndex *cardtuner,
1786 VBoxDeviceList *devicelist,
1787 const CaptureCard &parent) :
1788 m_deviceId(deviceid),
1789 m_desc(desc),
1790 m_cardIp(cardip),
1791 m_cardTuner(cardtuner),
1792 m_deviceList(devicelist),
1793 m_parent(parent)
1794{
1795 setLabel(QObject::tr("Available devices"));
1797 QObject::tr(
1798 "Device IP or ID, tuner number and tuner type of available VBox devices."));
1799
1800 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
1802};
1803
1805void VBoxDeviceIDList::fillSelections(const QString &cur)
1806{
1808
1809 std::vector<QString> devs;
1810 QMap<QString, bool> in_use;
1811
1812 const QString& current = cur;
1813
1814 for (auto it = m_deviceList->begin(); it != m_deviceList->end(); ++it)
1815 {
1816 devs.push_back(it.key());
1817 in_use[it.key()] = (*it).m_inUse;
1818 }
1819
1820 QString man_addr = VBoxDeviceIDList::tr("Manually Enter IP Address");
1821 QString sel = man_addr;
1822 devs.push_back(sel);
1823
1824 for (const auto & dev : devs)
1825 sel = (current == dev) ? dev : sel;
1826
1827 QString usestr = QString(" -- ");
1828 usestr += QObject::tr("Warning: already in use");
1829
1830 for (const auto & dev : devs)
1831 {
1832 QString desc = dev + (in_use[dev] ? usestr : "");
1833 addSelection(desc, dev, dev == sel);
1834 }
1835
1836 if (current != cur)
1837 {
1839 }
1840 else if (sel == man_addr && !current.isEmpty())
1841 {
1842 // Populate the proper values for IP address and tuner
1843 QStringList selection = current.split("-");
1844
1845 m_cardIp->SetOldValue(selection.first());
1846 m_cardTuner->SetOldValue(selection.last());
1847
1848 m_cardIp->setValue(selection.first());
1849 m_cardTuner->setValue(selection.last());
1850 }
1851}
1852
1854{
1856
1857 int cardid = m_parent.getCardID();
1858 QString device = CardUtil::GetVideoDevice(cardid);
1859 fillSelections(device);
1860}
1861
1863{
1864 if (v == VBoxDeviceIDList::tr("Manually Enter IP Address"))
1865 {
1866 m_cardIp->setEnabled(true);
1867 m_cardTuner->setEnabled(true);
1868 }
1869 else if (!v.isEmpty())
1870 {
1871 if (m_oldValue == VBoxDeviceIDList::tr("Manually Enter IP Address"))
1872 {
1873 m_cardIp->setEnabled(false);
1874 m_cardTuner->setEnabled(false);
1875 }
1876 m_deviceId->setValue(v);
1877
1878 // Update _cardip and _cardtuner
1880 m_cardTuner->setValue(QString("%1").arg((*m_deviceList)[v].m_tunerNo));
1882 }
1883 m_oldValue = v;
1884};
1885
1886// -----------------------
1887// IPTV Configuration
1888// -----------------------
1889
1891{
1892 public:
1893 explicit IPTVHost(const CaptureCard &parent) :
1894 CaptureCardTextEditSetting(parent, "videodevice")
1895 {
1896 setValue("http://mafreebox.freebox.fr/freeboxtv/playlist.m3u");
1897 setLabel(QObject::tr("M3U URL"));
1899 QObject::tr("URL of M3U file containing RTSP/RTP/UDP/HTTP channel URLs,"
1900 " example for HDHomeRun: http://<ipv4>/lineup.m3u and for Freebox:"
1901 " http://mafreebox.freebox.fr/freeboxtv/playlist.m3u."
1902 ));
1903 }
1904};
1905
1906static void IPTVConfigurationGroup(CaptureCard& parent, CardType& cardType)
1907{
1908 cardType.addTargetedChild("FREEBOX", new IPTVHost(parent));
1909 cardType.addTargetedChild("FREEBOX", new ChannelTimeout(parent, 30s, 1.75s));
1910 cardType.addTargetedChild("FREEBOX", new EmptyAudioDevice(parent));
1911 cardType.addTargetedChild("FREEBOX", new EmptyVBIDevice(parent));
1912}
1913
1915{
1916 public:
1917 explicit ASIDevice(const CaptureCard &parent) :
1918 CaptureCardComboBoxSetting(parent, true, "videodevice")
1919 {
1920 setLabel(QObject::tr("ASI device"));
1921 fillSelections(QString());
1922 };
1923
1927 void fillSelections(const QString &current)
1928 {
1930
1931 // Get devices from filesystem
1932 QStringList sdevs = CardUtil::ProbeVideoDevices("ASI");
1933
1934 // Add current if needed
1935 if (!current.isEmpty() &&
1936 (std::find(sdevs.begin(), sdevs.end(), current) == sdevs.end()))
1937 {
1938 std::stable_sort(sdevs.begin(), sdevs.end());
1939 }
1940
1941 // Get devices from DB
1942 QStringList db = CardUtil::GetVideoDevices("ASI");
1943
1944 // Figure out which physical devices are already in use
1945 // by another card defined in the DB, and select a device
1946 // for new configs (preferring non-conflicing devices).
1947 QMap<QString,bool> in_use;
1948 QString sel = current;
1949 for (const QString& dev : std::as_const(sdevs))
1950 {
1951 in_use[dev] = std::find(db.begin(), db.end(), dev) != db.end();
1952 if (sel.isEmpty() && !in_use[dev])
1953 sel = dev;
1954 }
1955
1956 // Unfortunately all devices are conflicted, select first device.
1957 if (sel.isEmpty() && !sdevs.empty())
1958 sel = sdevs[0];
1959
1960 QString usestr = QString(" -- ");
1961 usestr += QObject::tr("Warning: already in use");
1962
1963 // Add the devices to the UI
1964 bool found = false;
1965 for (const QString& dev : std::as_const(sdevs))
1966 {
1967 QString desc = dev + (in_use[dev] ? usestr : "");
1968 desc = (current == dev) ? dev : desc;
1969 addSelection(desc, dev, dev == sel);
1970 found |= (dev == sel);
1971 }
1972
1973 // If a configured device isn't on the list, add it with warning
1974 if (!found && !current.isEmpty())
1975 {
1976 QString desc = current + " -- " +
1977 QObject::tr("Warning: unable to open");
1978 addSelection(desc, current, true);
1979 }
1980 }
1981
1982 void Load(void) override // StandardSetting
1983 {
1985 addSelection(QString());
1986 GetStorage()->Load();
1988 }
1989};
1990
1992 CardType &cardType):
1993 m_parent(a_parent),
1994 m_device(new ASIDevice(m_parent)),
1995 m_cardInfo(new TransTextEditSetting())
1996{
1997 setVisible(false);
1998 m_cardInfo->setLabel(tr("Status"));
1999 m_cardInfo->setEnabled(false);
2000
2001 cardType.addTargetedChild("ASI", m_device);
2002 cardType.addTargetedChild("ASI", new EmptyAudioDevice(m_parent));
2003 cardType.addTargetedChild("ASI", new EmptyVBIDevice(m_parent));
2004 cardType.addTargetedChild("ASI", m_cardInfo);
2005
2006 connect(m_device, qOverload<const QString&>(&StandardSetting::valueChanged),
2008
2010};
2011
2012void ASIConfigurationGroup::probeCard([[maybe_unused]] const QString &device)
2013{
2014#if CONFIG_ASI
2015 if (device.isEmpty())
2016 {
2017 m_cardInfo->setValue("");
2018 return;
2019 }
2020
2021 if ((m_parent.getCardID() != 0) && m_parent.GetRawCardType() != "ASI")
2022 {
2023 m_cardInfo->setValue("");
2024 return;
2025 }
2026
2027 QString error;
2028 int device_num = CardUtil::GetASIDeviceNumber(device, &error);
2029 if (device_num < 0)
2030 {
2031 m_cardInfo->setValue(tr("Not a valid DVEO ASI card"));
2032 LOG(VB_GENERAL, LOG_WARNING,
2033 "ASIConfigurationGroup::probeCard(), Warning: " + error);
2034 return;
2035 }
2036 m_cardInfo->setValue(tr("Valid DVEO ASI card"));
2037#else
2038 m_cardInfo->setValue(QString("Not compiled with ASI support"));
2039#endif
2040}
2041
2043 CardType& a_cardtype):
2044 m_parent(a_parent),
2045 m_info(new GroupSetting()), m_size(new GroupSetting())
2046{
2047 setVisible(false);
2048 auto *device = new FileDevice(m_parent);
2049 device->setHelpText(tr("A local file used to simulate a recording."
2050 " Leave empty to use MythEvents to trigger an"
2051 " external program to import recording files."));
2052 a_cardtype.addTargetedChild("IMPORT", device);
2053
2054 a_cardtype.addTargetedChild("IMPORT", new EmptyAudioDevice(m_parent));
2055 a_cardtype.addTargetedChild("IMPORT", new EmptyVBIDevice(m_parent));
2056
2057 m_info->setLabel(tr("File info"));
2058 a_cardtype.addTargetedChild("IMPORT", m_info);
2059
2060 m_size->setLabel(tr("File size"));
2061 a_cardtype.addTargetedChild("IMPORT", m_size);
2062
2063 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2065
2066 probeCard(device->getValue());
2067};
2068
2069void ImportConfigurationGroup::probeCard(const QString &device)
2070{
2071 QString ci;
2072 QString cs;
2073 QFileInfo fileInfo(device);
2074
2075 // For convenience, ImportRecorder allows both formats:
2076 if (device.startsWith("file:", Qt::CaseInsensitive))
2077 fileInfo.setFile(device.mid(5));
2078
2079 if (fileInfo.exists())
2080 {
2081 if (fileInfo.isReadable() && (fileInfo.isFile()))
2082 {
2083 ci = HTTPRequest::TestMimeType(fileInfo.absoluteFilePath());
2084 cs = tr("%1 MB").arg(fileInfo.size() / 1024 / 1024);
2085 }
2086 else
2087 {
2088 ci = tr("File not readable");
2089 }
2090 }
2091 else
2092 {
2093 ci = tr("File %1 does not exist").arg(device);
2094 }
2095
2096 m_info->setValue(ci);
2097 m_size->setValue(cs);
2098}
2099
2100// -----------------------
2101// VBox Configuration
2102// -----------------------
2103
2105 (CaptureCard& a_parent, CardType& a_cardtype) :
2106 m_parent(a_parent),
2107 m_desc(new GroupSetting()),
2108 m_deviceId(new VBoxDeviceID(a_parent)),
2109 m_cardIp(new VBoxIP()),
2110 m_cardTuner(new VBoxTunerIndex())
2111{
2112 setVisible(false);
2113
2114 // Fill Device list
2116
2117 m_desc->setLabel(tr("Description"));
2120
2121 a_cardtype.addTargetedChild("VBOX", m_deviceIdList);
2122 a_cardtype.addTargetedChild("VBOX", new EmptyAudioDevice(m_parent));
2123 a_cardtype.addTargetedChild("VBOX", new EmptyVBIDevice(m_parent));
2124 a_cardtype.addTargetedChild("VBOX", m_deviceId);
2125 a_cardtype.addTargetedChild("VBOX", m_desc);
2126 a_cardtype.addTargetedChild("VBOX", m_cardIp);
2127 a_cardtype.addTargetedChild("VBOX", m_cardTuner);
2128 a_cardtype.addTargetedChild("VBOX", new SignalTimeout(m_parent, 7s, 1s));
2129 a_cardtype.addTargetedChild("VBOX", new ChannelTimeout(m_parent, 10s, 1.75s));
2130
2131 connect(m_cardIp, &VBoxIP::NewIP,
2135};
2136
2138{
2139 m_deviceList.clear();
2140
2141 // Find physical devices first
2142 // ProbeVideoDevices returns "deviceid ip tunerno tunertype"
2143 QStringList devs = CardUtil::ProbeVideoDevices("VBOX");
2144
2145 for (const auto & dev : std::as_const(devs))
2146 {
2147 QStringList devinfo = dev.split(" ");
2148 const QString& id = devinfo.at(0);
2149 const QString& ip = devinfo.at(1);
2150 const QString& tunerNo = devinfo.at(2);
2151 const QString& tunerType = devinfo.at(3);
2152
2153 VBoxDevice tmpdevice;
2154 tmpdevice.m_deviceId = id;
2155 tmpdevice.m_desc = CardUtil::GetVBoxdesc(id, ip, tunerNo, tunerType);
2156 tmpdevice.m_cardIp = ip;
2157 tmpdevice.m_inUse = false;
2158 tmpdevice.m_discovered = true;
2159 tmpdevice.m_tunerNo = tunerNo;
2160 tmpdevice.m_tunerType = tunerType;
2161 tmpdevice.m_mythDeviceId = id + "-" + tunerNo + "-" + tunerType;
2162 m_deviceList[tmpdevice.m_mythDeviceId] = tmpdevice;
2163 }
2164
2165 // Now find configured devices
2166
2167 // returns "ip.ip.ip.ip-n-type" or deviceid-n-type values
2168 QStringList db = CardUtil::GetVideoDevices("VBOX");
2169
2170 for (const auto & dev : std::as_const(db))
2171 {
2172 QMap<QString, VBoxDevice>::iterator dit;
2173 dit = m_deviceList.find(dev);
2174
2175 if (dit != m_deviceList.end())
2176 (*dit).m_inUse = true;
2177 }
2178}
2179
2180// -----------------------
2181// Ceton Configuration
2182// -----------------------
2183#if CONFIG_CETON
2184CetonSetting::CetonSetting(QString label, const QString& helptext)
2185{
2186 setLabel(std::move(label));
2187 setHelpText(helptext);
2188 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
2189 this, &CetonSetting::UpdateDevices);
2190}
2191
2192void CetonSetting::UpdateDevices(const QString &v)
2193{
2194 if (isEnabled())
2195 emit NewValue(v);
2196}
2197
2198void CetonSetting::LoadValue(const QString &value)
2199{
2200 setValue(value);
2201}
2202
2203CetonDeviceID::CetonDeviceID(const CaptureCard &parent) :
2204 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, "videodevice")),
2205 m_parent(parent)
2206{
2207 setLabel(tr("Device ID"));
2208 setHelpText(tr("Device ID of Ceton device"));
2209}
2210
2211CetonDeviceID::~CetonDeviceID()
2212{
2213 delete GetStorage();
2214}
2215
2216void CetonDeviceID::SetIP(const QString &ip)
2217{
2218 static const QRegularExpression ipV4Regex
2219 { "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){4}$" };
2220 auto match = ipV4Regex.match(ip + ".");
2221 if (match.hasMatch())
2222 {
2223 m_ip = ip;
2224 setValue(QString("%1-RTP.%3").arg(m_ip, m_tuner));
2225 }
2226}
2227
2228void CetonDeviceID::SetTuner(const QString &tuner)
2229{
2230 static const QRegularExpression oneDigit { "^\\d$" };
2231 auto match = oneDigit.match(tuner);
2232 if (match.hasMatch())
2233 {
2234 m_tuner = tuner;
2235 setValue(QString("%1-RTP.%2").arg(m_ip, m_tuner));
2236 }
2237}
2238
2239void CetonDeviceID::Load(void)
2240{
2241 GetStorage()->Load();
2242 UpdateValues();
2243}
2244
2245void CetonDeviceID::UpdateValues(void)
2246{
2247 static const QRegularExpression newstyle { R"(^([0-9.]+)-(\d|RTP)\.(\d)$)" };
2248 auto match = newstyle.match(getValue());
2249 if (match.hasMatch())
2250 {
2251 emit LoadedIP(match.captured(1));
2252 emit LoadedTuner(match.captured(3));
2253 }
2254}
2255
2256void CetonSetting::CetonConfigurationGroup(CaptureCard& parent, CardType& cardtype)
2257{
2258 auto *deviceid = new CetonDeviceID(parent);
2259 auto *desc = new GroupSetting();
2260 desc->setLabel(tr("CetonConfigurationGroup", "Description"));
2261 auto *ip = new CetonSetting(tr("IP Address"),
2262 tr("IP Address of the Ceton device (192.168.200.1 by default)"));
2263 auto *tuner = new CetonSetting(tr("Tuner"),
2264 tr("Number of the tuner on the Ceton device (first tuner is number 0)"));
2265
2266 cardtype.addTargetedChild("CETON", ip);
2267 cardtype.addTargetedChild("CETON", tuner);
2268 cardtype.addTargetedChild("CETON", deviceid);
2269 cardtype.addTargetedChild("CETON", desc);
2270 cardtype.addTargetedChild("CETON", new SignalTimeout(parent, 1s, 0.25s));
2271 cardtype.addTargetedChild("CETON", new ChannelTimeout(parent, 3s, 1.75s));
2272
2273 QObject::connect(ip, &CetonSetting::NewValue,
2274 deviceid, &CetonDeviceID::SetIP);
2275 QObject::connect(tuner, &CetonSetting::NewValue,
2276 deviceid, &CetonDeviceID::SetTuner);
2277
2278 QObject::connect(deviceid, &CetonDeviceID::LoadedIP,
2279 ip, &CetonSetting::LoadValue);
2280 QObject::connect(deviceid, &CetonDeviceID::LoadedTuner,
2281 tuner, &CetonSetting::LoadValue);
2282}
2283#endif
2284
2285// Override database schema default, set schedgroup false
2287{
2288 public:
2289 explicit SchedGroupFalse(const CaptureCard &parent) :
2291 "schedgroup"))
2292 {
2293 setValue(false);
2294 setVisible(false);
2295 };
2296
2298 {
2299 delete GetStorage();
2300 }
2301};
2302
2304 CardType& cardtype,
2305 const QString &inputtype) :
2306 m_parent(parent),
2307 m_cardInfo(new GroupSetting()),
2308 m_vbiDev(new VBIDevice(m_parent))
2309{
2310 setVisible(false);
2311 QRegularExpression drv { "^(?!ivtv|hdpvr|(saa7164(.*))).*$" };
2312 auto *device = new VideoDevice(m_parent, 0, 15, QString(), drv);
2313
2314 m_cardInfo->setLabel(tr("Probed info"));
2315 m_cardInfo->setReadOnly(true);
2316
2317 cardtype.addTargetedChild(inputtype, device);
2318 cardtype.addTargetedChild(inputtype, m_cardInfo);
2319 cardtype.addTargetedChild(inputtype, m_vbiDev);
2320 cardtype.addTargetedChild(inputtype, new AudioDevice(m_parent));
2321 cardtype.addTargetedChild(inputtype, new AudioRateLimit(m_parent));
2322 cardtype.addTargetedChild(inputtype, new SkipBtAudio(m_parent));
2323
2324 // Override database schema default, set schedgroup false
2325 cardtype.addTargetedChild(inputtype, new SchedGroupFalse(m_parent));
2326
2327 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2329
2330 probeCard(device->getValue());
2331};
2332
2333void V4LConfigurationGroup::probeCard(const QString &device)
2334{
2335 QString cn = tr("Failed to open");
2336 QString ci = cn;
2337 QString dn;
2338
2339 QByteArray adevice = device.toLatin1();
2340 int videofd = open(adevice.constData(), O_RDWR);
2341 if (videofd >= 0)
2342 {
2343 if (!CardUtil::GetV4LInfo(videofd, cn, dn))
2344 ci = cn = tr("Failed to probe");
2345 else if (!dn.isEmpty())
2346 ci = cn + " [" + dn + "]";
2347 close(videofd);
2348 }
2349
2350 m_cardInfo->setValue(ci);
2351 m_vbiDev->setFilter(cn, dn);
2352}
2353
2355 CardType &cardtype) :
2356 m_parent(parent),
2357 m_vbiDevice(new VBIDevice(parent)),
2358 m_cardInfo(new GroupSetting())
2359{
2360 setVisible(false);
2361 QRegularExpression drv { "^(ivtv|(saa7164(.*)))$" };
2362 m_device = new VideoDevice(m_parent, 0, 15, QString(), drv);
2363 m_vbiDevice->setVisible(false);
2364
2365 m_cardInfo->setLabel(tr("Probed info"));
2366 m_cardInfo->setReadOnly(true);
2367
2368 cardtype.addTargetedChild("MPEG", m_device);
2369 cardtype.addTargetedChild("MPEG", m_vbiDevice);
2370 cardtype.addTargetedChild("MPEG", m_cardInfo);
2371 cardtype.addTargetedChild("MPEG", new ChannelTimeout(m_parent, 12s, 2s));
2372
2373 // Override database schema default, set schedgroup false
2374 cardtype.addTargetedChild("MPEG", new SchedGroupFalse(m_parent));
2375
2376 connect(m_device, qOverload<const QString&>(&StandardSetting::valueChanged),
2378
2380}
2381
2382void MPEGConfigurationGroup::probeCard(const QString &device)
2383{
2384 QString cn = tr("Failed to open");
2385 QString ci = cn;
2386 QString dn;
2387
2388 QByteArray adevice = device.toLatin1();
2389 int videofd = open(adevice.constData(), O_RDWR);
2390 if (videofd >= 0)
2391 {
2392 if (!CardUtil::GetV4LInfo(videofd, cn, dn))
2393 ci = cn = tr("Failed to probe");
2394 else if (!dn.isEmpty())
2395 ci = cn + " [" + dn + "]";
2396 close(videofd);
2397 }
2398
2399 m_cardInfo->setValue(ci);
2400 m_vbiDevice->setVisible(dn!="ivtv");
2401 m_vbiDevice->setFilter(cn, dn);
2402}
2403
2405 CardType &a_cardtype) :
2406 m_parent(a_parent),
2407 m_info(new GroupSetting()), m_size(new GroupSetting())
2408{
2409 setVisible(false);
2410 auto *device = new FileDevice(m_parent);
2411 device->setHelpText(tr("A local MPEG file used to simulate a recording."));
2412
2413 a_cardtype.addTargetedChild("DEMO", device);
2414
2415 a_cardtype.addTargetedChild("DEMO", new EmptyAudioDevice(m_parent));
2416 a_cardtype.addTargetedChild("DEMO", new EmptyVBIDevice(m_parent));
2417
2418 m_info->setLabel(tr("File info"));
2419 a_cardtype.addTargetedChild("DEMO", m_info);
2420
2421 m_size->setLabel(tr("File size"));
2422 a_cardtype.addTargetedChild("DEMO", m_size);
2423
2424 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2426
2427 probeCard(device->getValue());
2428}
2429
2430void DemoConfigurationGroup::probeCard(const QString &device)
2431{
2432 QString ci;
2433 QString cs;
2434 QFileInfo fileInfo(device);
2435 if (fileInfo.exists())
2436 {
2437 if (fileInfo.isReadable() && (fileInfo.isFile()))
2438 {
2439 ci = HTTPRequest::TestMimeType(fileInfo.absoluteFilePath());
2440 cs = tr("%1 MB").arg(fileInfo.size() / 1024 / 1024);
2441 }
2442 else
2443 {
2444 ci = tr("File not readable");
2445 }
2446 }
2447 else
2448 {
2449 ci = tr("File does not exist");
2450 }
2451
2452 m_info->setValue(ci);
2453 m_size->setValue(cs);
2454}
2455
2456#ifndef Q_OS_WINDOWS
2457ExternalConfigurationGroup::ExternalConfigurationGroup(CaptureCard &a_parent,
2458 CardType &a_cardtype) :
2459 m_parent(a_parent),
2460 m_info(new GroupSetting())
2461{
2462 setVisible(false);
2463 auto *device = new CommandPath(m_parent);
2464 device->setLabel(tr("Command path"));
2465 device->setHelpText(tr("A 'black box' application controlled via stdin, status on "
2466 "stderr and TransportStream read from stdout.\n"
2467 "Use absolute path or path relative to the current directory."));
2468 a_cardtype.addTargetedChild("EXTERNAL", device);
2469
2470 m_info->setLabel(tr("File info"));
2471 a_cardtype.addTargetedChild("EXTERNAL", m_info);
2472
2473 a_cardtype.addTargetedChild("EXTERNAL",
2474 new ChannelTimeout(m_parent, 20s, 1.75s));
2475
2476 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2477 this, &ExternalConfigurationGroup::probeApp);
2478
2479 probeApp(device->getValue());
2480}
2481
2482void ExternalConfigurationGroup::probeApp(const QString & path)
2483{
2484 int idx1 = path.startsWith("file:", Qt::CaseInsensitive) ? 5 : 0;
2485 int idx2 = path.indexOf(' ', idx1);
2486
2487 QString ci;
2488 QFileInfo fileInfo(path.mid(idx1, idx2 - idx1));
2489
2490 if (fileInfo.exists())
2491 {
2492 ci = tr("File '%1' is valid.").arg(fileInfo.absoluteFilePath());
2493 if (!fileInfo.isReadable() || !fileInfo.isFile())
2494 ci = tr("WARNING: File '%1' is not readable.")
2495 .arg(fileInfo.absoluteFilePath());
2496 if (!fileInfo.isExecutable())
2497 ci = tr("WARNING: File '%1' is not executable.")
2498 .arg(fileInfo.absoluteFilePath());
2499 }
2500 else
2501 {
2502 ci = tr("WARNING: File '%1' does not exist.")
2503 .arg(fileInfo.absoluteFilePath());
2504 }
2505
2506 m_info->setValue(ci);
2507 m_info->setHelpText(ci);
2508}
2509#endif // !defined( Q_OS_WINDOWS )
2510
2512 CardType &a_cardtype) :
2513 m_parent(a_parent), m_cardInfo(new GroupSetting()),
2514 m_audioInput(new TunerCardAudioInput(m_parent, QString(), "HDPVR"))
2515{
2516 setVisible(false);
2517
2518 auto *device = new VideoDevice(m_parent, 0, 15, QString(),
2519 QRegularExpression("^hdpvr$"));
2520
2521 m_cardInfo->setLabel(tr("Probed info"));
2522 m_cardInfo->setReadOnly(true);
2523
2524 a_cardtype.addTargetedChild("HDPVR", device);
2525 a_cardtype.addTargetedChild("HDPVR", new EmptyAudioDevice(m_parent));
2526 a_cardtype.addTargetedChild("HDPVR", new EmptyVBIDevice(m_parent));
2527 a_cardtype.addTargetedChild("HDPVR", m_cardInfo);
2528 a_cardtype.addTargetedChild("HDPVR", m_audioInput);
2529 a_cardtype.addTargetedChild("HDPVR", new ChannelTimeout(m_parent, 15s, 2s));
2530
2531 // Override database schema default, set schedgroup false
2532 a_cardtype.addTargetedChild("HDPVR", new SchedGroupFalse(m_parent));
2533
2534 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2536
2537 probeCard(device->getValue());
2538}
2539
2540void HDPVRConfigurationGroup::probeCard(const QString &device)
2541{
2542 QString cn = tr("Failed to open");
2543 QString ci = cn;
2544 QString dn;
2545
2546 int videofd = open(device.toLocal8Bit().constData(), O_RDWR);
2547 if (videofd >= 0)
2548 {
2549 if (!CardUtil::GetV4LInfo(videofd, cn, dn))
2550 ci = cn = tr("Failed to probe");
2551 else if (!dn.isEmpty())
2552 ci = cn + " [" + dn + "]";
2553 close(videofd);
2555 }
2556
2557 m_cardInfo->setValue(ci);
2558}
2559
2561 m_parent(parent),
2562 m_cardInfo(new GroupSetting())
2563{
2564 setVisible(false);
2565
2566 m_device = new VideoDevice(m_parent, 0, 15);
2567
2568 setLabel(QObject::tr("V4L2 encoder devices (multirec capable)"));
2569
2570 m_cardInfo->setLabel(tr("Probed info"));
2571 m_cardInfo->setReadOnly(true);
2572
2573 cardtype.addTargetedChild("V4L2ENC", m_device);
2574 cardtype.addTargetedChild("V4L2ENC", m_cardInfo);
2575
2576 // Override database schema default, set schedgroup false
2577 cardtype.addTargetedChild("V4L2ENC", new SchedGroupFalse(m_parent));
2578
2579 connect(m_device, qOverload<const QString&>(&StandardSetting::valueChanged),
2581
2582 const QString &device_name = m_device->getValue();
2583 if (!device_name.isEmpty())
2584 probeCard(device_name);
2585}
2586
2587void V4L2encGroup::probeCard([[maybe_unused]] const QString &device_name)
2588{
2589#if CONFIG_V4L2
2590 QString card_name = tr("Failed to open");
2591 QString card_info = card_name;
2592 V4L2util v4l2(device_name);
2593
2594 if (!v4l2.IsOpen())
2595 {
2596 m_driverName = tr("Failed to probe");
2597 return;
2598 }
2599 m_driverName = v4l2.DriverName();
2600 card_name = v4l2.CardName();
2601
2602 if (!m_driverName.isEmpty())
2603 card_info = card_name + " [" + m_driverName + "]";
2604
2605 m_cardInfo->setValue(card_info);
2606
2607 if (m_device->getSubSettings()->empty())
2608 {
2609 auto* audioinput = new TunerCardAudioInput(m_parent, QString(), "V4L2");
2610 if (audioinput->fillSelections(device_name) > 1)
2611 {
2612 audioinput->setName("AudioInput");
2614 }
2615 else
2616 {
2617 delete audioinput;
2618 }
2619
2620 if (v4l2.HasSlicedVBI())
2621 {
2622 auto* vbidev = new VBIDevice(m_parent);
2623 if (vbidev->setFilter(card_name, m_driverName) > 0)
2624 {
2625 vbidev->setName("VBIDevice");
2627 }
2628 else
2629 {
2630 delete vbidev;
2631 }
2632 }
2633
2636 new ChannelTimeout(m_parent, 15s, 2s));
2637 }
2638#endif // CONFIG_V4L2
2639}
2640
2642{
2643 setLabel(QObject::tr("Capture Card Setup"));
2644
2645 auto* cardtype = new CardType(parent);
2646 parent.addChild(cardtype);
2647
2648#if CONFIG_DVB
2649 cardtype->addTargetedChild("DVB",
2650 new DVBConfigurationGroup(parent, *cardtype));
2651#endif // CONFIG_DVB
2652
2653#if CONFIG_V4L2
2654 cardtype->addTargetedChild("HDPVR",
2655 new HDPVRConfigurationGroup(parent, *cardtype));
2656#endif // CONFIG_V4L2
2657
2658#if CONFIG_HDHOMERUN
2659 cardtype->addTargetedChild("HDHOMERUN",
2660 new HDHomeRunConfigurationGroup(parent, *cardtype));
2661#endif // CONFIG_HDHOMERUN
2662
2663#if CONFIG_VBOX
2664 cardtype->addTargetedChild("VBOX",
2665 new VBoxConfigurationGroup(parent, *cardtype));
2666#endif // CONFIG_VBOX
2667
2668#if CONFIG_SATIP
2669 cardtype->addTargetedChild("SATIP",
2670 new SatIPConfigurationGroup(parent, *cardtype));
2671#endif // CONFIG_SATIP
2672
2673#if CONFIG_FIREWIRE
2674 FirewireConfigurationGroup(parent, *cardtype);
2675#endif // CONFIG_FIREWIRE
2676
2677#if CONFIG_CETON
2678 CetonSetting::CetonConfigurationGroup(parent, *cardtype);
2679#endif // CONFIG_CETON
2680
2681#if CONFIG_IPTV
2682 IPTVConfigurationGroup(parent, *cardtype);
2683#endif // CONFIG_IPTV
2684
2685#if CONFIG_V4L2
2686 cardtype->addTargetedChild("V4L2ENC", new V4L2encGroup(parent, *cardtype));
2687 cardtype->addTargetedChild("V4L",
2688 new V4LConfigurationGroup(parent, *cardtype, "V4L"));
2689 cardtype->addTargetedChild("MJPEG",
2690 new V4LConfigurationGroup(parent, *cardtype, "MJPEG"));
2691 cardtype->addTargetedChild("GO7007",
2692 new V4LConfigurationGroup(parent, *cardtype, "GO7007"));
2693 cardtype->addTargetedChild("MPEG",
2694 new MPEGConfigurationGroup(parent, *cardtype));
2695#endif // CONFIG_V4L2
2696
2697#if CONFIG_ASI
2698 cardtype->addTargetedChild("ASI",
2699 new ASIConfigurationGroup(parent, *cardtype));
2700#endif // CONFIG_ASI
2701
2702 // for testing without any actual tuner hardware:
2703 cardtype->addTargetedChild("IMPORT",
2704 new ImportConfigurationGroup(parent, *cardtype));
2705 cardtype->addTargetedChild("DEMO",
2706 new DemoConfigurationGroup(parent, *cardtype));
2707#ifndef Q_OS_WINDOWS
2708 cardtype->addTargetedChild("EXTERNAL",
2709 new ExternalConfigurationGroup(parent,
2710 *cardtype));
2711#endif
2712}
2713
2714CaptureCard::CaptureCard(bool use_card_group)
2715 : m_id(new ID)
2716{
2717 addChild(m_id);
2718 if (use_card_group)
2719 CaptureCardGroup(*this);
2720 addChild(new Hostname(*this));
2721}
2722
2724{
2725 int cardid = getCardID();
2726 if (cardid <= 0)
2727 return {};
2728 return CardUtil::GetRawInputType(cardid);
2729}
2730
2732{
2734 QString qstr =
2735 "SELECT cardid, videodevice, cardtype, displayname "
2736 "FROM capturecard "
2737 "WHERE hostname = :HOSTNAME AND parentid = 0 "
2738 "ORDER BY cardid";
2739
2740 query.prepare(qstr);
2741 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
2742
2743 if (!query.exec())
2744 {
2745 MythDB::DBError("CaptureCard::fillSelections", query);
2746 return;
2747 }
2748
2750
2751 while (query.next())
2752 {
2753 uint cardid = query.value(0).toUInt();
2754 QString videodevice = query.value(1).toString();
2755 QString cardtype = query.value(2).toString();
2756 QString displayname = query.value(3).toString();
2757
2758 QString label = QString("%1 (%2)")
2759 .arg(CardUtil::GetDeviceLabel(cardtype, videodevice), displayname);
2760
2761 auto *card = new CaptureCard();
2762 card->loadByID(cardid);
2763 card->setLabel(label);
2764 setting->addChild(card);
2765 }
2766}
2767
2769{
2770 m_id->setValue(cardid);
2771 Load();
2772}
2773
2775{
2776 return true;
2777}
2778
2780{
2782}
2783
2784
2786{
2787 uint init_cardid = getCardID();
2788 QString init_dev = CardUtil::GetVideoDevice(init_cardid);
2789
2791
2793
2795
2796 uint cardid = getCardID();
2797 QString type = CardUtil::GetRawInputType(cardid);
2798 QString dev = CardUtil::GetVideoDevice(cardid);
2799
2800 if (dev != init_dev)
2801 {
2802 if (!init_dev.isEmpty())
2803 {
2804 uint init_groupid = CardUtil::GetDeviceInputGroup(init_cardid);
2805 CardUtil::UnlinkInputGroup(init_cardid, init_groupid);
2806 }
2807 if (!dev.isEmpty())
2808 {
2809 uint groupid =
2811 gCoreContext->GetHostName(), dev);
2812 CardUtil::LinkInputGroup(cardid, groupid);
2813 CardUtil::UnlinkInputGroup(0, groupid);
2814 }
2815 }
2816
2817 // Handle any cloning we may need to do
2819 {
2820 std::vector<uint> clones = CardUtil::GetChildInputIDs(cardid);
2821 for (uint clone : clones)
2822 CardUtil::CloneCard(cardid, clone);
2823 }
2824}
2825
2827{
2828 if (getCardID() == 0)
2829 {
2830 Save();
2831 Load();
2832 }
2833}
2834
2836 CaptureCardComboBoxSetting(parent, false, "cardtype")
2837{
2838 setLabel(QObject::tr("Card type"));
2839 setHelpText(QObject::tr("Change the cardtype to the appropriate type for "
2840 "the capture card you are configuring."));
2841 fillSelections(this);
2842}
2843
2845{
2846#if CONFIG_DVB
2847 setting->addSelection(
2848 QObject::tr("DVB-T/S/C, ATSC or ISDB-T tuner card"), "DVB");
2849#endif // CONFIG_DVB
2850
2851#if CONFIG_V4L2
2852 setting->addSelection(
2853 QObject::tr("V4L2 encoder"), "V4L2ENC");
2854 setting->addSelection(
2855 QObject::tr("HD-PVR H.264 encoder"), "HDPVR");
2856#endif // CONFIG_V4L2
2857
2858#if CONFIG_HDHOMERUN
2859 setting->addSelection(
2860 QObject::tr("HDHomeRun networked tuner"), "HDHOMERUN");
2861#endif // CONFIG_HDHOMERUN
2862
2863#if CONFIG_SATIP
2864 setting->addSelection(
2865 QObject::tr("Sat>IP networked tuner"), "SATIP");
2866#endif // CONFIG_SATIP
2867
2868#if CONFIG_VBOX
2869 setting->addSelection(
2870 QObject::tr("V@Box TV Gateway networked tuner"), "VBOX");
2871#endif // CONFIG_VBOX
2872
2873#if CONFIG_FIREWIRE
2874 setting->addSelection(
2875 QObject::tr("FireWire cable box"), "FIREWIRE");
2876#endif // CONFIG_FIREWIRE
2877
2878#if CONFIG_CETON
2879 setting->addSelection(
2880 QObject::tr("Ceton Cablecard tuner"), "CETON");
2881#endif // CONFIG_CETON
2882
2883#if CONFIG_IPTV
2884 setting->addSelection(QObject::tr("IPTV recorder"), "FREEBOX");
2885#endif // CONFIG_IPTV
2886
2887#if CONFIG_V4L2
2888 setting->addSelection(
2889 QObject::tr("Analog to MPEG-2 encoder card (PVR-150/250/350, etc)"), "MPEG");
2890 setting->addSelection(
2891 QObject::tr("Analog to MJPEG encoder card (Matrox G200, DC10, etc)"), "MJPEG");
2892 setting->addSelection(
2893 QObject::tr("Analog to MPEG-4 encoder (Plextor ConvertX USB, etc)"),
2894 "GO7007");
2895 setting->addSelection(
2896 QObject::tr("Analog capture card"), "V4L");
2897#endif // CONFIG_V4L2
2898
2899#if CONFIG_ASI
2900 setting->addSelection(QObject::tr("DVEO ASI recorder"), "ASI");
2901#endif
2902
2903 setting->addSelection(QObject::tr("Import test recorder"), "IMPORT");
2904 setting->addSelection(QObject::tr("Demo test recorder"), "DEMO");
2905#ifndef Q_OS_WINDOWS
2906 setting->addSelection(QObject::tr("External (black box) recorder"),
2907 "EXTERNAL");
2908#endif
2909}
2910
2912 StandardSetting(new CaptureCardDBStorage(this, parent, "hostname"))
2913{
2914 setVisible(false);
2916}
2917
2919{
2920 delete GetStorage();
2921}
2922
2924{
2925 public:
2926 explicit InputName(const CardInput &parent) :
2927 MythUIComboBoxSetting(new CardInputDBStorage(this, parent, "inputname"))
2928 {
2929 setLabel(QObject::tr("Input name"));
2930 };
2931
2932 ~InputName() override
2933 {
2934 delete GetStorage();
2935 }
2936
2937 void Load(void) override // StandardSetting
2938 {
2941 };
2942
2944 clearSelections();
2945 addSelection(QObject::tr("(None)"), "None");
2946 auto *storage = dynamic_cast<CardInputDBStorage*>(GetStorage());
2947 if (storage == nullptr)
2948 return;
2949 uint cardid = storage->getInputID();
2950 QString type = CardUtil::GetRawInputType(cardid);
2951 QString device = CardUtil::GetVideoDevice(cardid);
2952 QStringList inputs;
2953 CardUtil::GetDeviceInputNames(device, type, inputs);
2954 while (!inputs.isEmpty())
2955 {
2956 addSelection(inputs.front());
2957 inputs.pop_front();
2958 }
2959 };
2960};
2961
2963{
2964 public:
2966 {
2967 setLabel(QObject::tr("Delivery system"));
2968 setHelpText(QObject::tr(
2969 "This shows the delivery system (modulation), for instance DVB-T2, "
2970 "that you have selected when you configured the capture card. "
2971 "This must be the same as the modulation used by the video source. "));
2972 };
2973};
2974
2976{
2978
2979 public:
2980 explicit InputDisplayName(const CardInput &parent) :
2981 MythUITextEditSetting(new CardInputDBStorage(this, parent, "displayname")), m_parent(parent)
2982 {
2983 setLabel(QObject::tr("Display name"));
2984 setHelpText(QObject::tr(
2985 "This name is displayed on screen when Live TV begins "
2986 "and in various other places. Make sure the last two "
2987 "characters are unique for each input or use a "
2988 "slash ('/') to designate the unique portion."));
2989 };
2990
2992 {
2993 delete GetStorage();
2994 }
2995 void Load(void) override {
2997 if (getValue().isEmpty())
2998 setValue(tr("Input %1").arg(m_parent.getInputID()));
2999 }
3000 private:
3002};
3003
3005{
3006 public:
3007 CardInputComboBoxSetting(const CardInput &parent, const QString &setting) :
3008 MythUIComboBoxSetting(new CardInputDBStorage(this, parent, setting))
3009 {
3010 }
3011
3013 {
3014 delete GetStorage();
3015 }
3016};
3017
3019{
3020 public:
3021 explicit SourceID(const CardInput &parent) :
3022 CardInputComboBoxSetting(parent, "sourceid")
3023 {
3024 setLabel(QObject::tr("Video source"));
3025 addSelection(QObject::tr("(None)"), "0");
3026 };
3027
3028 void Load(void) override // StandardSetting
3029 {
3032 };
3033
3035 clearSelections();
3036 addSelection(QObject::tr("(None)"), "0");
3038 };
3039};
3040
3042{
3043 public:
3044 InputGroup(const CardInput &parent, uint group_num) :
3045 m_cardInput(parent),
3046 m_groupNum(group_num)
3047 {
3048 setLabel(QObject::tr("Input group") +
3049 QString(" %1").arg(m_groupNum + 1));
3050 setHelpText(QObject::tr(
3051 "Leave as 'Generic' unless this input is shared with "
3052 "another device. Only one of the inputs in an input "
3053 "group will be allowed to record at any given time."));
3054 }
3055
3056 void Load(void) override; // StandardSetting
3057
3058 void Save(void) override // StandardSetting
3059 {
3060 uint inputid = m_cardInput.getInputID();
3061 uint new_groupid = getValue().toUInt();
3062
3063 if (m_groupId && (m_groupId != new_groupid))
3064 CardUtil::UnlinkInputGroup(inputid, m_groupId);
3065
3066 if (new_groupid)
3067 CardUtil::LinkInputGroup(inputid, new_groupid);
3068 }
3069
3070 virtual void Save(const QString& /*destination*/) { Save(); }
3071
3072 private:
3075 uint m_groupId {0};
3076};
3077
3079{
3080#if 0
3081 LOG(VB_GENERAL, LOG_DEBUG, QString("InputGroup::Load() %1 %2")
3082 .arg(m_groupNum).arg(m_cardInput.getInputID()));
3083#endif
3084
3085 uint inputid = m_cardInput.getInputID();
3086 QMap<uint, uint> grpcnt;
3087 std::vector<QString> names;
3088 std::vector<uint> grpid;
3089 std::vector<uint> selected_groupids;
3090
3091 names.push_back(QObject::tr("Generic"));
3092 grpid.push_back(0);
3093 grpcnt[0]++;
3094
3096 query.prepare(
3097 "SELECT cardinputid, inputgroupid, inputgroupname "
3098 "FROM inputgroup "
3099 "WHERE inputgroupname LIKE 'user:%' "
3100 "ORDER BY inputgroupid, cardinputid, inputgroupname");
3101
3102 if (!query.exec())
3103 {
3104 MythDB::DBError("InputGroup::Load()", query);
3105 }
3106 else
3107 {
3108 while (query.next())
3109 {
3110 uint groupid = query.value(1).toUInt();
3111 if ((inputid != 0U) && (query.value(0).toUInt() == inputid))
3112 selected_groupids.push_back(groupid);
3113
3114 grpcnt[groupid]++;
3115
3116 if (grpcnt[groupid] == 1)
3117 {
3118 names.push_back(query.value(2).toString().mid(5, -1));
3119 grpid.push_back(groupid);
3120 }
3121 }
3122 }
3123
3124 // makes sure we select something
3125 m_groupId = 0;
3126 if (m_groupNum < selected_groupids.size())
3127 m_groupId = selected_groupids[m_groupNum];
3128
3129#if 0
3130 LOG(VB_GENERAL, LOG_DEBUG, QString("Group num: %1 id: %2")
3131 .arg(m_groupNum).arg(m_groupId));
3132 {
3133 QString msg;
3134 for (uint i = 0; i < selected_groupids.size(); i++)
3135 msg += QString("%1 ").arg(selected_groupids[i]);
3136 LOG(VB_GENERAL, LOG_DEBUG, msg);
3137 }
3138#endif
3139
3140 // add selections to combobox
3141 clearSelections();
3142 uint index = 0;
3143 for (size_t i = 0; i < names.size(); i++)
3144 {
3145 bool sel = (m_groupId == grpid[i]);
3146 index = (sel) ? i : index;
3147
3148#if 0
3149 LOG(VB_GENERAL, LOG_DEBUG, QString("grpid %1, name '%2', i %3, s %4")
3150 .arg(grpid[i]).arg(names[i]) .arg(index).arg(sel ? "T" : "F"));
3151#endif
3152
3153 addSelection(names[i], QString::number(grpid[i]), sel);
3154 }
3155
3156#if 0
3157 LOG(VB_GENERAL, LOG_DEBUG, QString("Group index: %1").arg(index));
3158#endif
3159
3160 if (!names.empty())
3161 setValue(index);
3162
3164}
3165
3167{
3168 public:
3169 explicit QuickTune(const CardInput &parent) :
3170 CardInputComboBoxSetting(parent, "quicktune")
3171 {
3172 setLabel(QObject::tr("Use quick tuning"));
3173 addSelection(QObject::tr("Never"), "0", true);
3174 addSelection(QObject::tr("Live TV only"), "1", false);
3175 addSelection(QObject::tr("Always"), "2", false);
3176 setHelpText(QObject::tr(
3177 "If enabled, MythTV will tune using only the "
3178 "MPEG program number. The program numbers "
3179 "change more often than DVB or ATSC tuning "
3180 "parameters, so this is slightly less reliable. "
3181 "This will also inhibit EIT gathering during "
3182 "Live TV and recording."));
3183 };
3184};
3185
3187{
3188 public:
3189 explicit ExternalChannelCommand(const CardInput &parent) :
3190 MythUITextEditSetting(new CardInputDBStorage(this, parent, "externalcommand"))
3191 {
3192 setLabel(QObject::tr("External channel change command"));
3193 setValue("");
3194 setHelpText(QObject::tr("If specified, this command will be run to "
3195 "change the channel for inputs which have an external "
3196 "tuner device such as a cable box. The first argument "
3197 "will be the channel number."));
3198 };
3199
3201 {
3202 delete GetStorage();
3203 }
3204};
3205
3207{
3208 public:
3209 explicit PresetTuner(const CardInput &parent) :
3210 MythUITextEditSetting(new CardInputDBStorage(this, parent, "tunechan"))
3211 {
3212 setLabel(QObject::tr("Preset tuner to channel"));
3213 setValue("");
3214 setHelpText(QObject::tr("Leave this blank unless you have an external "
3215 "tuner that is connected to the tuner input of your card. "
3216 "If so, you will need to specify the preset channel for "
3217 "the signal (normally 3 or 4)."));
3218 };
3219
3220 ~PresetTuner() override
3221 {
3222 delete GetStorage();
3223 }
3224};
3225
3226void StartingChannel::SetSourceID(const QString &sourceid)
3227{
3228 clearSelections();
3229 if (sourceid.isEmpty() || !sourceid.toUInt())
3230 return;
3231
3232 // Get the existing starting channel
3233 auto *storage = dynamic_cast<CardInputDBStorage*>(GetStorage());
3234 if (storage == nullptr)
3235 return;
3236 int inputId = storage->getInputID();
3237 QString startChan = CardUtil::GetStartChannel(inputId);
3238
3239 ChannelInfoList channels = ChannelUtil::GetAllChannels(sourceid.toUInt());
3240
3241 if (channels.empty())
3242 {
3243 addSelection(tr("Please add channels to this source"),
3244 startChan.isEmpty() ? "0" : startChan);
3245 return;
3246 }
3247
3248 // If there are channels sort them, then add theme
3249 // (selecting the old start channel if it is there).
3250 QString order = gCoreContext->GetSetting("ChannelOrdering", "channum");
3251 ChannelUtil::SortChannels(channels, order);
3252 bool has_visible = false;
3253 for (size_t i = 0; i < channels.size() && !has_visible; i++)
3254 has_visible |= channels[i].m_visible;
3255
3256 for (auto & channel : channels)
3257 {
3258 const QString channum = channel.m_chanNum;
3259 bool sel = channum == startChan;
3260 if (!has_visible || channel.m_visible || sel)
3261 {
3262 addSelection(channum, channum, sel);
3263 }
3264 }
3265}
3266
3268{
3269 public:
3270 explicit InputPriority(const CardInput &parent) :
3271 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "recpriority"),
3272 -99, 99, 1)
3273 {
3274 setLabel(QObject::tr("Input priority"));
3275 setValue(0);
3276 setHelpText(QObject::tr("If the input priority is not equal for "
3277 "all inputs, the scheduler may choose to record a show "
3278 "at a later time so that it can record on an input with "
3279 "a higher value."));
3280 };
3281
3283 {
3284 delete GetStorage();
3285 }
3286};
3287
3289{
3290 public:
3291 ScheduleOrder(const CardInput &parent, int _value) :
3292 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "schedorder"),
3293 0, 99, 1)
3294 {
3295 setLabel(QObject::tr("Schedule order"));
3296 setValue(_value);
3297 setHelpText(QObject::tr("If priorities and other factors are equal "
3298 "the scheduler will choose the available "
3299 "input with the lowest, non-zero value. "
3300 "Setting this value to zero will make the "
3301 "input unavailable to the scheduler."));
3302 };
3303
3305 {
3306 delete GetStorage();
3307 }
3308};
3309
3311{
3312 public:
3313 LiveTVOrder(const CardInput &parent, int _value) :
3314 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "livetvorder"),
3315 0, 99, 1)
3316 {
3317 setLabel(QObject::tr("Live TV order"));
3318 setValue(_value);
3319 setHelpText(QObject::tr("When entering Live TV, the available, local "
3320 "input with the lowest, non-zero value will "
3321 "be used. If no local inputs are available, "
3322 "the available, remote input with the lowest, "
3323 "non-zero value will be used. "
3324 "Setting this value to zero will make the "
3325 "input unavailable to live TV."));
3326 };
3327
3328 ~LiveTVOrder() override
3329 {
3330 delete GetStorage();
3331 }
3332};
3333
3335{
3336 public:
3337 explicit DishNetEIT(const CardInput &parent) :
3339 "dishnet_eit"))
3340 {
3341 setLabel(QObject::tr("Use DishNet long-term EIT data"));
3342 setValue(false);
3344 QObject::tr(
3345 "If you point your satellite dish toward DishNet's birds, "
3346 "you may wish to enable this feature. For best results, "
3347 "enable general EIT collection as well."));
3348 };
3349
3350 ~DishNetEIT() override
3351 {
3352 delete GetStorage();
3353 }
3354};
3355
3356CardInput::CardInput(const QString & cardtype, const QString & device,
3357 int _cardid) :
3358 m_id(new ID()),
3359 m_inputName(new InputName(*this)),
3360 m_sourceId(new SourceID(*this)),
3361 m_startChan(new StartingChannel(*this)),
3362 m_scan(new ButtonStandardSetting(tr("Scan for channels"))),
3363 m_srcFetch(new ButtonStandardSetting(tr("Fetch channels from listings source"))),
3364 m_externalInputSettings(new DiSEqCDevSettings()),
3365 m_inputGrp0(new InputGroup(*this, 0)),
3366 m_inputGrp1(new InputGroup(*this, 1))
3367{
3368 addChild(m_id);
3369
3371 {
3373 _cardid, true));
3374 }
3375
3376 // Delivery system for DVB, input name for other,
3377 // same field capturecard/inputname for both
3378 if ("DVB" == cardtype)
3379 {
3380 auto *ds = new DeliverySystem();
3381 ds->setValue(CardUtil::GetDeliverySystemFromDB(_cardid));
3382 addChild(ds);
3383 }
3384 else if (CardUtil::IsV4L(cardtype))
3385 {
3387 }
3388 addChild(new InputDisplayName(*this));
3390
3391 if (CardUtil::IsEncoder(cardtype) || CardUtil::IsUnscanable(cardtype))
3392 {
3393 addChild(new ExternalChannelCommand(*this));
3394 if (CardUtil::HasTuner(cardtype, device))
3395 addChild(new PresetTuner(*this));
3396 }
3397 else
3398 {
3399 addChild(new QuickTune(*this));
3400 if ("DVB" == cardtype)
3401 addChild(new DishNetEIT(*this));
3402 }
3403
3405 tr("Use channel scanner to find channels for this input."));
3406
3408 tr("This uses the listings data source to "
3409 "provide the channels for this input.") + " " +
3410 tr("This can take a long time to run."));
3411
3414
3416
3417 auto *interact = new GroupSetting();
3418
3419 interact->setLabel(QObject::tr("Interactions between inputs"));
3420 if (CardUtil::IsTunerSharingCapable(cardtype))
3421 {
3422 m_instanceCount = new InstanceCount(*this);
3423 interact->addChild(m_instanceCount);
3424 m_schedGroup = new SchedGroup(*this);
3425 interact->addChild(m_schedGroup);
3426 }
3427 interact->addChild(new InputPriority(*this));
3428 interact->addChild(new ScheduleOrder(*this, _cardid));
3429 interact->addChild(new LiveTVOrder(*this, _cardid));
3430
3431 auto *ingrpbtn =
3432 new ButtonStandardSetting(QObject::tr("Create a New Input Group"));
3433 ingrpbtn->setHelpText(
3434 QObject::tr("Input groups are only needed when two or more cards "
3435 "share the same resource such as a FireWire card and "
3436 "an analog card input controlling the same set top box."));
3437 interact->addChild(ingrpbtn);
3438 interact->addChild(m_inputGrp0);
3439 interact->addChild(m_inputGrp1);
3440
3441 addChild(interact);
3442
3443 setObjectName("CardInput");
3444 SetSourceID("-1");
3445
3448 connect(m_sourceId, qOverload<const QString&>(&StandardSetting::valueChanged),
3450 connect(m_sourceId, qOverload<const QString&>(&StandardSetting::valueChanged),
3451 this, &CardInput::SetSourceID);
3452 connect(ingrpbtn, &ButtonStandardSetting::clicked,
3454}
3455
3457{
3459 {
3461 m_externalInputSettings = nullptr;
3462 }
3463}
3464
3465void CardInput::SetSourceID(const QString &sourceid)
3466{
3467 uint cid = m_id->getValue().toUInt();
3468 QString raw_card_type = CardUtil::GetRawInputType(cid);
3469 bool enable = (sourceid.toInt() > 0);
3470 m_scan->setEnabled(enable && !raw_card_type.isEmpty() &&
3471 !CardUtil::IsUnscanable(raw_card_type));
3472 m_srcFetch->setEnabled(enable);
3473}
3474
3475QString CardInput::getSourceName(void) const
3476{
3477 return m_sourceId->getValueLabel();
3478}
3479
3481{
3482 m_inputGrp0->Save();
3483 m_inputGrp1->Save();
3484
3485 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
3486 auto *settingdialog =
3487 new MythTextInputDialog(popupStack, tr("Enter new group name"));
3488
3489 if (settingdialog->Create())
3490 {
3491 connect(settingdialog, &MythTextInputDialog::haveResult,
3493 popupStack->AddScreen(settingdialog);
3494 }
3495 else
3496 {
3497 delete settingdialog;
3498 }
3499}
3500
3501void CardInput::CreateNewInputGroupSlot(const QString& name)
3502{
3503 if (name.isEmpty())
3504 {
3505 ShowOkPopup(tr("Sorry, this Input Group name cannot be blank."));
3506 return;
3507 }
3508
3509 QString new_name = QString("user:") + name;
3510
3512 query.prepare("SELECT inputgroupname "
3513 "FROM inputgroup "
3514 "WHERE inputgroupname = :GROUPNAME");
3515 query.bindValue(":GROUPNAME", new_name);
3516
3517 if (!query.exec())
3518 {
3519 MythDB::DBError("CreateNewInputGroup 1", query);
3520 return;
3521 }
3522
3523 if (query.next())
3524 {
3525 ShowOkPopup(tr("Sorry, this Input Group name is already in use."));
3526 return;
3527 }
3528
3529 uint inputgroupid = CardUtil::CreateInputGroup(new_name);
3530
3531 m_inputGrp0->Load();
3532 m_inputGrp1->Load();
3533
3534 if (m_inputGrp0->getValue().toUInt() == 0U)
3535 {
3537 m_inputGrp0->getValueIndex(QString::number(inputgroupid)));
3538 }
3539 else
3540 {
3542 m_inputGrp1->getValueIndex(QString::number(inputgroupid)));
3543 }
3544}
3545
3547{
3548 uint srcid = m_sourceId->getValue().toUInt();
3549 uint crdid = m_id->getValue().toUInt();
3550 QString in = m_inputName->getValue();
3551
3552#if CONFIG_BACKEND
3553 uint num_channels_before = SourceUtil::GetChannelCount(srcid);
3554
3555 Save(); // save info for scanner.
3556
3557 QString cardtype = CardUtil::GetRawInputType(crdid);
3558 if (CardUtil::IsUnscanable(cardtype))
3559 {
3560 LOG(VB_GENERAL, LOG_ERR,
3561 QString("Sorry, %1 cards do not yet support scanning.")
3562 .arg(cardtype));
3563 return;
3564 }
3565
3567 auto *ssd = new StandardSettingDialog(mainStack, "generalsettings",
3568 new ScanWizard(srcid, crdid, in));
3569
3570 if (ssd->Create())
3571 {
3572 connect(ssd, &StandardSettingDialog::Exiting, this,
3573 [srcid, this, num_channels_before]()
3574 {
3575 if (SourceUtil::GetChannelCount(srcid))
3576 m_startChan->SetSourceID(QString::number(srcid));
3577 if (num_channels_before)
3578 {
3579 m_startChan->Load();
3580 m_startChan->Save();
3581 }
3582 });
3583 mainStack->AddScreen(ssd);
3584 }
3585 else
3586 {
3587 delete ssd;
3588 }
3589
3590#else
3591 LOG(VB_GENERAL, LOG_ERR, "You must compile the backend "
3592 "to be able to scan for channels");
3593#endif
3594}
3595
3597{
3598 uint srcid = m_sourceId->getValue().toUInt();
3599 uint crdid = m_id->getValue().toUInt();
3600
3601 uint num_channels_before = SourceUtil::GetChannelCount(srcid);
3602
3603 if (crdid && srcid)
3604 {
3605 Save(); // save info for fetch..
3606
3607 QString cardtype = CardUtil::GetRawInputType(crdid);
3608
3609 if (!CardUtil::IsCableCardPresent(crdid, cardtype) &&
3610 !CardUtil::IsUnscanable(cardtype) &&
3611 !CardUtil::IsEncoder(cardtype) &&
3612 cardtype != "HDHOMERUN" &&
3613 !num_channels_before)
3614 {
3615 LOG(VB_GENERAL, LOG_ERR, "Skipping channel fetch, you need to "
3616 "scan for channels first.");
3617 return;
3618 }
3619
3621 }
3622
3623 if (SourceUtil::GetChannelCount(srcid))
3624 m_startChan->SetSourceID(QString::number(srcid));
3625 if (num_channels_before)
3626 {
3627 m_startChan->Load();
3628 m_startChan->Save();
3629 }
3630}
3631
3633{
3634 QString cardinputidTag(":WHERECARDID");
3635
3636 QString query("cardid = " + cardinputidTag);
3637
3638 bindings.insert(cardinputidTag, m_parent.getInputID());
3639
3640 return query;
3641}
3642
3644{
3645 QString cardinputidTag(":SETCARDID");
3646 QString colTag(":SET" + GetColumnName().toUpper());
3647
3648 QString query("cardid = " + cardinputidTag + ", " +
3649 GetColumnName() + " = " + colTag);
3650
3651 bindings.insert(cardinputidTag, m_parent.getInputID());
3652 bindings.insert(colTag, m_user->GetDBValue());
3653
3654 return query;
3655}
3656
3657void CardInput::loadByID(int inputid)
3658{
3659 m_id->setValue(inputid);
3660 m_externalInputSettings->Load(inputid);
3662}
3663
3664void CardInput::loadByInput(int _cardid, const QString& _inputname)
3665{
3667 query.prepare("SELECT cardid FROM capturecard "
3668 "WHERE cardid = :CARDID AND inputname = :INPUTNAME");
3669 query.bindValue(":CARDID", _cardid);
3670 query.bindValue(":INPUTNAME", _inputname);
3671
3672 if (query.exec() && query.isActive() && query.next())
3673 {
3674 loadByID(query.value(0).toInt());
3675 }
3676}
3677
3679{
3680 uint cardid = m_id->getValue().toUInt();
3683
3684 uint icount = 1;
3685 if (m_instanceCount)
3686 icount = m_instanceCount->getValue().toUInt();
3687
3688 CardUtil::InputSetMaxRecordings(cardid, icount);
3689}
3690
3692{
3693 return m_parent.getInputID();
3694}
3695
3697{
3698 return m_parent.getCardID();
3699}
3700
3702{
3703 emit Clicked(m_value);
3704}
3705
3706void CaptureCardEditor::AddSelection(const QString &label, const CCESlot slot)
3707{
3708 auto *button = new ButtonStandardSetting(label);
3709 connect(button, &ButtonStandardSetting::clicked, this, slot);
3710 addChild(button);
3711}
3712
3713void CaptureCardEditor::AddSelection(const QString &label, const CCESlotConst slot)
3714{
3715 auto *button = new ButtonStandardSetting(label);
3716 connect(button, &ButtonStandardSetting::clicked, this, slot);
3717 addChild(button);
3718}
3719
3721{
3723 tr("Are you sure you want to delete "
3724 "ALL capture cards on %1?").arg(gCoreContext->GetHostName()),
3726 true);
3727}
3728
3730{
3732 tr("Are you sure you want to delete "
3733 "ALL capture cards?"),
3735 true);
3736}
3737
3739{
3740 auto *card = new CaptureCard();
3741 card->setLabel(tr("New capture card"));
3742 card->Load();
3743 addChild(card);
3744 emit settingsChanged(this);
3745}
3746
3748{
3749 if (!doDelete)
3750 return;
3751
3753 Load();
3754 emit settingsChanged(this);
3755}
3756
3758{
3759 if (!doDelete)
3760 return;
3761
3763
3764 cards.prepare(
3765 "SELECT cardid "
3766 "FROM capturecard "
3767 "WHERE hostname = :HOSTNAME");
3768 cards.bindValue(":HOSTNAME", gCoreContext->GetHostName());
3769
3770 if (!cards.exec() || !cards.isActive())
3771 {
3773 tr("Error getting list of cards for this host. "
3774 "Unable to delete capturecards for %1")
3775 .arg(gCoreContext->GetHostName()));
3776
3777 MythDB::DBError("Selecting cardids for deletion", cards);
3778 return;
3779 }
3780
3781 while (cards.next())
3782 CardUtil::DeleteInput(cards.value(0).toUInt());
3783
3784 Load();
3785 emit settingsChanged(this);
3786}
3787
3789{
3790 setLabel(tr("Capture cards"));
3791}
3792
3794{
3795 clearSettings();
3796 AddSelection(QObject::tr("(New capture card)"), &CaptureCardEditor::AddNewCard);
3797 AddSelection(QObject::tr("(Delete all capture cards on %1)")
3798 .arg(gCoreContext->GetHostName()),
3800 AddSelection(QObject::tr("(Delete all capture cards)"),
3803}
3804
3806{
3807 setLabel(tr("Video sources"));
3808}
3809
3811{
3812 clearSettings();
3813 AddSelection(QObject::tr("(New video source)"), &VideoSourceEditor::NewSource);
3814 AddSelection(QObject::tr("(Delete all video sources)"),
3818}
3819
3820void VideoSourceEditor::AddSelection(const QString &label, const VSESlot slot)
3821{
3822 auto *button = new ButtonStandardSetting(label);
3823 connect(button, &ButtonStandardSetting::clicked, this, slot);
3824 addChild(button);
3825}
3826
3827void VideoSourceEditor::AddSelection(const QString &label, const VSESlotConst slot)
3828{
3829 auto *button = new ButtonStandardSetting(label);
3830 connect(button, &ButtonStandardSetting::clicked, this, slot);
3831 addChild(button);
3832}
3833
3835{
3837 tr("Are you sure you want to delete "
3838 "ALL video sources?"),
3840 true);
3841}
3842
3844{
3845 if (!doDelete)
3846 return;
3847
3849 Load();
3850 emit settingsChanged(this);
3851}
3852
3854{
3855 auto *source = new VideoSource();
3856 source->setLabel(tr("New video source"));
3857 source->Load();
3858 addChild(source);
3859 emit settingsChanged(this);
3860}
3861
3863{
3864 setLabel(tr("Input connections"));
3865}
3866
3868{
3869 m_cardInputs.clear();
3870 clearSettings();
3871
3872 // We do this manually because we want custom labels. If
3873 // SelectSetting provided a facility to edit the labels, we
3874 // could use CaptureCard::fillSelections
3875
3877 query.prepare(
3878 "SELECT cardid, videodevice, cardtype, displayname "
3879 "FROM capturecard "
3880 "WHERE hostname = :HOSTNAME "
3881 " AND parentid = 0 "
3882 "ORDER BY cardid");
3883 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
3884
3885 if (!query.exec())
3886 {
3887 MythDB::DBError("CardInputEditor::load", query);
3888 return;
3889 }
3890
3891 while (query.next())
3892 {
3893 uint cardid = query.value(0).toUInt();
3894 QString videodevice = query.value(1).toString();
3895 QString cardtype = query.value(2).toString();
3896 QString displayname = query.value(3).toString();
3897
3898 auto *cardinput = new CardInput(cardtype, videodevice, cardid);
3899 cardinput->loadByID(cardid);
3900 QString inputlabel = QString("%1 (%2) -> %3")
3901 .arg(CardUtil::GetDeviceLabel(cardtype, videodevice),
3902 displayname, cardinput->getSourceName());
3903 m_cardInputs.push_back(cardinput);
3904 cardinput->setLabel(inputlabel);
3905 addChild(cardinput);
3906 }
3907
3909}
3910
3911#if CONFIG_DVB
3912static QString remove_chaff(const QString &name)
3913{
3914 // Trim off some of the chaff.
3915 QString short_name = name;
3916 if (short_name.startsWith("LG Electronics"))
3917 short_name = short_name.right(short_name.length() - 15);
3918 if (short_name.startsWith("Oren"))
3919 short_name = short_name.right(short_name.length() - 5);
3920 if (short_name.startsWith("Nextwave"))
3921 short_name = short_name.right(short_name.length() - 9);
3922 if (short_name.startsWith("frontend", Qt::CaseInsensitive))
3923 short_name = short_name.left(short_name.length() - 9);
3924 if (short_name.endsWith("VSB/QAM"))
3925 short_name = short_name.left(short_name.length() - 8);
3926 if (short_name.endsWith("VSB"))
3927 short_name = short_name.left(short_name.length() - 4);
3928 if (short_name.endsWith("DVB-T"))
3929 short_name = short_name.left(short_name.length() - 6);
3930
3931 // It would be infinitely better if DVB allowed us to query
3932 // the vendor ID. But instead we have to guess based on the
3933 // demodulator name. This means cards like the Air2PC HD5000
3934 // and DViCO Fusion HDTV cards are not identified correctly.
3935 short_name = short_name.simplified();
3936 if (short_name.startsWith("or51211", Qt::CaseInsensitive))
3937 short_name = "pcHDTV HD-2000";
3938 else if (short_name.startsWith("or51132", Qt::CaseInsensitive))
3939 short_name = "pcHDTV HD-3000";
3940 else if (short_name.startsWith("bcm3510", Qt::CaseInsensitive))
3941 short_name = "Air2PC v1";
3942 else if (short_name.startsWith("nxt2002", Qt::CaseInsensitive) ||
3943 short_name.startsWith("nxt200x", Qt::CaseInsensitive))
3944 short_name = "Air2PC v2";
3945 else if (short_name.startsWith("lgdt3302", Qt::CaseInsensitive))
3946 short_name = "DViCO HDTV3";
3947 else if (short_name.startsWith("lgdt3303", Qt::CaseInsensitive))
3948 short_name = "DViCO v2 or Air2PC v3 or pcHDTV HD-5500";
3949
3950 return short_name;
3951}
3952#endif // CONFIG_DVB
3953
3954void DVBConfigurationGroup::reloadDiseqcTree(const QString &videodevice)
3955{
3956 if (m_diseqcTree)
3957 m_diseqcTree->Load(videodevice);
3958
3959 if (m_cardType->getValue() == "DVB-S" ||
3960 m_cardType->getValue() == "DVB-S2" )
3961 {
3962 m_diseqcBtn->setVisible(true);
3963 }
3964 else
3965 {
3966 m_diseqcBtn->setVisible(false);
3967 }
3968 emit getParent()->settingsChanged(this);
3969}
3970
3971void DVBConfigurationGroup::probeCard(const QString &videodevice)
3972{
3973 if (videodevice.isEmpty())
3974 {
3975 m_cardName->setValue("");
3976 m_cardType->setValue("");
3977 return;
3978 }
3979
3980 if ((m_parent.getCardID() != 0) && m_parent.GetRawCardType() != "DVB")
3981 {
3982 m_cardName->setValue("");
3983 m_cardType->setValue("");
3984 return;
3985 }
3986
3987#if CONFIG_DVB
3988 QString frontend_name = CardUtil::ProbeDVBFrontendName(videodevice);
3989 QString subtype = CardUtil::ProbeDVBType(videodevice);
3990
3991 QString err_open = tr("Could not open card %1").arg(videodevice);
3992 QString err_other = tr("Could not get card info for card %1").arg(videodevice);
3993
3994 switch (CardUtil::toInputType(subtype))
3995 {
3997 m_cardName->setValue(err_open);
3998 m_cardType->setValue(strerror(errno));
3999 break;
4001 m_cardName->setValue(err_other);
4002 m_cardType->setValue("Unknown error");
4003 break;
4005 m_cardName->setValue(err_other);
4006 m_cardType->setValue(strerror(errno));
4007 break;
4009 m_cardType->setValue("DVB-S");
4010 m_cardName->setValue(frontend_name);
4013 break;
4015 m_cardType->setValue("DVB-S2");
4016 m_cardName->setValue(frontend_name);
4019 break;
4021 m_cardType->setValue("DVB-C");
4022 m_cardName->setValue(frontend_name);
4025 break;
4027 m_cardType->setValue("DVB-T2");
4028 m_cardName->setValue(frontend_name);
4031 break;
4033 {
4034 m_cardType->setValue("DVB-T");
4035 m_cardName->setValue(frontend_name);
4038 if (frontend_name.toLower().indexOf("usb") >= 0)
4039 {
4042 }
4043
4044 // slow down tuning for buggy drivers
4045 if ((frontend_name == "DiBcom 3000P/M-C DVB-T") ||
4046 (frontend_name ==
4047 "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"))
4048 {
4049 m_tuningDelay->setValueMs(200ms);
4050 }
4051
4052#if 0 // frontends on hybrid DVB-T/Analog cards
4053 QString short_name = remove_chaff(frontend_name);
4054 m_buttonAnalog->setVisible(
4055 short_name.startsWith("zarlink zl10353",
4056 Qt::CaseInsensitive) ||
4057 short_name.startsWith("wintv hvr 900 m/r: 65008/a1c0",
4058 Qt::CaseInsensitive) ||
4059 short_name.startsWith("philips tda10046h",
4060 Qt::CaseInsensitive));
4061#endif
4062 }
4063 break;
4065 {
4066 QString short_name = remove_chaff(frontend_name);
4067 m_cardType->setValue("ATSC");
4068 m_cardName->setValue(short_name);
4071
4072 // According to #1779 and #1935 the AverMedia 180 needs
4073 // a 3000 ms signal timeout, at least for QAM tuning.
4074 if (frontend_name == "Nextwave NXT200X VSB/QAM frontend")
4075 {
4078 }
4079
4080#if 0 // frontends on hybrid DVB-T/Analog cards
4081 if (frontend_name.toLower().indexOf("usb") < 0)
4082 {
4083 m_buttonAnalog->setVisible(
4084 short_name.startsWith("pchdtv", Qt::CaseInsensitive) ||
4085 short_name.startsWith("dvico", Qt::CaseInsensitive) ||
4086 short_name.startsWith("nextwave", Qt::CaseInsensitive));
4087 }
4088#endif
4089 }
4090 break;
4091 default:
4092 break;
4093 }
4094
4095 // Create selection list of all delivery systems of this card
4096 {
4098 QStringList delsyslist = CardUtil::ProbeDeliverySystems(videodevice);
4099 for (const auto & item : std::as_const(delsyslist))
4100 {
4101 LOG(VB_GENERAL, LOG_DEBUG, QString("DVBCardType: add deliverysystem:%1")
4102 .arg(item));
4103
4104 m_cardType->addSelection(item, item);
4105 }
4106
4107 // Default value, used if not already defined in capturecard/inputname
4108 QString delsys = CardUtil::ProbeDefaultDeliverySystem(videodevice);
4109 if (!delsys.isEmpty())
4110 {
4111 m_cardType->setValue(delsys);
4112 }
4113 }
4114#
4115#else
4116 m_cardType->setValue(QString("Recompile with DVB-Support!"));
4117#endif
4118}
4119
4121 QString dev, QString type) :
4122 CaptureCardComboBoxSetting(parent, false, "audiodevice"),
4123 m_lastDevice(std::move(dev)), m_lastCardType(std::move(type))
4124{
4125 setLabel(QObject::tr("Audio input"));
4126 setHelpText(QObject::tr("If there is more than one audio input, "
4127 "select which one to use."));
4128 int cardid = parent.getCardID();
4129 if (cardid <= 0)
4130 return;
4131
4134}
4135
4136int TunerCardAudioInput::fillSelections(const QString &device)
4137{
4139
4140 if (device.isEmpty())
4141 return 0;
4142
4143 m_lastDevice = device;
4144 QStringList inputs =
4146
4147 for (uint i = 0; i < (uint)inputs.size(); i++)
4148 {
4149 addSelection(inputs[i], QString::number(i),
4150 m_lastDevice == QString::number(i));
4151 }
4152 return inputs.size();
4153}
4154
4156 CardType& cardType) :
4157 m_parent(a_parent),
4158 m_cardNum(new DVBCardNum(a_parent)),
4159 m_cardName(new DVBCardName()),
4160 m_cardType(new DVBCardType(a_parent)),
4161 m_signalTimeout(new SignalTimeout(a_parent, 0.5s, 0.25s)),
4162 m_tuningDelay(new DVBTuningDelay(a_parent)),
4163 m_diseqcTree(new DiSEqCDevTree()),
4164 m_diseqcBtn(new DeviceTree(*m_diseqcTree))
4165{
4166 setVisible(false);
4167
4168 m_channelTimeout = new ChannelTimeout(m_parent, 3s, 1.75s);
4169
4170 cardType.addTargetedChild("DVB", m_cardNum);
4171
4172 cardType.addTargetedChild("DVB", m_cardName);
4173 cardType.addTargetedChild("DVB", m_cardType);
4174
4175 cardType.addTargetedChild("DVB", m_signalTimeout);
4176 cardType.addTargetedChild("DVB", m_channelTimeout);
4177
4178 cardType.addTargetedChild("DVB", new EmptyAudioDevice(m_parent));
4179 cardType.addTargetedChild("DVB", new EmptyVBIDevice(m_parent));
4180
4181 cardType.addTargetedChild("DVB", new DVBNoSeqStart(m_parent));
4182 cardType.addTargetedChild("DVB", new DVBOnDemand(m_parent));
4183 cardType.addTargetedChild("DVB", new DVBEITScan(m_parent));
4184
4185 m_diseqcBtn->setLabel(tr("DiSEqC (Switch, LNB and Rotor Configuration)"));
4186 m_diseqcBtn->setHelpText(tr("Input and satellite settings."));
4187
4188 cardType.addTargetedChild("DVB", m_tuningDelay);
4189 cardType.addTargetedChild("DVB", m_diseqcBtn);
4190 m_tuningDelay->setVisible(false);
4191
4192 connect(m_cardNum, qOverload<const QString&>(&StandardSetting::valueChanged),
4194 connect(m_cardNum, qOverload<const QString&>(&StandardSetting::valueChanged),
4196}
4197
4199{
4200 if (m_diseqcTree)
4201 {
4202 delete m_diseqcTree;
4203 m_diseqcTree = nullptr;
4204 }
4205}
4206
4208{
4210 m_diseqcBtn->Load();
4212 if (m_cardType->getValue() == "DVB-S" ||
4213 m_cardType->getValue() == "DVB-S2" ||
4215 {
4216 m_diseqcBtn->setVisible(true);
4217 }
4218}
4219
4221{
4225}
4226
4227// -----------------------
4228// SAT>IP configuration
4229// -----------------------
4230#if CONFIG_SATIP
4231
4232class DiSEqCPosition : public MythUISpinBoxSetting
4233{
4234 public:
4235 explicit DiSEqCPosition(const CaptureCard &parent, int value, int min_val) :
4236 MythUISpinBoxSetting(new CaptureCardDBStorage(this, parent, "dvb_diseqc_type"),
4237 min_val, 0xff, 1)
4238 {
4239 setLabel(QObject::tr("DiSEqC position"));
4240 setHelpText(QObject::tr("Position of the LNB on the DiSEqC switch. "
4241 "Leave at 1 if there is no DiSEqC switch "
4242 "and the LNB is directly connected to the SatIP server. "
4243 "This value is used as signal source (attribute src) in "
4244 "the SatIP tune command."));
4245 setValue(value);
4246 };
4247
4248 ~DiSEqCPosition() override
4249 {
4250 delete GetStorage();
4251 }
4252};
4253
4254SatIPConfigurationGroup::SatIPConfigurationGroup
4255 (CaptureCard& a_parent, CardType &a_cardtype) :
4256 m_parent(a_parent),
4257 m_deviceId(new SatIPDeviceID(a_parent))
4258{
4259 setVisible(false);
4260
4261 FillDeviceList();
4262
4263 m_friendlyName = new SatIPDeviceAttribute(tr("Friendly name"), tr("Friendly name of the Sat>IP server"));
4264 m_tunerType = new SatIPDeviceAttribute(tr("Tuner type"), tr("Type of the selected tuner"));
4265 m_tunerIndex = new SatIPDeviceAttribute(tr("Tuner index"), tr("Index of the tuner on the Sat>IP server"));
4266
4267 m_deviceIdList = new SatIPDeviceIDList(
4268 m_deviceId, m_friendlyName, m_tunerType, m_tunerIndex, &m_deviceList, m_parent);
4269
4270 a_cardtype.addTargetedChild("SATIP", m_deviceIdList);
4271 a_cardtype.addTargetedChild("SATIP", m_friendlyName);
4272 a_cardtype.addTargetedChild("SATIP", m_tunerType);
4273 a_cardtype.addTargetedChild("SATIP", m_tunerIndex);
4274 a_cardtype.addTargetedChild("SATIP", m_deviceId);
4275 a_cardtype.addTargetedChild("SATIP", new SignalTimeout(m_parent, 7s, 1s));
4276 a_cardtype.addTargetedChild("SATIP", new ChannelTimeout(m_parent, 10s, 2s));
4277 a_cardtype.addTargetedChild("SATIP", new DVBEITScan(m_parent));
4278 a_cardtype.addTargetedChild("SATIP", new DiSEqCPosition(m_parent, 1, 1));
4279
4280 connect(m_deviceIdList, &SatIPDeviceIDList::NewTuner,
4281 m_deviceId, &SatIPDeviceID::SetTuner);
4282};
4283
4284void SatIPConfigurationGroup::FillDeviceList(void)
4285{
4286 m_deviceList.clear();
4287
4288 // Find devices on the network
4289 // Returns each devices as "deviceid friendlyname ip tunerno tunertype"
4290 QStringList devs = CardUtil::ProbeVideoDevices("SATIP");
4291
4292 for (const auto & dev : std::as_const(devs))
4293 {
4294 QStringList devparts = dev.split(" ");
4295 const QString& id = devparts.value(0);
4296 const QString& name = devparts.value(1);
4297 const QString& ip = devparts.value(2);
4298 const QString& tunerno = devparts.value(3);
4299 const QString& tunertype = devparts.value(4);
4300
4301 SatIPDevice device;
4302 device.m_deviceId = id;
4303 device.m_cardIP = ip;
4304 device.m_inUse = false;
4305 device.m_friendlyName = name;
4306 device.m_tunerNo = tunerno;
4307 device.m_tunerType = tunertype;
4308 device.m_mythDeviceId = QString("%1:%2:%3").arg(id, tunertype, tunerno);
4309
4310 QString friendlyIdentifier = QString("%1, %2, Tuner #%3").arg(name, tunertype, tunerno);
4311
4312 m_deviceList[device.m_mythDeviceId] = device;
4313
4314 LOG(VB_CHANNEL, LOG_DEBUG, QString("SatIP: Add %1 '%2' '%3'")
4315 .arg(device.m_mythDeviceId, device.m_friendlyName, friendlyIdentifier));
4316 }
4317
4318 // Now find configured devices
4319 // Returns each devices as "deviceid friendlyname ip tunerno tunertype"
4320 QStringList db = CardUtil::GetVideoDevices("SATIP");
4321 for (const auto& dev : std::as_const(db))
4322 {
4323 auto dit = m_deviceList.find(dev);
4324 if (dit != m_deviceList.end())
4325 {
4326 (*dit).m_inUse = true;
4327 }
4328 }
4329};
4330
4331SatIPDeviceIDList::SatIPDeviceIDList(
4332 SatIPDeviceID *deviceId,
4333 SatIPDeviceAttribute *friendlyName,
4334 SatIPDeviceAttribute *tunerType,
4335 SatIPDeviceAttribute *tunerIndex,
4336 SatIPDeviceList *deviceList,
4337 const CaptureCard &parent) :
4338 m_deviceId(deviceId),
4339 m_friendlyName(friendlyName),
4340 m_tunerType(tunerType),
4341 m_tunerIndex(tunerIndex),
4342 m_deviceList(deviceList),
4343 m_parent(parent)
4344{
4345 setLabel(tr("Available devices"));
4346 setHelpText(tr("Device IP or ID, tuner number and tuner type of available Sat>IP device"));
4347
4348 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
4349 this, &SatIPDeviceIDList::UpdateDevices);
4350};
4351
4352void SatIPDeviceIDList::Load(void)
4353{
4354 clearSelections();
4355
4356 int cardid = m_parent.getCardID();
4357 QString device = CardUtil::GetVideoDevice(cardid);
4358
4359 fillSelections(device);
4360};
4361
4362void SatIPDeviceIDList::UpdateDevices(const QString &v)
4363{
4364 SatIPDevice dev = (*m_deviceList)[v];
4365 m_deviceId->setValue(dev.m_mythDeviceId);
4366 m_friendlyName->setValue(dev.m_friendlyName);
4367 m_tunerType->setValue(dev.m_tunerType);
4368 m_tunerIndex->setValue(dev.m_tunerNo);
4369};
4370
4371void SatIPDeviceIDList::fillSelections(const QString &cur)
4372{
4373 clearSelections();
4374
4375 std::vector<QString> names;
4376 std::vector<QString> devs;
4377 QMap<QString, bool> in_use;
4378
4379 const QString& current = cur;
4380 QString sel;
4381
4382 SatIPDeviceList::iterator it = m_deviceList->begin();
4383 for(; it != m_deviceList->end(); ++it)
4384 {
4385 QString friendlyIdentifier = QString("%1, %2, Tuner #%3")
4386 .arg((*it).m_friendlyName, (*it).m_tunerType, (*it).m_tunerNo);
4387 names.push_back(friendlyIdentifier);
4388
4389 devs.push_back(it.key());
4390 in_use[it.key()] = (*it).m_inUse;
4391 }
4392
4393 for (const auto& it2s : devs)
4394 {
4395 sel = (current == it2s) ? it2s : sel;
4396 }
4397
4398 QString usestr = QString(" -- ");
4399 usestr += tr("Warning: already in use");
4400
4401 for (uint i = 0; i < devs.size(); ++i)
4402 {
4403 const QString& dev = devs[i];
4404 const QString& name = names[i];
4405 bool dev_in_use = (dev == sel) ? false : in_use[devs[i]];
4406 QString desc = name + (dev_in_use ? usestr : "");
4407 addSelection(desc, dev, dev == sel);
4408 }
4409};
4410
4411SatIPDeviceID::SatIPDeviceID(const CaptureCard &parent) :
4412 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, "videodevice")),
4413 m_parent(parent)
4414{
4415 setLabel(tr("Device ID"));
4416 setHelpText(tr("Device ID of the Sat>IP tuner"));
4417 setEnabled(true);
4418 setReadOnly(true);
4419};
4420
4421SatIPDeviceID::~SatIPDeviceID()
4422{
4423 delete GetStorage();
4424}
4425
4426void SatIPDeviceID::Load(void)
4427{
4429};
4430
4431void SatIPDeviceID::SetTuner(const QString &tuner)
4432{
4433 setValue(tuner);
4434};
4435
4436SatIPDeviceAttribute::SatIPDeviceAttribute(const QString& label, const QString& helptext)
4437{
4438 setLabel(label);
4439 setHelpText(helptext);
4440};
4441#endif // CONFIG_SATIP
@ DVB_DEV_FRONTEND
Definition: cardutil.h:32
std::vector< ChannelInfo > ChannelInfoList
Definition: channelinfo.h:130
TransTextEditSetting * m_cardInfo
Definition: videosource.h:633
void probeCard(const QString &device)
ASIDevice * m_device
Definition: videosource.h:632
ASIConfigurationGroup(CaptureCard &parent, CardType &cardType)
CaptureCard & m_parent
Definition: videosource.h:631
ASIDevice(const CaptureCard &parent)
void Load(void) override
void fillSelections(const QString &current)
Adds all available cards to list If current is >= 0 it will be considered available even if no device...
QString m_product_name
Definition: avcinfo.h:52
AudioDevice(const CaptureCard &parent)
AudioRateLimit(const CaptureCard &parent)
~BouquetID() override
BouquetID(const VideoSource &parent, signed int value, signed int min_val)
void Clicked(const QString &choice)
void edit(MythScreenType *screen) override
QString GetSetClause(MSqlBindings &bindings) const override
const CaptureCard & m_parent
Definition: videosource.h:256
QString GetWhereClause(MSqlBindings &bindings) const override
int getCardID(void) const
void ShowDeleteAllCaptureCardsDialog(void) const
void DeleteAllCaptureCardsOnHost(bool doDelete)
void(CaptureCardEditor::*)(void) const CCESlotConst
Definition: videosource.h:854
void Load(void) override
void(CaptureCardEditor::*)(void) CCESlot
Definition: videosource.h:853
void DeleteAllCaptureCards(bool doDelete)
void AddNewCard(void)
void ShowDeleteAllCaptureCardsDialogOnHost(void) const
void AddSelection(const QString &label, CCESlot slot)
CaptureCardGroup(CaptureCard &parent)
~CaptureCardSpinBoxSetting() override
void setValueMs(std::chrono::duration< T > newSecs)
CaptureCardSpinBoxSetting(const CaptureCard &parent, std::chrono::milliseconds min_val, std::chrono::milliseconds max_val, std::chrono::milliseconds step, const QString &setting)
void setValueMs(std::chrono::milliseconds newValue)
CaptureCardTextEditSetting(const CaptureCard &parent, const QString &setting)
~CaptureCardTextEditSetting() override
Hostname(const CaptureCard &parent)
void Save(void) override
void deleteEntry(void) override
QString GetRawCardType(void) const
bool canDelete(void) override
int getCardID(void) const
Definition: videosource.h:764
void loadByID(int id)
CaptureCard(bool use_card_group=true)
void reload(void)
static void fillSelections(GroupSetting *setting)
~CardInputComboBoxSetting() override
CardInputComboBoxSetting(const CardInput &parent, const QString &setting)
const CardInput & m_parent
Definition: videosource.h:820
int getInputID(void) const
QString GetWhereClause(MSqlBindings &bindings) const override
QString GetSetClause(MSqlBindings &bindings) const override
void Load(void) override
std::vector< CardInput * > m_cardInputs
Definition: videosource.h:898
void SetSourceID(const QString &sourceid)
SourceID * m_sourceId
Definition: videosource.h:958
void CreateNewInputGroup()
void sourceFetch()
void Save(void) override
InputName * m_inputName
Definition: videosource.h:957
CardInput(const QString &cardtype, const QString &device, int cardid)
int getInputID(void) const
Definition: videosource.h:932
MythUISpinBoxSetting * m_instanceCount
Definition: videosource.h:965
QString getSourceName(void) const
void loadByID(int id)
MythUICheckBoxSetting * m_schedGroup
Definition: videosource.h:966
void loadByInput(int cardid, const QString &inputname)
~CardInput() override
StartingChannel * m_startChan
Definition: videosource.h:959
DiSEqCDevSettings * m_externalInputSettings
Definition: videosource.h:962
ButtonStandardSetting * m_srcFetch
Definition: videosource.h:961
ButtonStandardSetting * m_scan
Definition: videosource.h:960
InputGroup * m_inputGrp0
Definition: videosource.h:963
void channelScanner()
void CreateNewInputGroupSlot(const QString &name)
InputGroup * m_inputGrp1
Definition: videosource.h:964
static void fillSelections(MythUIComboBoxSetting *setting)
CardType(const CaptureCard &parent)
static int GetASIDeviceNumber(const QString &device, QString *error=nullptr)
Definition: cardutil.cpp:3262
static bool IsTunerSharingCapable(const QString &rawtype)
Definition: cardutil.h:179
static INPUT_TYPES toInputType(const QString &name)
Definition: cardutil.h:80
static void GetDeviceInputNames(const QString &device, const QString &inputtype, QStringList &inputs)
Definition: cardutil.cpp:2666
static bool IsUnscanable(const QString &rawtype)
Definition: cardutil.h:160
static uint CreateInputGroup(const QString &name)
Definition: cardutil.cpp:2033
static QString GetRawInputType(uint inputid)
Definition: cardutil.h:294
static QString GetAudioDevice(uint inputid)
Definition: cardutil.h:298
static QString ProbeDVBFrontendName(const QString &device)
Returns the input type from the video device.
Definition: cardutil.cpp:752
static QString ProbeDVBType(const QString &device)
Definition: cardutil.cpp:731
static bool InputSetMaxRecordings(uint parentid, uint max_recordings)
Definition: cardutil.cpp:1567
static QStringList ProbeDeliverySystems(const QString &device)
Definition: cardutil.cpp:640
static uint CreateDeviceInputGroup(uint inputid, const QString &type, const QString &host, const QString &device)
Definition: cardutil.cpp:2075
static QString GetStartChannel(uint inputid)
Definition: cardutil.cpp:1798
static QString GetDeviceLabel(const QString &inputtype, const QString &videodevice)
Definition: cardutil.cpp:2644
static QString GetVBoxdesc(const QString &id, const QString &ip, const QString &tunerNo, const QString &tunerType)
Get a nicely formatted string describing the device.
Definition: cardutil.cpp:3191
static QStringList GetVideoDevices(const QString &rawtype, QString hostname=QString())
Returns the videodevices of the matching inputs, duplicates removed.
Definition: cardutil.cpp:402
static bool HasTuner(const QString &rawtype, const QString &device)
Definition: cardutil.cpp:221
static QString GetDeliverySystemFromDB(uint inputid)
Definition: cardutil.h:302
static bool UnlinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:2163
static bool LinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:2112
static QString GetVideoDevice(uint inputid)
Definition: cardutil.h:296
static bool IsInNeedOfExternalInputConf(uint inputid)
Definition: cardutil.cpp:2317
static void ClearVideoDeviceCache()
Definition: cardutil.cpp:447
static uint CloneCard(uint src_inputid, uint dst_inputid)
Definition: cardutil.cpp:1556
static bool IsV4L(const QString &rawtype)
Definition: cardutil.h:147
static std::vector< uint > GetChildInputIDs(uint inputid)
Definition: cardutil.cpp:1379
static QString GetDeviceName(dvb_dev_type_t type, const QString &device)
Definition: cardutil.cpp:2973
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:137
static bool DeleteInput(uint inputid)
Definition: cardutil.cpp:2829
static QString ProbeDefaultDeliverySystem(const QString &device)
Definition: cardutil.cpp:715
static QStringList ProbeVideoDevices(const QString &rawtype)
Definition: cardutil.cpp:453
static bool IsCableCardPresent(uint inputid, const QString &inputType)
Definition: cardutil.cpp:113
static bool GetV4LInfo(int videofd, QString &input, QString &driver, uint32_t &version, uint32_t &capabilities)
Definition: cardutil.cpp:2361
static bool DeleteAllInputs(void)
Definition: cardutil.cpp:2899
static QStringList ProbeAudioInputs(const QString &device, const QString &inputtype=QString())
Definition: cardutil.cpp:2548
static uint GetDeviceInputGroup(uint inputid)
Definition: cardutil.cpp:2088
ChannelTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::duration< T > min_secs)
ChannelTimeout(const CaptureCard &parent, std::chrono::duration< T > value, std::chrono::duration< T > min_secs)
ChannelTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::milliseconds min_val)
static void SortChannels(ChannelInfoList &list, const QString &order, bool eliminate_duplicates=false)
static ChannelInfoList GetAllChannels(uint sourceid)
Returns channels that are not connected to an input and channels that are not marked as visible.
Definition: channelutil.h:265
~CommandPath() override
CommandPath(const CaptureCard &parent)
QString GetColumnName(void) const
Definition: mythstorage.h:47
StorageUser * m_user
Definition: mythstorage.h:50
void fillSelections(const QString &current)
Adds all available cards to list If current is >= 0 it will be considered available even if no device...
void Load(void) override
DVBCardNum(const CaptureCard &parent)
DVBCardType(const CaptureCard &parent)
void reloadDiseqcTree(const QString &device)
DiSEqCDevTree * m_diseqcTree
Definition: videosource.h:717
DVBCardType * m_cardType
Definition: videosource.h:713
DVBCardName * m_cardName
Definition: videosource.h:712
CaptureCard & m_parent
Definition: videosource.h:709
DVBConfigurationGroup(CaptureCard &a_parent, CardType &cardType)
~DVBConfigurationGroup() override
SignalTimeout * m_signalTimeout
Definition: videosource.h:714
ChannelTimeout * m_channelTimeout
Definition: videosource.h:715
DeviceTree * m_diseqcBtn
Definition: videosource.h:718
void Load(void) override
void Save(void) override
void probeCard(const QString &videodevice)
DVBCardNum * m_cardNum
Definition: videosource.h:711
DVBTuningDelay * m_tuningDelay
Definition: videosource.h:716
DVBEITScan(const CaptureCard &parent)
~DVBEITScan() override
DVBNetID(const VideoSource &parent, signed int value, signed int min_val)
~DVBNetID() override
~DVBNoSeqStart() override
DVBNoSeqStart(const CaptureCard &parent)
~DVBOnDemand() override
DVBOnDemand(const CaptureCard &parent)
DVBTuningDelay(const CaptureCard &parent)
GroupSetting * m_info
Definition: videosource.h:664
GroupSetting * m_size
Definition: videosource.h:665
DemoConfigurationGroup(CaptureCard &parent, CardType &cardtype)
void probeCard(const QString &device)
CaptureCard & m_parent
Definition: videosource.h:663
void Load(void) override
DVB-S device settings class.
Definition: diseqc.h:37
bool Store(uint card_input_id) const
Stores configuration chain to DB for specified card input id.
Definition: diseqc.cpp:164
bool Load(uint card_input_id)
Loads configuration chain from DB for specified card input id.
Definition: diseqc.cpp:130
DVB-S device tree class.
Definition: diseqc.h:75
bool Load(const QString &device)
Loads the device tree from the database.
Definition: diseqc.cpp:313
bool Store(uint cardid, const QString &device="")
Stores the device tree to the database.
Definition: diseqc.cpp:424
static bool Exists(int cardid)
Check if a Diseqc device tree exists.
Definition: diseqc.cpp:395
static void InvalidateTrees(void)
Invalidate cached trees.
Definition: diseqc.cpp:247
~DishNetEIT() override
DishNetEIT(const CardInput &parent)
UseEIT * m_useEit
Definition: videosource.h:153
void Save(void) override
EITOnly_config(const VideoSource &_parent, StandardSetting *_setting)
~ExternalChannelCommand() override
ExternalChannelCommand(const CardInput &parent)
~FileDevice() override
FileDevice(const CaptureCard &parent)
FirewireConnection(const CaptureCard &parent)
~FirewireConnection() override
const FirewireGUID * m_guid
Definition: videosource.h:748
void SetGUID(const QString &_guid)
static std::vector< AVCInfo > GetSTBList(void)
static QString GetModelName(uint vendor_id, uint model_id)
AVCInfo GetAVCInfo(const QString &guid) const
FirewireGUID(const CaptureCard &parent)
QMap< QString, AVCInfo > m_guidToAvcInfo
const FirewireGUID * m_guid
Definition: videosource.h:733
FirewireModel(const CaptureCard &parent, const FirewireGUID *_guid)
void SetGUID(const QString &_guid)
FirewireSpeed(const CaptureCard &parent)
~FirewireSpeed() override
FreqTableSelector(const VideoSource &parent)
~FreqTableSelector() override
GroupSetting()=default
void probeCard(const QString &device)
CaptureCard & m_parent
Definition: videosource.h:594
GroupSetting * m_cardInfo
Definition: videosource.h:595
TunerCardAudioInput * m_audioInput
Definition: videosource.h:596
HDPVRConfigurationGroup(CaptureCard &parent, CardType &cardtype)
static QString TestMimeType(const QString &sFileName)
IPTVHost(const CaptureCard &parent)
void setValue(int value) override
Definition: videosource.h:177
GroupSetting * m_info
Definition: videosource.h:648
CaptureCard & m_parent
Definition: videosource.h:647
ImportConfigurationGroup(CaptureCard &parent, CardType &cardtype)
void probeCard(const QString &device)
GroupSetting * m_size
Definition: videosource.h:649
Q_DECLARE_TR_FUNCTIONS(InputDisplayName)
~InputDisplayName() override
void Load(void) override
InputDisplayName(const CardInput &parent)
const CardInput & m_parent
const CardInput & m_cardInput
void Save(void) override
virtual void Save(const QString &)
void Load(void) override
InputGroup(const CardInput &parent, uint group_num)
void fillSelections()
void Load(void) override
InputName(const CardInput &parent)
~InputName() override
InputPriority(const CardInput &parent)
~InputPriority() override
~InstanceCount() override
InstanceCount(const CardInput &parent)
LCNOffset(const VideoSource &parent, signed int value, signed int min_val)
~LCNOffset() override
LiveTVOrder(const CardInput &parent, int _value)
~LiveTVOrder() override
VBIDevice * m_vbiDevice
Definition: videosource.h:579
void probeCard(const QString &device)
MPEGConfigurationGroup(CaptureCard &parent, CardType &cardtype)
GroupSetting * m_cardInfo
Definition: videosource.h:580
CaptureCard & m_parent
Definition: videosource.h:577
VideoDevice * m_device
Definition: videosource.h:578
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
QString GetHostName(void)
QString GetSetting(const QString &key, const QString &defaultval="")
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
MythScreenStack * GetMainStack()
MythScreenStack * GetStack(const QString &Stackname)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Screen in which all other widgets are contained and rendered.
virtual bool Create(void)
uint Wait(std::chrono::seconds timeout=0s)
void Run(std::chrono::seconds timeout=0s)
Runs a command inside the /bin/sh shell. Returns immediately.
QByteArray & ReadAll()
Dialog prompting the user to enter a text string.
void haveResult(QString)
void setValue(const QString &newValue) override
void addSelection(const QString &label, QString value=QString(), bool select=false)
QString getValueLabel(void) const
void fillSelectionsFromDir(const QDir &dir, bool absPath=true)
void setValue(int value) override
int getValueIndex(const QString &value) const
UseEIT * m_useEit
Definition: videosource.h:165
void Save(void) override
NoGrabber_config(const VideoSource &_parent)
~PresetTuner() override
PresetTuner(const CardInput &parent)
QuickTune(const CardInput &parent)
~RegionID() override
RegionID(const VideoSource &parent, signed int value, signed int min_val)
~ScanFrequencyStart() override
ScanFrequencyStart(const VideoSource &parent)
SchedGroupFalse(const CaptureCard &parent)
~SchedGroupFalse() override
SchedGroup(const CardInput &parent)
~SchedGroup() override
~ScheduleOrder() override
ScheduleOrder(const CardInput &parent, int _value)
SignalTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::milliseconds min_val)
SignalTimeout(const CaptureCard &parent, std::chrono::duration< T > value, std::chrono::duration< T > min_secs)
SignalTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::duration< T > min_secs)
SkipBtAudio(const CaptureCard &parent)
~SkipBtAudio() override
void fillSelections()
void Load(void) override
SourceID(const CardInput &parent)
static bool GetListingsLoginData(uint sourceid, QString &grabber, QString &userid, QString &passwd, QString &lineupid)
Definition: sourceutil.cpp:172
static bool DeleteSource(uint sourceid)
Definition: sourceutil.cpp:530
static bool DeleteAllSources(void)
Definition: sourceutil.cpp:586
static uint GetChannelCount(uint sourceid)
Definition: sourceutil.cpp:136
static bool UpdateChannelsFromListings(uint sourceid, const QString &inputtype=QString(), bool wait=false)
Definition: sourceutil.cpp:396
virtual void addChild(StandardSetting *child)
virtual void Save(void)
StandardSetting * m_parent
virtual void Load(void)
bool isEnabled() const
virtual void setReadOnly(bool readonly)
void addTargetedChild(const QString &value, StandardSetting *setting)
virtual void clearSettings()
void settingsChanged(StandardSetting *selectedSetting=nullptr)
virtual void setHelpText(const QString &str)
StandardSetting * getParent() const
void setVisible(bool visible)
virtual QList< StandardSetting * > * getSubSettings()
virtual void setValue(const QString &newValue)
Storage * GetStorage(void) const
void valueChanged(const QString &newValue)
virtual QString getValue(void) const
virtual void setEnabled(bool enabled)
virtual void setLabel(QString str)
void SetSourceID(const QString &sourceid)
virtual QString GetDBValue(void) const =0
virtual void Load(void)=0
void SetSourceID(uint sourceid)
void Save(void) override
void Load(void) override
TransFreqTableSelector(uint sourceid)
int fillSelections(const QString &device)
TunerCardAudioInput(const CaptureCard &parent, QString dev=QString(), QString type=QString())
UseEIT(const VideoSource &parent)
~UseEIT() override
VideoDevice * m_device
Definition: videosource.h:612
CaptureCard & m_parent
Definition: videosource.h:610
GroupSetting * m_cardInfo
Definition: videosource.h:611
void probeCard(const QString &device)
QString m_driverName
Definition: videosource.h:614
V4L2encGroup(CaptureCard &parent, CardType &cardType)
QString DriverName(void) const
Definition: v4l2util.h:50
bool HasSlicedVBI(void) const
Definition: v4l2util.cpp:118
bool IsOpen(void) const
Definition: v4l2util.h:31
QString CardName(void) const
Definition: v4l2util.h:51
V4LConfigurationGroup(CaptureCard &parent, CardType &cardtype, const QString &inputtype)
void probeCard(const QString &device)
VBIDevice * m_vbiDev
Definition: videosource.h:560
GroupSetting * m_cardInfo
Definition: videosource.h:559
CaptureCard & m_parent
Definition: videosource.h:558
VBIDevice(const CaptureCard &parent)
uint fillSelectionsFromDir(const QDir &dir, const QString &card, const QString &driver)
uint setFilter(const QString &card, const QString &driver)
void fillSelectionsFromDir(const QDir &dir, bool absPath=true)
VBoxDeviceList m_deviceList
Definition: videosource.h:544
StandardSetting * m_desc
Definition: videosource.h:539
VBoxDeviceID * m_deviceId
Definition: videosource.h:541
VBoxDeviceIDList * m_deviceIdList
Definition: videosource.h:540
VBoxTunerIndex * m_cardTuner
Definition: videosource.h:543
VBoxConfigurationGroup(CaptureCard &parent, CardType &cardtype)
CaptureCard & m_parent
Definition: videosource.h:538
void fillSelections(const QString &current)
Adds all available device-tuner combinations to list.
VBoxDeviceID * m_deviceId
Definition: videosource.h:1035
const CaptureCard & m_parent
Definition: videosource.h:1040
VBoxDeviceList * m_deviceList
Definition: videosource.h:1039
StandardSetting * m_desc
Definition: videosource.h:1036
VBoxTunerIndex * m_cardTuner
Definition: videosource.h:1038
void Load(void) override
void UpdateDevices(const QString &v)
VBoxDeviceIDList(VBoxDeviceID *deviceid, StandardSetting *desc, VBoxIP *cardip, VBoxTunerIndex *cardtuner, VBoxDeviceList *devicelist, const CaptureCard &parent)
void SetTuner(const QString &tuner)
void Load(void) override
void SetOverrideDeviceID(const QString &deviceid)
~VBoxDeviceID() override
VBoxDeviceID(const CaptureCard &parent)
void SetIP(const QString &ip)
QString m_tuner
Definition: videosource.h:1062
QString m_ip
Definition: videosource.h:1061
QString m_overrideDeviceId
Definition: videosource.h:1063
bool m_inUse
Definition: videosource.h:517
QString m_cardIp
Definition: videosource.h:514
QString m_deviceId
Definition: videosource.h:512
QString m_mythDeviceId
Definition: videosource.h:511
QString m_tunerNo
Definition: videosource.h:515
QString m_tunerType
Definition: videosource.h:516
QString m_desc
Definition: videosource.h:513
bool m_discovered
Definition: videosource.h:518
void NewIP(const QString &)
void SetOldValue(const QString &s)
Definition: videosource.h:981
void setEnabled(bool e) override
void UpdateDevices(const QString &v)
QString m_oldValue
Definition: videosource.h:991
QString m_oldValue
Definition: videosource.h:1012
void UpdateDevices(const QString &v)
void setEnabled(bool e) override
void NewTuner(const QString &)
void SetOldValue(const QString &s)
Definition: videosource.h:1002
void fillSelectionsFromDir(const QDir &dir, bool absPath=true)
QString Card(void) const
QString m_driverName
VideoDevice(const CaptureCard &parent, uint minor_min=0, uint minor_max=UINT_MAX, const QString &card=QString(), const QRegularExpression &driver=QRegularExpression())
QString Driver(void) const
QString m_cardName
uint fillSelectionsFromDir(const QDir &dir, uint minor_min, uint minor_max, const QString &card, const QRegularExpression &driver, bool allow_duplicates)
QMap< uint, uint > m_minorList
QString GetWhereClause(MSqlBindings &bindings) const override
const VideoSource & m_parent
Definition: videosource.h:59
QString GetSetClause(MSqlBindings &bindings) const override
void DeleteAllSources(bool doDelete)
void NewSource(void)
static bool cardTypesInclude(int SourceID, const QString &thecardtype)
void(VideoSourceEditor::*)(void) VSESlot
Definition: videosource.h:877
void ShowDeleteAllSourcesDialog(void) const
void(VideoSourceEditor::*)(void) const VSESlotConst
Definition: videosource.h:878
void Load(void) override
void AddSelection(const QString &label, VSESlot slot)
VideoSourceSelector(uint _initial_sourceid, QString _card_types, bool _must_have_mplexid)
Definition: videosource.cpp:69
void Load(void) override
Definition: videosource.cpp:85
void Load(void) override
VideoSourceShow(uint _initial_sourceid)
uint m_initialSourceId
Definition: videosource.h:93
static void fillSelections(GroupSetting *setting)
int getSourceID(void) const
Definition: videosource.h:188
Name * m_name
Definition: videosource.h:237
QString getSourceName(void) const
Definition: videosource.h:196
bool canDelete(void) override
void deleteEntry(void) override
void loadByID(int id)
static QMutex m_lock
void LoadXMLTVGrabbers(QStringList name_list, QStringList prog_list)
static QStringList m_nameList
void Save(void) override
static QStringList m_progList
~XMLTVGrabber() override
XMLTVGrabber(const VideoSource &parent)
const VideoSource & m_parent
void Load(void) override
QStringList m_grabberArgs
Definition: videosource.h:141
const VideoSource & m_parent
Definition: videosource.h:139
void Save(void) override
XMLTV_generic_config(const VideoSource &_parent, const QString &_grabber, StandardSetting *_setting)
unsigned int uint
Definition: compat.h:60
#define close
Definition: compat.h:28
#define lstat
Definition: compat.h:65
#define minor(X)
Definition: compat.h:58
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
const CHANLISTS_vec gChanLists
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:100
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
QString GetConfDir(void)
Definition: mythdirs.cpp:285
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
@ kMSStdOut
allow access to stdout
Definition: mythsystem.h:41
@ kMSRunShell
run process through shell
Definition: mythsystem.h:43
@ kMSDontDisableDrawing
avoid disabling UI drawing
Definition: mythsystem.h:37
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
dictionary info
Definition: azlyrics.py:7
def error(message)
Definition: smolt.py:409
STL namespace.
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:86
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:80
static void IPTVConfigurationGroup(CaptureCard &parent, CardType &cardType)
QMap< QString, VBoxDevice > VBoxDeviceList
Definition: videosource.h:521
static bool is_grabber_external(const QString &grabber)
Definition: videosource.h:34