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,5,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>
424 void setValueMs (std::chrono::duration<T> newSecs)
425 requires (!std::is_integral<T>())
426 { setValueMs(duration_cast<std::chrono::milliseconds>(newSecs)); }
427};
428
430{
431 public:
433 const QString &setting) :
434 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, setting))
435 {
436 }
437
439 {
440 delete GetStorage();
441 }
442};
443
445{
446 public:
447 explicit ScanFrequencyStart(const VideoSource &parent) :
448 MythUITextEditSetting(new VideoSourceDBStorage(this, parent, "scanfrequency"))
449 {
450 setLabel(QObject::tr("Scan Frequency"));
451 setHelpText(QObject::tr("The frequency to start scanning this video source. "
452 "This is then default for 'Full Scan (Tuned)' channel scanning. "
453 "Frequency value in Hz for DVB-T/T2/C, in kHz for DVB-S/S2. "
454 "Leave at 0 if not known. "));
455 };
456
458 {
459 delete GetStorage();
460 }
461};
462
464{
465 public:
466 DVBNetID(const VideoSource &parent, signed int value, signed int min_val) :
467 MythUISpinBoxSetting(new VideoSourceDBStorage(this, parent, "dvb_nit_id"),
468 min_val, 0xffff, 1)
469 {
470 setLabel(QObject::tr("Network ID"));
471 //: Network_ID is the name of an identifier in the DVB's Service
472 //: Information standard specification.
473 setHelpText(QObject::tr("If your provider has asked you to configure a "
474 "specific network identifier (Network_ID), "
475 "enter it here. Leave it at -1 otherwise."));
476 setValue(value);
477 };
478
479 ~DVBNetID() override
480 {
481 delete GetStorage();
482 }
483};
484
486{
487 public:
488 BouquetID(const VideoSource &parent, signed int value, signed int min_val) :
489 MythUISpinBoxSetting(new VideoSourceDBStorage(this, parent, "bouquet_id"),
490 min_val, 0xffff, 1)
491 {
492 setLabel(QObject::tr("Bouquet ID"));
493 setHelpText(QObject::tr("Bouquet ID for Freesat or Sky on satellite Astra-2 28.2E. "
494 "Leave this at 0 if you do not receive this satellite. "
495 "This is needed to get the Freesat and Sky channel numbers. "
496 "Value 272 selects Freesat bouquet 'England HD'. "
497 "See the MythTV Wiki https://www.mythtv.org/wiki/DVB_UK."));
498 setValue(value);
499 };
500
501 ~BouquetID() override
502 {
503 delete GetStorage();
504 }
505};
506
508{
509 public:
510 RegionID(const VideoSource &parent, signed int value, signed int min_val) :
511 MythUISpinBoxSetting(new VideoSourceDBStorage(this, parent, "region_id"),
512 min_val, 100, 1)
513 {
514 setLabel(QObject::tr("Region ID"));
515 setHelpText(QObject::tr("Region ID for Freesat or Sky on satellite Astra-2 28.2E. "
516 "Leave this at 0 you do not receive this satellite. "
517 "This is needed to get the Freesat and Sky channel numbers. "
518 "Value 1 selects region London. "
519 "See the MythTV Wiki https://www.mythtv.org/wiki/DVB_UK."));
520 setValue(value);
521 };
522
523 ~RegionID() override
524 {
525 delete GetStorage();
526 }
527};
528
530{
531 public:
532 LCNOffset(const VideoSource &parent, signed int value, signed int min_val) :
533 MythUISpinBoxSetting(new VideoSourceDBStorage(this, parent, "lcnoffset"),
534 min_val, 20000, 100)
535 {
536 setLabel(QObject::tr("Logical Channel Number Offset"));
537 setHelpText(QObject::tr("The offset is added to each logical channel number found "
538 "during a scan of a DVB video source. This makes it possible "
539 "to give different video sources a non-overlapping range "
540 "of channel numbers. Leave at 0 if you have only one video source "
541 "or if the video sources do not have DVB logical channel numbers."));
542 setValue(value);
543 };
544
545 ~LCNOffset() override
546 {
547 delete GetStorage();
548 }
549};
550
552 MythUIComboBoxSetting(new VideoSourceDBStorage(this, parent, "freqtable"))
553{
554 setLabel(QObject::tr("Channel frequency table"));
555 addSelection("default");
556
557 for (const auto & chanlist : gChanLists)
558 addSelection(chanlist.name);
559
560 setHelpText(QObject::tr("Use default unless this source uses a "
561 "different frequency table than the system wide table "
562 "defined in the General settings."));
563}
564
566{
567 delete GetStorage();
568}
569
571 m_sourceId(_sourceid)
572{
573 setLabel(QObject::tr("Channel frequency table"));
574
575 for (const auto & chanlist : gChanLists)
576 addSelection(chanlist.name);
577}
578
580{
581 int idx1 = getValueIndex(gCoreContext->GetSetting("FreqTable"));
582 if (idx1 >= 0)
583 setValue(idx1);
584
585 if (!m_sourceId)
586 return;
587
589 query.prepare(
590 "SELECT freqtable "
591 "FROM videosource "
592 "WHERE sourceid = :SOURCEID");
593 query.bindValue(":SOURCEID", m_sourceId);
594
595 if (!query.exec() || !query.isActive())
596 {
597 MythDB::DBError("TransFreqTableSelector::load", query);
598 return;
599 }
600
601 m_loadedFreqTable.clear();
602
603 if (query.next())
604 {
605 m_loadedFreqTable = query.value(0).toString();
606 if (!m_loadedFreqTable.isEmpty() &&
607 (m_loadedFreqTable.toLower() != "default"))
608 {
610 if (idx2 >= 0)
611 setValue(idx2);
612 }
613 }
614}
615
617{
618 LOG(VB_GENERAL, LOG_INFO, "TransFreqTableSelector::Save(void)");
619
620 if ((m_loadedFreqTable == getValue()) ||
621 ((m_loadedFreqTable.toLower() == "default") &&
622 (getValue() == gCoreContext->GetSetting("FreqTable"))))
623 {
624 return;
625 }
626
628 query.prepare(
629 "UPDATE videosource "
630 "SET freqtable = :FREQTABLE "
631 "WHERE sourceid = :SOURCEID");
632
633 query.bindValue(":FREQTABLE", getValue());
634 query.bindValue(":SOURCEID", m_sourceId);
635
636 if (!query.exec() || !query.isActive())
637 {
638 MythDB::DBError("TransFreqTableSelector::load", query);
639 return;
640 }
641}
642
644{
645 m_sourceId = sourceid;
646 Load();
647}
648
650{
651 public:
652 explicit UseEIT(const VideoSource &parent) :
653 MythUICheckBoxSetting(new VideoSourceDBStorage(this, parent, "useeit"))
654 {
655 setLabel(QObject::tr("Perform EIT scan"));
656 setHelpText(QObject::tr(
657 "If enabled, program guide data for channels on this "
658 "source will be updated with data provided by the "
659 "channels themselves 'Over-the-Air'."));
660 }
661
662 ~UseEIT() override
663 {
664 delete GetStorage();
665 }
666};
667
669 const QString& _grabber,
670 StandardSetting *_setting) :
671 m_parent(_parent), m_grabber(_grabber)
672{
673 setVisible(false);
674
675 QString filename = QString("%1/%2.xmltv")
677
678 m_grabberArgs.push_back("--config-file");
679 m_grabberArgs.push_back(filename);
680 m_grabberArgs.push_back("--configure");
681
682 _setting->addTargetedChild(_grabber, new UseEIT(m_parent));
683
684 auto *config = new ButtonStandardSetting(tr("Configure"));
685 config->setHelpText(tr("Run XMLTV configure command."));
686
687 _setting->addTargetedChild(_grabber, config);
688
690}
691
693{
695#if 0
696 QString err_msg = QObject::tr(
697 "You MUST run 'mythfilldatabase --manual' the first time,\n"
698 "instead of just 'mythfilldatabase'.\nYour grabber does not provide "
699 "channel numbers, so you have to set them manually.");
700
702 {
703 LOG(VB_GENERAL, LOG_ERR, err_msg);
704 ShowOkPopup(err_msg);
705 }
706#endif
707}
708
710{
712 MythScreenType *ssd =
713 new MythTerminal(mainStack, m_grabber, m_grabberArgs);
714
715 if (ssd->Create())
716 mainStack->AddScreen(ssd);
717 else
718 delete ssd;
719}
720
722 : m_useEit(new UseEIT(_parent))
723{
724 setVisible(false);
725
726 m_useEit->setValue(true);
727 m_useEit->setVisible(false);
729
730 auto *label=new TransTextEditSetting();
731 label->setValue(QObject::tr("Use only the transmitted guide data."));
732 label->setHelpText(
733 QObject::tr("This will usually only work with ATSC or DVB channels, "
734 "and generally provides data only for the next few days."));
735 _setting->addTargetedChild("eitonly", label);
736}
737
739{
740 // Force this value on
741 m_useEit->setValue(true);
742 m_useEit->Save();
743}
744
746 : m_useEit(new UseEIT(_parent))
747{
748 m_useEit->setValue(false);
749 m_useEit->setVisible(false);
751
752 auto *label = new TransTextEditSetting();
753 label->setValue(QObject::tr("Do not configure a grabber"));
754 addTargetedChild("/bin/true", label);
755}
756
758{
759 m_useEit->setValue(false);
760 m_useEit->Save();
761}
762
764 // must be first
765 : m_id(new ID())
766{
767 addChild(m_id = new ID());
768
769 setLabel(QObject::tr("Video Source Setup"));
770 addChild(m_name = new Name(*this));
771 addChild(new XMLTVGrabber(*this));
772 addChild(new FreqTableSelector(*this));
773 addChild(new ScanFrequencyStart(*this));
774 addChild(new DVBNetID(*this, -1, -1));
775 addChild(new BouquetID(*this, 0, 0));
776 addChild(new RegionID(*this, 0, 0));
777 addChild(new LCNOffset(*this, 0, 0));
778}
779
781{
782 return true;
783}
784
786{
788}
789
791 const QString &thecardtype)
792{
794 query.prepare("SELECT count(cardtype)"
795 " FROM capturecard "
796 " WHERE capturecard.sourceid = :SOURCEID "
797 " AND capturecard.cardtype = :CARDTYPE ;");
798 query.bindValue(":SOURCEID", sourceID);
799 query.bindValue(":CARDTYPE", thecardtype);
800
801 if (query.exec() && query.next())
802 {
803 int count = query.value(0).toInt();
804
805 if (count > 0)
806 return true;
807 }
808
809 return false;
810}
811
813{
815 result.prepare("SELECT name, sourceid FROM videosource;");
816
817 if (result.exec() && result.isActive() && result.size() > 0)
818 {
819 while (result.next())
820 {
821 auto* source = new VideoSource();
822 source->setLabel(result.value(0).toString());
823 source->loadByID(result.value(1).toInt());
824 setting->addChild(source);
825 }
826 }
827}
828
830{
832 result.prepare("SELECT name, sourceid FROM videosource;");
833
834 if (result.exec() && result.isActive() && result.size() > 0)
835 {
836 while (result.next())
837 {
838 setting->addSelection(result.value(0).toString(),
839 result.value(1).toString());
840 }
841 }
842}
843
844void VideoSource::loadByID(int sourceid)
845{
846 m_id->setValue(sourceid);
847}
848
850{
851 public:
852 explicit VideoDevice(const CaptureCard &parent,
853 uint minor_min = 0,
854 uint minor_max = UINT_MAX,
855 const QString& card = QString(),
856 const QRegularExpression& driver = QRegularExpression()) :
857 CaptureCardComboBoxSetting(parent, true, "videodevice")
858 {
859 setLabel(QObject::tr("Video device"));
860
861 // /dev/v4l/video*
862 QDir dev("/dev/v4l", "video*", QDir::Name, QDir::System);
863 fillSelectionsFromDir(dev, minor_min, minor_max,
864 card, driver, false);
865
866 // /dev/video*
867 dev.setPath("/dev");
868 fillSelectionsFromDir(dev, minor_min, minor_max,
869 card, driver, false);
870
871 // /dev/dtv/video*
872 dev.setPath("/dev/dtv");
873 fillSelectionsFromDir(dev, minor_min, minor_max,
874 card, driver, false);
875
876 // /dev/dtv*
877 dev.setPath("/dev");
878 dev.setNameFilters(QStringList("dtv*"));
879 fillSelectionsFromDir(dev, minor_min, minor_max,
880 card, driver, false);
881 };
882
887 void fillSelectionsFromDir(const QDir &dir,
888 [[maybe_unused]] bool absPath = true) override
889 {
890 fillSelectionsFromDir(dir, 0, 255, QString(), QRegularExpression(), false);
891 }
892
894 uint minor_min, uint minor_max,
895 const QString& card, const QRegularExpression& driver,
896 bool allow_duplicates)
897 {
898 uint cnt = 0;
899 QFileInfoList entries = dir.entryInfoList();
900 for (const auto & fi : std::as_const(entries))
901 {
902 struct stat st {};
903 QString filepath = fi.absoluteFilePath();
904 int err = lstat(filepath.toLocal8Bit().constData(), &st);
905
906 if (err)
907 {
908 LOG(VB_GENERAL, LOG_ERR,
909 QString("Could not stat file: %1").arg(filepath));
910 continue;
911 }
912
913 // is this is a character device?
914 if (!S_ISCHR(st.st_mode))
915 continue;
916
917 // is this device is in our minor range?
918 uint minor_num = minor(st.st_rdev);
919 if (minor_min > minor_num || minor_max < minor_num)
920 continue;
921
922 // ignore duplicates if allow_duplicates not set
923 if (!allow_duplicates && m_minorList[minor_num])
924 continue;
925
926 // if the driver returns any info add this device to our list
927 QByteArray tmp = filepath.toLatin1();
928 int videofd = open(tmp.constData(), O_RDWR);
929 if (videofd >= 0)
930 {
931 QString card_name;
932 QString driver_name;
933 if (CardUtil::GetV4LInfo(videofd, card_name, driver_name))
934 {
935 auto match = driver.match(driver_name);
936 if ((!driver.pattern().isEmpty() || match.hasMatch()) &&
937 (card.isEmpty() || (card_name == card)))
938 {
939 addSelection(filepath);
940 cnt++;
941 }
942 }
943 close(videofd);
944 }
945
946 // add to list of minors discovered to avoid duplicates
947 m_minorList[minor_num] = 1;
948 }
949
950 return cnt;
951 }
952
953 QString Driver(void) const { return m_driverName; }
954 QString Card(void) const { return m_cardName; }
955
956 private:
957 QMap<uint, uint> m_minorList;
958 QString m_cardName;
960};
961
963{
964 public:
965 explicit VBIDevice(const CaptureCard &parent) :
966 CaptureCardComboBoxSetting(parent, true /*, mustexist true */,
967 "vbidevice")
968 {
969 setLabel(QObject::tr("VBI device"));
970 setFilter(QString(), QString());
971 setHelpText(QObject::tr("Device to read VBI (captions) from."));
972 };
973
974 uint setFilter(const QString &card, const QString &driver)
975 {
977 QDir dev("/dev/v4l", "vbi*", QDir::Name, QDir::System);
978 uint count = fillSelectionsFromDir(dev, card, driver);
979 if (count == 0)
980 {
981 dev.setPath("/dev");
982 count = fillSelectionsFromDir(dev, card, driver);
983 if ((count == 0U) && !getValue().isEmpty())
984 {
986 }
987 }
988
989 return count;
990 }
991
996 void fillSelectionsFromDir(const QDir &dir,
997 [[maybe_unused]] bool absPath = true) override
998 {
999 fillSelectionsFromDir(dir, QString(), QString());
1000 }
1001
1002 uint fillSelectionsFromDir(const QDir &dir, const QString &card,
1003 const QString &driver)
1004 {
1005 QStringList devices;
1006 QFileInfoList entries = dir.entryInfoList();
1007 for (const auto & fi : std::as_const(entries))
1008 {
1009 QString device = fi.absoluteFilePath();
1010 QByteArray adevice = device.toLatin1();
1011 int vbifd = open(adevice.constData(), O_RDWR);
1012 if (vbifd < 0)
1013 continue;
1014
1015 QString cn;
1016 QString dn;
1017 if (CardUtil::GetV4LInfo(vbifd, cn, dn) &&
1018 (driver.isEmpty() || (dn == driver)) &&
1019 (card.isEmpty() || (cn == card)))
1020 {
1021 devices.push_back(device);
1022 }
1023
1024 close(vbifd);
1025 }
1026
1027 QString sel = getValue();
1028 for (const QString& device : std::as_const(devices))
1029 addSelection(device, device, device == sel);
1030
1031 return (uint) devices.size();
1032 }
1033};
1034
1036{
1037 public:
1038 explicit CommandPath(const CaptureCard &parent) :
1040 "videodevice"))
1041 {
1042 setLabel(QObject::tr(""));
1043 setValue("");
1044 setHelpText(QObject::tr("Specify the command to run, with any "
1045 "needed arguments."));
1046 };
1047
1048 ~CommandPath() override
1049 {
1050 delete GetStorage();
1051 }
1052};
1053
1055{
1056 public:
1057 explicit FileDevice(const CaptureCard &parent) :
1059 new CaptureCardDBStorage(this, parent, "videodevice")
1060 /* mustexist, false */)
1061 {
1062 setLabel(QObject::tr("File path"));
1063 };
1064
1065 ~FileDevice() override
1066 {
1067 delete GetStorage();
1068 }
1069};
1070
1072{
1073 public:
1074 explicit AudioDevice(const CaptureCard &parent) :
1075 CaptureCardComboBoxSetting(parent, true /* mustexist false */,
1076 "audiodevice")
1077 {
1078 setLabel(QObject::tr("Audio device"));
1079#if CONFIG_AUDIO_OSS
1080 QDir dev("/dev", "dsp*", QDir::Name, QDir::System);
1082 dev.setPath("/dev/sound");
1084#endif
1085#if CONFIG_AUDIO_ALSA
1086 addSelection("ALSA:default", "ALSA:default");
1087#endif
1088 addSelection(QObject::tr("(None)"), "NULL");
1089 setHelpText(QObject::tr("Device to read audio from, "
1090 "if audio is separate from the video."));
1091 };
1092};
1093
1095{
1096 public:
1097 // Handles integer milliseconds (compiler converts seconds to milliseconds)
1098 SignalTimeout(const CaptureCard &parent, std::chrono::milliseconds value,
1099 std::chrono::milliseconds min_val) :
1100 CaptureCardSpinBoxSetting(parent, min_val, 60s, 250ms, "signal_timeout")
1101 {
1102 setLabel(QObject::tr("Signal timeout (ms)"));
1103 setValueMs(value);
1104 setHelpText(QObject::tr(
1105 "Maximum time (in milliseconds) MythTV waits for "
1106 "a signal when scanning for channels."));
1107 };
1108 // Handle non-integer seconds
1109 template<typename T>
1110 SignalTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::duration<T> min_secs)
1111 requires (std::is_floating_point_v<T>) :
1112 SignalTimeout(parent, value, duration_cast<std::chrono::milliseconds>(min_secs)) {};
1113 template<typename T>
1114 SignalTimeout(const CaptureCard &parent, std::chrono::duration<T> value, std::chrono::duration<T> min_secs)
1115 requires (std::is_floating_point_v<T>) :
1116 SignalTimeout(parent,
1117 duration_cast<std::chrono::milliseconds>(value),
1118 duration_cast<std::chrono::milliseconds>(min_secs)) {};
1119};
1120
1122{
1123 public:
1124 // Handles integer milliseconds (compiler converts seconds to milliseconds)
1125 ChannelTimeout(const CaptureCard &parent, std::chrono::milliseconds value,
1126 std::chrono::milliseconds min_val) :
1127 CaptureCardSpinBoxSetting(parent, min_val, 65s, 250ms, "channel_timeout")
1128 {
1129 setLabel(QObject::tr("Tuning timeout (ms)"));
1130 setValueMs(value);
1131 setHelpText(QObject::tr(
1132 "Maximum time (in milliseconds) MythTV waits for "
1133 "a channel lock. For recordings, if this time is "
1134 "exceeded, the recording will be marked as failed."));
1135 };
1136 // Handle non-integer seconds
1137 template<typename T>
1138 ChannelTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::duration<T> min_secs)
1139 requires (std::is_floating_point_v<T>) :
1140 ChannelTimeout(parent, value, duration_cast<std::chrono::milliseconds>(min_secs)) {};
1141 template<typename T>
1142 ChannelTimeout(const CaptureCard &parent, std::chrono::duration<T> value, std::chrono::duration<T> min_secs)
1143 requires (std::is_floating_point_v<T>) :
1144 ChannelTimeout(parent, value, duration_cast<std::chrono::milliseconds>(min_secs)) {};
1145};
1146
1148{
1149 public:
1150 explicit AudioRateLimit(const CaptureCard &parent) :
1151 CaptureCardComboBoxSetting(parent, false, "audioratelimit")
1152 {
1153 setLabel(QObject::tr("Force audio sampling rate"));
1155 QObject::tr("If non-zero, override the audio sampling "
1156 "rate in the recording profile when this card is "
1157 "used. Use this if your capture card does not "
1158 "support all of the standard rates."));
1159 addSelection(QObject::tr("(None)"), "0");
1160 addSelection("32000");
1161 addSelection("44100");
1162 addSelection("48000");
1163 };
1164};
1165
1167{
1168 public:
1169 explicit SkipBtAudio(const CaptureCard &parent) :
1171 "skipbtaudio"))
1172 {
1173 setLabel(QObject::tr("Do not adjust volume"));
1175 QObject::tr("Enable this option for budget BT878 based "
1176 "DVB-T cards such as the AverTV DVB-T which "
1177 "require the audio volume to be left alone."));
1178 };
1179
1180 ~SkipBtAudio() override
1181 {
1182 delete GetStorage();
1183 }
1184};
1185
1187{
1188 public:
1189 explicit DVBCardNum(const CaptureCard &parent) :
1190 CaptureCardComboBoxSetting(parent, true, "videodevice")
1191 {
1192 setLabel(QObject::tr("DVB device"));
1194 QObject::tr("When you change this setting, the text below "
1195 "should change to the name and type of your card. "
1196 "If the card cannot be opened, an error message "
1197 "will be displayed."));
1198 fillSelections(QString());
1199 };
1200
1204 void fillSelections(const QString &current)
1205 {
1207
1208 // Get devices from filesystem
1209 QStringList sdevs = CardUtil::ProbeVideoDevices("DVB");
1210
1211 // Add current if needed
1212 if (!current.isEmpty() &&
1213#ifdef __cpp_lib_ranges_contains
1214 !std::ranges::contains(sdevs, current)
1215#else
1216 (std::ranges::find(sdevs, current) == sdevs.end())
1217#endif
1218 )
1219 {
1220 // QList doesn't always play well with std::ranges
1221 // NOLINTNEXTLINE(modernize-use-ranges)
1222 std::stable_sort(sdevs.begin(), sdevs.end());
1223 }
1224
1225 QStringList db = CardUtil::GetVideoDevices("DVB");
1226
1227 QMap<QString,bool> in_use;
1228 QString sel = current;
1229 for (const QString& dev : std::as_const(sdevs))
1230 {
1231 in_use[dev] = db.contains(dev);
1232 if (sel.isEmpty() && !in_use[dev])
1233 sel = dev;
1234 }
1235
1236 if (sel.isEmpty() && !sdevs.empty())
1237 sel = sdevs[0];
1238
1239 QString usestr = QString(" -- ");
1240 usestr += QObject::tr("Warning: already in use");
1241
1242 for (const QString& dev : std::as_const(sdevs))
1243 {
1244 QString desc = dev + (in_use[dev] ? usestr : "");
1245 desc = (current == dev) ? dev : desc;
1246 addSelection(desc, dev, dev == sel);
1247 }
1248 }
1249
1250 void Load(void) override // StandardSetting
1251 {
1253 addSelection(QString());
1254
1256
1258 fillSelections(dev);
1259 }
1260};
1261
1262// Use capturecard/inputname to store the delivery system selection of the card
1264{
1265 public:
1266 explicit DVBCardType(const CaptureCard &parent) :
1267 CaptureCardComboBoxSetting(parent, false, "inputname")
1268 {
1269 setLabel(QObject::tr("Delivery system"));
1271 QObject::tr("If your card supports more than one delivery system "
1272 "then you can select here the one that you want to use."));
1273 };
1274};
1275
1277{
1278 public:
1280 {
1281 setLabel(QObject::tr("Frontend ID"));
1283 QObject::tr("Identification string reported by the card. "
1284 "If the message \"Could not get card info...\" appears "
1285 "the card can be in use by another program."));
1286 };
1287};
1288
1290{
1291 public:
1292 explicit DVBNoSeqStart(const CaptureCard &parent) :
1294 new CaptureCardDBStorage(this, parent, "dvb_wait_for_seqstart"))
1295 {
1296 setLabel(QObject::tr("Wait for SEQ start header"));
1297 setValue(true);
1299 QObject::tr("If enabled, drop packets from the start of a DVB "
1300 "recording until a sequence start header is seen."));
1301 };
1302
1304 {
1305 delete GetStorage();
1306 }
1307};
1308
1310{
1311 public:
1312 explicit DVBOnDemand(const CaptureCard &parent) :
1314 new CaptureCardDBStorage(this, parent, "dvb_on_demand"))
1315 {
1316 setLabel(QObject::tr("Open DVB card on demand"));
1317 setValue(true);
1319 QObject::tr("If enabled, only open the DVB card when required, "
1320 "leaving it free for other programs at other times."));
1321 };
1322
1323 ~DVBOnDemand() override
1324 {
1325 delete GetStorage();
1326 }
1327};
1328
1330{
1331 public:
1332 explicit DVBEITScan(const CaptureCard &parent) :
1334 new CaptureCardDBStorage(this, parent, "dvb_eitscan"))
1335 {
1336 setLabel(QObject::tr("Use DVB card for active EIT scan"));
1337 setValue(true);
1339 QObject::tr("If enabled, activate active scanning for "
1340 "program data (EIT). When this option is enabled "
1341 "the DVB card is constantly in use."));
1342 };
1343
1344 ~DVBEITScan() override
1345 {
1346 delete GetStorage();
1347 }
1348};
1349
1351{
1352 public:
1353 explicit DVBTuningDelay(const CaptureCard &parent) :
1354 CaptureCardSpinBoxSetting(parent, 0ms, 2s, 25ms, "dvb_tuning_delay")
1355 {
1356 setLabel(QObject::tr("DVB tuning delay (ms)"));
1357 setValueMs(0ms);
1359 QObject::tr("Some Linux DVB drivers, in particular for the "
1360 "Hauppauge Nova-T, require that we slow down "
1361 "the tuning process by specifying a delay "
1362 "(in milliseconds)."));
1363 };
1364};
1365
1367{
1368 public:
1369 explicit FirewireGUID(const CaptureCard &parent) :
1370 CaptureCardComboBoxSetting(parent, false, "videodevice")
1371 {
1372 setLabel(QObject::tr("GUID"));
1373#if CONFIG_FIREWIRE
1374 std::vector<AVCInfo> list = FirewireDevice::GetSTBList();
1375 for (auto & i : list)
1376 {
1377 QString guid = i.GetGUIDString();
1378 m_guidToAvcInfo[guid] = i;
1379 addSelection(guid);
1380 }
1381#endif // CONFIG_FIREWIRE
1382 }
1383
1384 AVCInfo GetAVCInfo(const QString &guid) const
1385 { return m_guidToAvcInfo[guid]; }
1386
1387 private:
1388 QMap<QString,AVCInfo> m_guidToAvcInfo;
1389};
1390
1392 const FirewireGUID *_guid) :
1393 CaptureCardComboBoxSetting(parent, false, "firewire_model"),
1394 m_guid(_guid)
1395{
1396 setLabel(QObject::tr("Cable box model"));
1397 addSelection(QObject::tr("Motorola Generic"), "MOTO GENERIC");
1398 addSelection(QObject::tr("SA/Cisco Generic"), "SA GENERIC");
1399 addSelection("DCH-3200");
1400 addSelection("DCX-3200");
1401 addSelection("DCT-3412");
1402 addSelection("DCT-3416");
1403 addSelection("DCT-6200");
1404 addSelection("DCT-6212");
1405 addSelection("DCT-6216");
1406 addSelection("QIP-6200");
1407 addSelection("QIP-7100");
1408 addSelection("PACE-550");
1409 addSelection("PACE-779");
1410 addSelection("SA3250HD");
1411 addSelection("SA4200HD");
1412 addSelection("SA4250HDC");
1413 addSelection("SA8300HD");
1414 QString help = QObject::tr(
1415 "Choose the model that most closely resembles your set top box. "
1416 "Depending on firmware revision SA4200HD may work better for a "
1417 "SA3250HD box.");
1419}
1420
1421void FirewireModel::SetGUID([[maybe_unused]] const QString &_guid)
1422{
1423#if CONFIG_FIREWIRE
1424 AVCInfo info = m_guid->GetAVCInfo(_guid);
1425 QString model = FirewireDevice::GetModelName(info.m_vendorid, info.m_modelid);
1426 setValue(std::max(getValueIndex(model), 0));
1427#endif // CONFIG_FIREWIRE
1428}
1429
1430void FirewireDesc::SetGUID([[maybe_unused]] const QString &_guid)
1431{
1432 setLabel(tr("Description"));
1433
1434#if CONFIG_FIREWIRE
1435 QString name = m_guid->GetAVCInfo(_guid).m_product_name;
1436 name.replace("Scientific-Atlanta", "SA");
1437 name.replace(", Inc.", "");
1438 name.replace("Explorer(R)", "");
1439 name = name.simplified();
1440 setValue((name.isEmpty()) ? "" : name);
1441#endif // CONFIG_FIREWIRE
1442}
1443
1445{
1446 public:
1447 explicit FirewireConnection(const CaptureCard &parent) :
1449 "firewire_connection"))
1450 {
1451 setLabel(QObject::tr("Connection Type"));
1452 addSelection(QObject::tr("Point to Point"),"0");
1453 addSelection(QObject::tr("Broadcast"),"1");
1454 }
1455
1457 {
1458 delete GetStorage();
1459 }
1460};
1461
1463{
1464 public:
1465 explicit FirewireSpeed(const CaptureCard &parent) :
1467 "firewire_speed"))
1468 {
1469 setLabel(QObject::tr("Speed"));
1470 addSelection(QObject::tr("100Mbps"),"0");
1471 addSelection(QObject::tr("200Mbps"),"1");
1472 addSelection(QObject::tr("400Mbps"),"2");
1473 addSelection(QObject::tr("800Mbps"),"3");
1474 }
1475
1477 {
1478 delete GetStorage();
1479 }
1480};
1481
1482#if CONFIG_FIREWIRE
1483static void FirewireConfigurationGroup(CaptureCard& parent, CardType& cardtype)
1484{
1485 auto *dev(new FirewireGUID(parent));
1486 auto *desc(new FirewireDesc(dev));
1487 auto *model(new FirewireModel(parent, dev));
1488 cardtype.addTargetedChild("FIREWIRE", dev);
1489 cardtype.addTargetedChild("FIREWIRE", new EmptyAudioDevice(parent));
1490 cardtype.addTargetedChild("FIREWIRE", new EmptyVBIDevice(parent));
1491 cardtype.addTargetedChild("FIREWIRE", desc);
1492 cardtype.addTargetedChild("FIREWIRE", model);
1493
1494#if CONFIG_FIREWIRE_LINUX
1495 cardtype.addTargetedChild("FIREWIRE", new FirewireConnection(parent));
1496 cardtype.addTargetedChild("FIREWIRE", new FirewireSpeed(parent));
1497#endif // CONFIG_FIREWIRE_LINUX
1498
1499 cardtype.addTargetedChild("FIREWIRE", new SignalTimeout(parent, 2s, 1s));
1500 cardtype.addTargetedChild("FIREWIRE", new ChannelTimeout(parent, 9s, 1.75s));
1501
1502 model->SetGUID(dev->getValue());
1503 desc->SetGUID(dev->getValue());
1504 QObject::connect(dev, qOverload<const QString&>(&StandardSetting::valueChanged),
1505 model, &FirewireModel::SetGUID);
1506 QObject::connect(dev, qOverload<const QString&>(&StandardSetting::valueChanged),
1507 desc, &FirewireDesc::SetGUID);
1508}
1509#endif
1510
1511#if CONFIG_HDHOMERUN
1512
1513// -----------------------
1514// HDHomeRun Configuration
1515// -----------------------
1516
1517HDHomeRunDeviceID::HDHomeRunDeviceID(const CaptureCard &parent,
1518 HDHomeRunConfigurationGroup &_group) :
1520 new CaptureCardDBStorage(this, parent, "videodevice")),
1521 m_group(_group)
1522{
1523 setVisible(false);
1524};
1525
1526HDHomeRunDeviceID::~HDHomeRunDeviceID()
1527{
1528 delete GetStorage();
1529}
1530
1531void HDHomeRunDeviceID::Load(void)
1532{
1534 m_group.SetDeviceCheckBoxes(getValue());
1535}
1536
1537void HDHomeRunDeviceID::Save(void)
1538{
1539 setValue(m_group.GetDeviceCheckBoxes());
1541}
1542
1543class HDHomeRunEITScan : public MythUICheckBoxSetting
1544{
1545 public:
1546 explicit HDHomeRunEITScan(const CaptureCard &parent) :
1548 new CaptureCardDBStorage(this, parent, "dvb_eitscan"))
1549 {
1550 setLabel(QObject::tr("Use HDHomeRun for active EIT scan"));
1551 setValue(true);
1553 QObject::tr("If enabled, activate active scanning for "
1554 "program data (EIT). When this option is enabled "
1555 "the HDHomeRun is constantly in use."));
1556 };
1557
1558 ~HDHomeRunEITScan() override
1559 {
1560 delete GetStorage();
1561 }
1562};
1563
1564
1565class UseHDHomeRunDevice : public TransMythUICheckBoxSetting
1566{
1567 public:
1568 explicit UseHDHomeRunDevice(QString &deviceid, QString &model,
1569 QString &ipaddr)
1570 {
1571 setLabel(QObject::tr("Use HDHomeRun %1 (%2 %3)")
1572 .arg(deviceid, model, ipaddr));
1573 setValue(false);
1575 QObject::tr("If enabled, use tuners from this HDHomeRun "
1576 "device."));
1577 };
1578};
1579
1580HDHomeRunConfigurationGroup::HDHomeRunConfigurationGroup
1581 (CaptureCard& a_parent, CardType &a_cardtype) :
1582 m_parent(a_parent),
1583 m_deviceId(new HDHomeRunDeviceID(a_parent, *this))
1584{
1585 setVisible(false);
1586
1587 // Fill Device list
1588 FillDeviceList();
1589
1590 QMap<QString, HDHomeRunDevice>::iterator dit;
1591 for (dit = m_deviceList.begin(); dit != m_deviceList.end(); ++dit)
1592 {
1593 HDHomeRunDevice &dev = *dit;
1594 dev.m_checkbox = new UseHDHomeRunDevice(
1595 dev.m_deviceId, dev.m_model, dev.m_cardIp);
1596 a_cardtype.addTargetedChild("HDHOMERUN", dev.m_checkbox);
1597 }
1598 a_cardtype.addTargetedChild("HDHOMERUN", new EmptyAudioDevice(m_parent));
1599 a_cardtype.addTargetedChild("HDHOMERUN", new EmptyVBIDevice(m_parent));
1600 a_cardtype.addTargetedChild("HDHOMERUN", m_deviceId);
1601
1602 auto *buttonRecOpt = new GroupSetting();
1603 buttonRecOpt->setLabel(tr("Recording Options"));
1604 buttonRecOpt->addChild(new SignalTimeout(m_parent, 3s, 0.25s));
1605 buttonRecOpt->addChild(new ChannelTimeout(m_parent, 6s, 1.75s));
1606 buttonRecOpt->addChild(new HDHomeRunEITScan(m_parent));
1607 a_cardtype.addTargetedChild("HDHOMERUN", buttonRecOpt);
1608};
1609
1610void HDHomeRunConfigurationGroup::FillDeviceList(void)
1611{
1612 m_deviceList.clear();
1613
1614 // Find physical devices first
1615 // ProbeVideoDevices returns "deviceid ip" pairs
1616 QStringList devs = CardUtil::ProbeVideoDevices("HDHOMERUN");
1617
1618 for (const auto & dev : std::as_const(devs))
1619 {
1620 QStringList devinfo = dev.split(" ");
1621 const QString& devid = devinfo.at(0);
1622 const QString& devip = devinfo.at(1);
1623 const QString& model = devinfo.at(2);
1624
1625 HDHomeRunDevice tmpdevice;
1626 tmpdevice.m_model = model;
1627 tmpdevice.m_cardIp = devip;
1628 tmpdevice.m_deviceId = devid;
1629 // Fully specify object. Checkboxes will be added later when
1630 // the configuration group is created.
1631 tmpdevice.m_checkbox = nullptr;
1632 m_deviceList[tmpdevice.m_deviceId] = tmpdevice;
1633 }
1634
1635#if 0
1636 // Debug dump of cards
1637 QMap<QString, HDHomeRunDevice>::iterator debugit;
1638 for (debugit = m_deviceList.begin(); debugit != m_deviceList.end(); ++debugit)
1639 {
1640 LOG(VB_GENERAL, LOG_DEBUG, QString("%1: %2 %3")
1641 .arg(debugit.key()).arg((*debugit).model)
1642 .arg((*debugit).cardip));
1643 }
1644#endif
1645}
1646
1647void HDHomeRunConfigurationGroup::SetDeviceCheckBoxes(const QString& devices)
1648{
1649 QStringList devstrs = devices.split(",");
1650 for (const QString& devstr : std::as_const(devstrs))
1651 {
1652 // Get the HDHomeRun device ID using libhdhomerun. We need to
1653 // do it this way because legacy configurations could use an
1654 // IP address and a tuner nubmer.
1655 QByteArray ba = devstr.toUtf8();
1656 hdhomerun_device_t *device = hdhomerun_device_create_from_str(
1657 ba.data(), nullptr);
1658 if (!device)
1659 continue;
1660 QString devid = QString("%1").arg(
1661 hdhomerun_device_get_device_id(device), 8, 16).toUpper();
1662 hdhomerun_device_destroy(device);
1663
1664 // If we know about this device, set its checkbox to on.
1665 QMap<QString, HDHomeRunDevice>::iterator dit;
1666 dit = m_deviceList.find(devid);
1667 if (dit != m_deviceList.end())
1668 (*dit).m_checkbox->setValue(true);
1669 }
1670}
1671
1672QString HDHomeRunConfigurationGroup::GetDeviceCheckBoxes(void)
1673{
1674 // Return a string listing each HDHomeRun device with its checbox
1675 // turned on.
1676 QStringList devstrs;
1677 QMap<QString, HDHomeRunDevice>::iterator dit;
1678 for (dit = m_deviceList.begin(); dit != m_deviceList.end(); ++dit)
1679 {
1680 if ((*dit).m_checkbox->boolValue())
1681 devstrs << (*dit).m_deviceId;
1682 }
1683 QString devices = devstrs.join(",");
1684 return devices;
1685}
1686
1687#endif
1688
1689// -----------------------
1690// VBOX Configuration
1691// -----------------------
1692
1694{
1695 setLabel(QObject::tr("IP Address"));
1696 setHelpText(QObject::tr("Device IP or ID of a VBox device. eg. '192.168.1.100' or 'vbox_3718'"));
1697 VBoxIP::setEnabled(false);
1698 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
1699 this, &VBoxIP::UpdateDevices);
1700};
1701
1703{
1705 if (e)
1706 {
1707 if (!m_oldValue.isEmpty())
1709 emit NewIP(getValue());
1710 }
1711 else
1712 {
1713 m_oldValue = getValue();
1714 }
1715}
1716
1717void VBoxIP::UpdateDevices(const QString &v)
1718{
1719 if (isEnabled())
1720 emit NewIP(v);
1721}
1722
1724{
1725 setLabel(QObject::tr("Tuner"));
1726 setHelpText(QObject::tr("Number and type of the tuner to use. eg '1-DVBT/T2'."));
1728 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
1730};
1731
1733{
1735 if (e) {
1736 if (!m_oldValue.isEmpty())
1738 emit NewTuner(getValue());
1739 }
1740 else
1741 {
1742 m_oldValue = getValue();
1743 }
1744}
1745
1746void VBoxTunerIndex::UpdateDevices(const QString &v)
1747{
1748 if (isEnabled())
1749 emit NewTuner(v);
1750}
1751
1753 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, "videodevice"))
1754{
1755 setLabel(tr("Device ID"));
1756 setHelpText(tr("Device ID of VBox device"));
1757 setReadOnly(true);
1758}
1759
1761{
1762 delete GetStorage();
1763}
1764
1765void VBoxDeviceID::SetIP(const QString &ip)
1766{
1767 m_ip = ip;
1768 setValue(QString("%1-%2").arg(m_ip, m_tuner));
1769}
1770
1771void VBoxDeviceID::SetTuner(const QString &tuner)
1772{
1773 m_tuner = tuner;
1774 setValue(QString("%1-%2").arg(m_ip, m_tuner));
1775}
1776
1777void VBoxDeviceID::SetOverrideDeviceID(const QString &deviceid)
1778{
1779 m_overrideDeviceId = deviceid;
1780 setValue(deviceid);
1781}
1782
1784{
1785 GetStorage()->Load();
1786 if (!m_overrideDeviceId.isEmpty())
1787 {
1789 m_overrideDeviceId.clear();
1790 }
1791}
1792
1794 VBoxDeviceID *deviceid,
1795 StandardSetting *desc,
1796 VBoxIP *cardip,
1797 VBoxTunerIndex *cardtuner,
1798 VBoxDeviceList *devicelist,
1799 const CaptureCard &parent) :
1800 m_deviceId(deviceid),
1801 m_desc(desc),
1802 m_cardIp(cardip),
1803 m_cardTuner(cardtuner),
1804 m_deviceList(devicelist),
1805 m_parent(parent)
1806{
1807 setLabel(QObject::tr("Available devices"));
1809 QObject::tr(
1810 "Device IP or ID, tuner number and tuner type of available VBox devices."));
1811
1812 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
1814};
1815
1817void VBoxDeviceIDList::fillSelections(const QString &cur)
1818{
1820
1821 std::vector<QString> devs;
1822 QMap<QString, bool> in_use;
1823
1824 const QString& current = cur;
1825
1826 for (auto it = m_deviceList->begin(); it != m_deviceList->end(); ++it)
1827 {
1828 devs.push_back(it.key());
1829 in_use[it.key()] = (*it).m_inUse;
1830 }
1831
1832 QString man_addr = VBoxDeviceIDList::tr("Manually Enter IP Address");
1833 QString sel = man_addr;
1834 devs.push_back(sel);
1835
1836 for (const auto & dev : devs)
1837 sel = (current == dev) ? dev : sel;
1838
1839 QString usestr = QString(" -- ");
1840 usestr += QObject::tr("Warning: already in use");
1841
1842 for (const auto & dev : devs)
1843 {
1844 QString desc = dev + (in_use[dev] ? usestr : "");
1845 addSelection(desc, dev, dev == sel);
1846 }
1847
1848 if (current != cur)
1849 {
1851 }
1852 else if (sel == man_addr && !current.isEmpty())
1853 {
1854 // Populate the proper values for IP address and tuner
1855 QStringList selection = current.split("-");
1856
1857 m_cardIp->SetOldValue(selection.first());
1858 m_cardTuner->SetOldValue(selection.last());
1859
1860 m_cardIp->setValue(selection.first());
1861 m_cardTuner->setValue(selection.last());
1862 }
1863}
1864
1866{
1868
1869 int cardid = m_parent.getCardID();
1870 QString device = CardUtil::GetVideoDevice(cardid);
1871 fillSelections(device);
1872}
1873
1875{
1876 if (v == VBoxDeviceIDList::tr("Manually Enter IP Address"))
1877 {
1878 m_cardIp->setEnabled(true);
1879 m_cardTuner->setEnabled(true);
1880 }
1881 else if (!v.isEmpty())
1882 {
1883 if (m_oldValue == VBoxDeviceIDList::tr("Manually Enter IP Address"))
1884 {
1885 m_cardIp->setEnabled(false);
1886 m_cardTuner->setEnabled(false);
1887 }
1888 m_deviceId->setValue(v);
1889
1890 // Update _cardip and _cardtuner
1892 m_cardTuner->setValue(QString("%1").arg((*m_deviceList)[v].m_tunerNo));
1894 }
1895 m_oldValue = v;
1896};
1897
1898// -----------------------
1899// IPTV Configuration
1900// -----------------------
1901
1903{
1904 public:
1905 explicit IPTVHost(const CaptureCard &parent) :
1906 CaptureCardTextEditSetting(parent, "videodevice")
1907 {
1908 setValue("http://mafreebox.freebox.fr/freeboxtv/playlist.m3u");
1909 setLabel(QObject::tr("M3U URL"));
1911 QObject::tr("URL of M3U file containing RTSP/RTP/UDP/HTTP channel URLs,"
1912 " example for HDHomeRun: http://<ipv4>/lineup.m3u and for Freebox:"
1913 " http://mafreebox.freebox.fr/freeboxtv/playlist.m3u."
1914 ));
1915 }
1916};
1917
1918static void IPTVConfigurationGroup(CaptureCard& parent, CardType& cardType)
1919{
1920 cardType.addTargetedChild("FREEBOX", new IPTVHost(parent));
1921 cardType.addTargetedChild("FREEBOX", new ChannelTimeout(parent, 30s, 1.75s));
1922 cardType.addTargetedChild("FREEBOX", new EmptyAudioDevice(parent));
1923 cardType.addTargetedChild("FREEBOX", new EmptyVBIDevice(parent));
1924}
1925
1927{
1928 public:
1929 explicit ASIDevice(const CaptureCard &parent) :
1930 CaptureCardComboBoxSetting(parent, true, "videodevice")
1931 {
1932 setLabel(QObject::tr("ASI device"));
1933 fillSelections(QString());
1934 };
1935
1939 void fillSelections(const QString &current)
1940 {
1942
1943 // Get devices from filesystem
1944 QStringList sdevs = CardUtil::ProbeVideoDevices("ASI");
1945
1946 // Add current if needed
1947 if (!current.isEmpty() && !sdevs.contains(current))
1948 {
1949 // QList doesn't always play well with std::ranges
1950 // NOLINTNEXTLINE(modernize-use-ranges)
1951 std::stable_sort(sdevs.begin(), sdevs.end());
1952 }
1953
1954 // Get devices from DB
1955 QStringList db = CardUtil::GetVideoDevices("ASI");
1956
1957 // Figure out which physical devices are already in use
1958 // by another card defined in the DB, and select a device
1959 // for new configs (preferring non-conflicing devices).
1960 QMap<QString,bool> in_use;
1961 QString sel = current;
1962 for (const QString& dev : std::as_const(sdevs))
1963 {
1964 in_use[dev] = db.contains(dev);
1965 if (sel.isEmpty() && !in_use[dev])
1966 sel = dev;
1967 }
1968
1969 // Unfortunately all devices are conflicted, select first device.
1970 if (sel.isEmpty() && !sdevs.empty())
1971 sel = sdevs[0];
1972
1973 QString usestr = QString(" -- ");
1974 usestr += QObject::tr("Warning: already in use");
1975
1976 // Add the devices to the UI
1977 bool found = false;
1978 for (const QString& dev : std::as_const(sdevs))
1979 {
1980 QString desc = dev + (in_use[dev] ? usestr : "");
1981 desc = (current == dev) ? dev : desc;
1982 addSelection(desc, dev, dev == sel);
1983 found |= (dev == sel);
1984 }
1985
1986 // If a configured device isn't on the list, add it with warning
1987 if (!found && !current.isEmpty())
1988 {
1989 QString desc = current + " -- " +
1990 QObject::tr("Warning: unable to open");
1991 addSelection(desc, current, true);
1992 }
1993 }
1994
1995 void Load(void) override // StandardSetting
1996 {
1998 addSelection(QString());
1999 GetStorage()->Load();
2001 }
2002};
2003
2005 CardType &cardType):
2006 m_parent(a_parent),
2007 m_device(new ASIDevice(m_parent)),
2008 m_cardInfo(new TransTextEditSetting())
2009{
2010 setVisible(false);
2011 m_cardInfo->setLabel(tr("Status"));
2012 m_cardInfo->setEnabled(false);
2013
2014 cardType.addTargetedChild("ASI", m_device);
2015 cardType.addTargetedChild("ASI", new EmptyAudioDevice(m_parent));
2016 cardType.addTargetedChild("ASI", new EmptyVBIDevice(m_parent));
2017 cardType.addTargetedChild("ASI", m_cardInfo);
2018
2019 connect(m_device, qOverload<const QString&>(&StandardSetting::valueChanged),
2021
2023};
2024
2025void ASIConfigurationGroup::probeCard([[maybe_unused]] const QString &device)
2026{
2027#if CONFIG_ASI
2028 if (device.isEmpty())
2029 {
2030 m_cardInfo->setValue("");
2031 return;
2032 }
2033
2034 if ((m_parent.getCardID() != 0) && m_parent.GetRawCardType() != "ASI")
2035 {
2036 m_cardInfo->setValue("");
2037 return;
2038 }
2039
2040 QString error;
2041 int device_num = CardUtil::GetASIDeviceNumber(device, &error);
2042 if (device_num < 0)
2043 {
2044 m_cardInfo->setValue(tr("Not a valid DVEO ASI card"));
2045 LOG(VB_GENERAL, LOG_WARNING,
2046 "ASIConfigurationGroup::probeCard(), Warning: " + error);
2047 return;
2048 }
2049 m_cardInfo->setValue(tr("Valid DVEO ASI card"));
2050#else
2051 m_cardInfo->setValue(QString("Not compiled with ASI support"));
2052#endif
2053}
2054
2056 CardType& a_cardtype):
2057 m_parent(a_parent),
2058 m_info(new GroupSetting()), m_size(new GroupSetting())
2059{
2060 setVisible(false);
2061 auto *device = new FileDevice(m_parent);
2062 device->setHelpText(tr("A local file used to simulate a recording."
2063 " Leave empty to use MythEvents to trigger an"
2064 " external program to import recording files."));
2065 a_cardtype.addTargetedChild("IMPORT", device);
2066
2067 a_cardtype.addTargetedChild("IMPORT", new EmptyAudioDevice(m_parent));
2068 a_cardtype.addTargetedChild("IMPORT", new EmptyVBIDevice(m_parent));
2069
2070 m_info->setLabel(tr("File info"));
2071 a_cardtype.addTargetedChild("IMPORT", m_info);
2072
2073 m_size->setLabel(tr("File size"));
2074 a_cardtype.addTargetedChild("IMPORT", m_size);
2075
2076 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2078
2079 probeCard(device->getValue());
2080};
2081
2082void ImportConfigurationGroup::probeCard(const QString &device)
2083{
2084 QString ci;
2085 QString cs;
2086 QFileInfo fileInfo(device);
2087
2088 // For convenience, ImportRecorder allows both formats:
2089 if (device.startsWith("file:", Qt::CaseInsensitive))
2090 fileInfo.setFile(device.mid(5));
2091
2092 if (fileInfo.exists())
2093 {
2094 if (fileInfo.isReadable() && (fileInfo.isFile()))
2095 {
2096 ci = HTTPRequest::TestMimeType(fileInfo.absoluteFilePath());
2097 cs = tr("%1 MB").arg(fileInfo.size() / 1024 / 1024);
2098 }
2099 else
2100 {
2101 ci = tr("File not readable");
2102 }
2103 }
2104 else
2105 {
2106 ci = tr("File %1 does not exist").arg(device);
2107 }
2108
2109 m_info->setValue(ci);
2110 m_size->setValue(cs);
2111}
2112
2113// -----------------------
2114// VBox Configuration
2115// -----------------------
2116
2118 (CaptureCard& a_parent, CardType& a_cardtype) :
2119 m_parent(a_parent),
2120 m_desc(new GroupSetting()),
2121 m_deviceId(new VBoxDeviceID(a_parent)),
2122 m_cardIp(new VBoxIP()),
2123 m_cardTuner(new VBoxTunerIndex())
2124{
2125 setVisible(false);
2126
2127 // Fill Device list
2129
2130 m_desc->setLabel(tr("Description"));
2133
2134 a_cardtype.addTargetedChild("VBOX", m_deviceIdList);
2135 a_cardtype.addTargetedChild("VBOX", new EmptyAudioDevice(m_parent));
2136 a_cardtype.addTargetedChild("VBOX", new EmptyVBIDevice(m_parent));
2137 a_cardtype.addTargetedChild("VBOX", m_deviceId);
2138 a_cardtype.addTargetedChild("VBOX", m_desc);
2139 a_cardtype.addTargetedChild("VBOX", m_cardIp);
2140 a_cardtype.addTargetedChild("VBOX", m_cardTuner);
2141 a_cardtype.addTargetedChild("VBOX", new SignalTimeout(m_parent, 7s, 1s));
2142 a_cardtype.addTargetedChild("VBOX", new ChannelTimeout(m_parent, 10s, 1.75s));
2143
2144 connect(m_cardIp, &VBoxIP::NewIP,
2148};
2149
2151{
2152 m_deviceList.clear();
2153
2154 // Find physical devices first
2155 // ProbeVideoDevices returns "deviceid ip tunerno tunertype"
2156 QStringList devs = CardUtil::ProbeVideoDevices("VBOX");
2157
2158 for (const auto & dev : std::as_const(devs))
2159 {
2160 QStringList devinfo = dev.split(" ");
2161 const QString& id = devinfo.at(0);
2162 const QString& ip = devinfo.at(1);
2163 const QString& tunerNo = devinfo.at(2);
2164 const QString& tunerType = devinfo.at(3);
2165
2166 VBoxDevice tmpdevice;
2167 tmpdevice.m_deviceId = id;
2168 tmpdevice.m_desc = CardUtil::GetVBoxdesc(id, ip, tunerNo, tunerType);
2169 tmpdevice.m_cardIp = ip;
2170 tmpdevice.m_inUse = false;
2171 tmpdevice.m_discovered = true;
2172 tmpdevice.m_tunerNo = tunerNo;
2173 tmpdevice.m_tunerType = tunerType;
2174 tmpdevice.m_mythDeviceId = id + "-" + tunerNo + "-" + tunerType;
2175 m_deviceList[tmpdevice.m_mythDeviceId] = tmpdevice;
2176 }
2177
2178 // Now find configured devices
2179
2180 // returns "ip.ip.ip.ip-n-type" or deviceid-n-type values
2181 QStringList db = CardUtil::GetVideoDevices("VBOX");
2182
2183 for (const auto & dev : std::as_const(db))
2184 {
2185 QMap<QString, VBoxDevice>::iterator dit;
2186 dit = m_deviceList.find(dev);
2187
2188 if (dit != m_deviceList.end())
2189 (*dit).m_inUse = true;
2190 }
2191}
2192
2193// -----------------------
2194// Ceton Configuration
2195// -----------------------
2196#if CONFIG_CETON
2197CetonSetting::CetonSetting(QString label, const QString& helptext)
2198{
2199 setLabel(std::move(label));
2200 setHelpText(helptext);
2201 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
2202 this, &CetonSetting::UpdateDevices);
2203}
2204
2205void CetonSetting::UpdateDevices(const QString &v)
2206{
2207 if (isEnabled())
2208 emit NewValue(v);
2209}
2210
2211void CetonSetting::LoadValue(const QString &value)
2212{
2213 setValue(value);
2214}
2215
2216CetonDeviceID::CetonDeviceID(const CaptureCard &parent) :
2217 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, "videodevice")),
2218 m_parent(parent)
2219{
2220 setLabel(tr("Device ID"));
2221 setHelpText(tr("Device ID of Ceton device"));
2222}
2223
2224CetonDeviceID::~CetonDeviceID()
2225{
2226 delete GetStorage();
2227}
2228
2229void CetonDeviceID::SetIP(const QString &ip)
2230{
2231 static const QRegularExpression ipV4Regex
2232 { "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){4}$" };
2233 auto match = ipV4Regex.match(ip + ".");
2234 if (match.hasMatch())
2235 {
2236 m_ip = ip;
2237 setValue(QString("%1-RTP.%3").arg(m_ip, m_tuner));
2238 }
2239}
2240
2241void CetonDeviceID::SetTuner(const QString &tuner)
2242{
2243 static const QRegularExpression oneDigit { "^\\d$" };
2244 auto match = oneDigit.match(tuner);
2245 if (match.hasMatch())
2246 {
2247 m_tuner = tuner;
2248 setValue(QString("%1-RTP.%2").arg(m_ip, m_tuner));
2249 }
2250}
2251
2252void CetonDeviceID::Load(void)
2253{
2254 GetStorage()->Load();
2255 UpdateValues();
2256}
2257
2258void CetonDeviceID::UpdateValues(void)
2259{
2260 static const QRegularExpression newstyle { R"(^([0-9.]+)-(\d|RTP)\.(\d)$)" };
2261 auto match = newstyle.match(getValue());
2262 if (match.hasMatch())
2263 {
2264 emit LoadedIP(match.captured(1));
2265 emit LoadedTuner(match.captured(3));
2266 }
2267}
2268
2269void CetonSetting::CetonConfigurationGroup(CaptureCard& parent, CardType& cardtype)
2270{
2271 auto *deviceid = new CetonDeviceID(parent);
2272 auto *desc = new GroupSetting();
2273 desc->setLabel(tr("CetonConfigurationGroup", "Description"));
2274 auto *ip = new CetonSetting(tr("IP Address"),
2275 tr("IP Address of the Ceton device (192.168.200.1 by default)"));
2276 auto *tuner = new CetonSetting(tr("Tuner"),
2277 tr("Number of the tuner on the Ceton device (first tuner is number 0)"));
2278
2279 cardtype.addTargetedChild("CETON", ip);
2280 cardtype.addTargetedChild("CETON", tuner);
2281 cardtype.addTargetedChild("CETON", deviceid);
2282 cardtype.addTargetedChild("CETON", desc);
2283 cardtype.addTargetedChild("CETON", new SignalTimeout(parent, 1s, 0.25s));
2284 cardtype.addTargetedChild("CETON", new ChannelTimeout(parent, 3s, 1.75s));
2285
2286 QObject::connect(ip, &CetonSetting::NewValue,
2287 deviceid, &CetonDeviceID::SetIP);
2288 QObject::connect(tuner, &CetonSetting::NewValue,
2289 deviceid, &CetonDeviceID::SetTuner);
2290
2291 QObject::connect(deviceid, &CetonDeviceID::LoadedIP,
2292 ip, &CetonSetting::LoadValue);
2293 QObject::connect(deviceid, &CetonDeviceID::LoadedTuner,
2294 tuner, &CetonSetting::LoadValue);
2295}
2296#endif
2297
2298// Override database schema default, set schedgroup false
2300{
2301 public:
2302 explicit SchedGroupFalse(const CaptureCard &parent) :
2304 "schedgroup"))
2305 {
2306 setValue(false);
2307 setVisible(false);
2308 };
2309
2311 {
2312 delete GetStorage();
2313 }
2314};
2315
2317 CardType& cardtype,
2318 const QString &inputtype) :
2319 m_parent(parent),
2320 m_cardInfo(new GroupSetting()),
2321 m_vbiDev(new VBIDevice(m_parent))
2322{
2323 setVisible(false);
2324 QRegularExpression drv { "^(?!ivtv|hdpvr|(saa7164(.*))).*$" };
2325 auto *device = new VideoDevice(m_parent, 0, 15, QString(), drv);
2326
2327 m_cardInfo->setLabel(tr("Probed info"));
2328 m_cardInfo->setReadOnly(true);
2329
2330 cardtype.addTargetedChild(inputtype, device);
2331 cardtype.addTargetedChild(inputtype, m_cardInfo);
2332 cardtype.addTargetedChild(inputtype, m_vbiDev);
2333 cardtype.addTargetedChild(inputtype, new AudioDevice(m_parent));
2334 cardtype.addTargetedChild(inputtype, new AudioRateLimit(m_parent));
2335 cardtype.addTargetedChild(inputtype, new SkipBtAudio(m_parent));
2336
2337 // Override database schema default, set schedgroup false
2338 cardtype.addTargetedChild(inputtype, new SchedGroupFalse(m_parent));
2339
2340 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2342
2343 probeCard(device->getValue());
2344};
2345
2346void V4LConfigurationGroup::probeCard(const QString &device)
2347{
2348 QString cn = tr("Failed to open");
2349 QString ci = cn;
2350 QString dn;
2351
2352 QByteArray adevice = device.toLatin1();
2353 int videofd = open(adevice.constData(), O_RDWR);
2354 if (videofd >= 0)
2355 {
2356 if (!CardUtil::GetV4LInfo(videofd, cn, dn))
2357 ci = cn = tr("Failed to probe");
2358 else if (!dn.isEmpty())
2359 ci = cn + " [" + dn + "]";
2360 close(videofd);
2361 }
2362
2363 m_cardInfo->setValue(ci);
2364 m_vbiDev->setFilter(cn, dn);
2365}
2366
2368 CardType &cardtype) :
2369 m_parent(parent),
2370 m_vbiDevice(new VBIDevice(parent)),
2371 m_cardInfo(new GroupSetting())
2372{
2373 setVisible(false);
2374 QRegularExpression drv { "^(ivtv|(saa7164(.*)))$" };
2375 m_device = new VideoDevice(m_parent, 0, 15, QString(), drv);
2376 m_vbiDevice->setVisible(false);
2377
2378 m_cardInfo->setLabel(tr("Probed info"));
2379 m_cardInfo->setReadOnly(true);
2380
2381 cardtype.addTargetedChild("MPEG", m_device);
2382 cardtype.addTargetedChild("MPEG", m_vbiDevice);
2383 cardtype.addTargetedChild("MPEG", m_cardInfo);
2384 cardtype.addTargetedChild("MPEG", new ChannelTimeout(m_parent, 12s, 2s));
2385
2386 // Override database schema default, set schedgroup false
2387 cardtype.addTargetedChild("MPEG", new SchedGroupFalse(m_parent));
2388
2389 connect(m_device, qOverload<const QString&>(&StandardSetting::valueChanged),
2391
2393}
2394
2395void MPEGConfigurationGroup::probeCard(const QString &device)
2396{
2397 QString cn = tr("Failed to open");
2398 QString ci = cn;
2399 QString dn;
2400
2401 QByteArray adevice = device.toLatin1();
2402 int videofd = open(adevice.constData(), O_RDWR);
2403 if (videofd >= 0)
2404 {
2405 if (!CardUtil::GetV4LInfo(videofd, cn, dn))
2406 ci = cn = tr("Failed to probe");
2407 else if (!dn.isEmpty())
2408 ci = cn + " [" + dn + "]";
2409 close(videofd);
2410 }
2411
2412 m_cardInfo->setValue(ci);
2413 m_vbiDevice->setVisible(dn!="ivtv");
2414 m_vbiDevice->setFilter(cn, dn);
2415}
2416
2418 CardType &a_cardtype) :
2419 m_parent(a_parent),
2420 m_info(new GroupSetting()), m_size(new GroupSetting())
2421{
2422 setVisible(false);
2423 auto *device = new FileDevice(m_parent);
2424 device->setHelpText(tr("A local MPEG file used to simulate a recording."));
2425
2426 a_cardtype.addTargetedChild("DEMO", device);
2427
2428 a_cardtype.addTargetedChild("DEMO", new EmptyAudioDevice(m_parent));
2429 a_cardtype.addTargetedChild("DEMO", new EmptyVBIDevice(m_parent));
2430
2431 m_info->setLabel(tr("File info"));
2432 a_cardtype.addTargetedChild("DEMO", m_info);
2433
2434 m_size->setLabel(tr("File size"));
2435 a_cardtype.addTargetedChild("DEMO", m_size);
2436
2437 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2439
2440 probeCard(device->getValue());
2441}
2442
2443void DemoConfigurationGroup::probeCard(const QString &device)
2444{
2445 QString ci;
2446 QString cs;
2447 QFileInfo fileInfo(device);
2448 if (fileInfo.exists())
2449 {
2450 if (fileInfo.isReadable() && (fileInfo.isFile()))
2451 {
2452 ci = HTTPRequest::TestMimeType(fileInfo.absoluteFilePath());
2453 cs = tr("%1 MB").arg(fileInfo.size() / 1024 / 1024);
2454 }
2455 else
2456 {
2457 ci = tr("File not readable");
2458 }
2459 }
2460 else
2461 {
2462 ci = tr("File does not exist");
2463 }
2464
2465 m_info->setValue(ci);
2466 m_size->setValue(cs);
2467}
2468
2469#ifndef Q_OS_WINDOWS
2470ExternalConfigurationGroup::ExternalConfigurationGroup(CaptureCard &a_parent,
2471 CardType &a_cardtype) :
2472 m_parent(a_parent),
2473 m_info(new GroupSetting())
2474{
2475 setVisible(false);
2476 auto *device = new CommandPath(m_parent);
2477 device->setLabel(tr("Command path"));
2478 device->setHelpText(tr("A 'black box' application controlled via stdin, status on "
2479 "stderr and TransportStream read from stdout.\n"
2480 "Use absolute path or path relative to the current directory."));
2481 a_cardtype.addTargetedChild("EXTERNAL", device);
2482
2483 m_info->setLabel(tr("File info"));
2484 a_cardtype.addTargetedChild("EXTERNAL", m_info);
2485
2486 a_cardtype.addTargetedChild("EXTERNAL",
2487 new ChannelTimeout(m_parent, 20s, 1.75s));
2488
2489 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2490 this, &ExternalConfigurationGroup::probeApp);
2491
2492 probeApp(device->getValue());
2493}
2494
2495void ExternalConfigurationGroup::probeApp(const QString & path)
2496{
2497 int idx1 = path.startsWith("file:", Qt::CaseInsensitive) ? 5 : 0;
2498 int idx2 = path.indexOf(' ', idx1);
2499
2500 QString ci;
2501 QFileInfo fileInfo(path.mid(idx1, idx2 - idx1));
2502
2503 if (fileInfo.exists())
2504 {
2505 ci = tr("File '%1' is valid.").arg(fileInfo.absoluteFilePath());
2506 if (!fileInfo.isReadable() || !fileInfo.isFile())
2507 ci = tr("WARNING: File '%1' is not readable.")
2508 .arg(fileInfo.absoluteFilePath());
2509 if (!fileInfo.isExecutable())
2510 ci = tr("WARNING: File '%1' is not executable.")
2511 .arg(fileInfo.absoluteFilePath());
2512 }
2513 else
2514 {
2515 ci = tr("WARNING: File '%1' does not exist.")
2516 .arg(fileInfo.absoluteFilePath());
2517 }
2518
2519 m_info->setValue(ci);
2520 m_info->setHelpText(ci);
2521}
2522#endif // !defined( Q_OS_WINDOWS )
2523
2525 CardType &a_cardtype) :
2526 m_parent(a_parent), m_cardInfo(new GroupSetting()),
2527 m_audioInput(new TunerCardAudioInput(m_parent, QString(), "HDPVR"))
2528{
2529 setVisible(false);
2530
2531 auto *device = new VideoDevice(m_parent, 0, 15, QString(),
2532 QRegularExpression("^hdpvr$"));
2533
2534 m_cardInfo->setLabel(tr("Probed info"));
2535 m_cardInfo->setReadOnly(true);
2536
2537 a_cardtype.addTargetedChild("HDPVR", device);
2538 a_cardtype.addTargetedChild("HDPVR", new EmptyAudioDevice(m_parent));
2539 a_cardtype.addTargetedChild("HDPVR", new EmptyVBIDevice(m_parent));
2540 a_cardtype.addTargetedChild("HDPVR", m_cardInfo);
2541 a_cardtype.addTargetedChild("HDPVR", m_audioInput);
2542 a_cardtype.addTargetedChild("HDPVR", new ChannelTimeout(m_parent, 15s, 2s));
2543
2544 // Override database schema default, set schedgroup false
2545 a_cardtype.addTargetedChild("HDPVR", new SchedGroupFalse(m_parent));
2546
2547 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2549
2550 probeCard(device->getValue());
2551}
2552
2553void HDPVRConfigurationGroup::probeCard(const QString &device)
2554{
2555 QString cn = tr("Failed to open");
2556 QString ci = cn;
2557 QString dn;
2558
2559 int videofd = open(device.toLocal8Bit().constData(), O_RDWR);
2560 if (videofd >= 0)
2561 {
2562 if (!CardUtil::GetV4LInfo(videofd, cn, dn))
2563 ci = cn = tr("Failed to probe");
2564 else if (!dn.isEmpty())
2565 ci = cn + " [" + dn + "]";
2566 close(videofd);
2568 }
2569
2570 m_cardInfo->setValue(ci);
2571}
2572
2574 m_parent(parent),
2575 m_cardInfo(new GroupSetting())
2576{
2577 setVisible(false);
2578
2579 m_device = new VideoDevice(m_parent, 0, 15);
2580
2581 setLabel(QObject::tr("V4L2 encoder devices (multirec capable)"));
2582
2583 m_cardInfo->setLabel(tr("Probed info"));
2584 m_cardInfo->setReadOnly(true);
2585
2586 cardtype.addTargetedChild("V4L2ENC", m_device);
2587 cardtype.addTargetedChild("V4L2ENC", m_cardInfo);
2588
2589 // Override database schema default, set schedgroup false
2590 cardtype.addTargetedChild("V4L2ENC", new SchedGroupFalse(m_parent));
2591
2592 connect(m_device, qOverload<const QString&>(&StandardSetting::valueChanged),
2594
2595 const QString &device_name = m_device->getValue();
2596 if (!device_name.isEmpty())
2597 probeCard(device_name);
2598}
2599
2600void V4L2encGroup::probeCard([[maybe_unused]] const QString &device_name)
2601{
2602#if CONFIG_V4L2
2603 QString card_name = tr("Failed to open");
2604 QString card_info = card_name;
2605 V4L2util v4l2(device_name);
2606
2607 if (!v4l2.IsOpen())
2608 {
2609 m_driverName = tr("Failed to probe");
2610 return;
2611 }
2612 m_driverName = v4l2.DriverName();
2613 card_name = v4l2.CardName();
2614
2615 if (!m_driverName.isEmpty())
2616 card_info = card_name + " [" + m_driverName + "]";
2617
2618 m_cardInfo->setValue(card_info);
2619
2620 if (m_device->getSubSettings()->empty())
2621 {
2622 auto* audioinput = new TunerCardAudioInput(m_parent, QString(), "V4L2");
2623 if (audioinput->fillSelections(device_name) > 1)
2624 {
2625 audioinput->setName("AudioInput");
2627 }
2628 else
2629 {
2630 delete audioinput;
2631 }
2632
2633 if (v4l2.HasSlicedVBI())
2634 {
2635 auto* vbidev = new VBIDevice(m_parent);
2636 if (vbidev->setFilter(card_name, m_driverName) > 0)
2637 {
2638 vbidev->setName("VBIDevice");
2640 }
2641 else
2642 {
2643 delete vbidev;
2644 }
2645 }
2646
2649 new ChannelTimeout(m_parent, 15s, 2s));
2650 }
2651#endif // CONFIG_V4L2
2652}
2653
2655{
2656 setLabel(QObject::tr("Capture Card Setup"));
2657
2658 auto* cardtype = new CardType(parent);
2659 parent.addChild(cardtype);
2660
2661#if CONFIG_DVB
2662 cardtype->addTargetedChild("DVB",
2663 new DVBConfigurationGroup(parent, *cardtype));
2664#endif // CONFIG_DVB
2665
2666#if CONFIG_V4L2
2667 cardtype->addTargetedChild("HDPVR",
2668 new HDPVRConfigurationGroup(parent, *cardtype));
2669#endif // CONFIG_V4L2
2670
2671#if CONFIG_HDHOMERUN
2672 cardtype->addTargetedChild("HDHOMERUN",
2673 new HDHomeRunConfigurationGroup(parent, *cardtype));
2674#endif // CONFIG_HDHOMERUN
2675
2676#if CONFIG_VBOX
2677 cardtype->addTargetedChild("VBOX",
2678 new VBoxConfigurationGroup(parent, *cardtype));
2679#endif // CONFIG_VBOX
2680
2681#if CONFIG_SATIP
2682 cardtype->addTargetedChild("SATIP",
2683 new SatIPConfigurationGroup(parent, *cardtype));
2684#endif // CONFIG_SATIP
2685
2686#if CONFIG_FIREWIRE
2687 FirewireConfigurationGroup(parent, *cardtype);
2688#endif // CONFIG_FIREWIRE
2689
2690#if CONFIG_CETON
2691 CetonSetting::CetonConfigurationGroup(parent, *cardtype);
2692#endif // CONFIG_CETON
2693
2694#if CONFIG_IPTV
2695 IPTVConfigurationGroup(parent, *cardtype);
2696#endif // CONFIG_IPTV
2697
2698#if CONFIG_V4L2
2699 cardtype->addTargetedChild("V4L2ENC", new V4L2encGroup(parent, *cardtype));
2700 cardtype->addTargetedChild("V4L",
2701 new V4LConfigurationGroup(parent, *cardtype, "V4L"));
2702 cardtype->addTargetedChild("MJPEG",
2703 new V4LConfigurationGroup(parent, *cardtype, "MJPEG"));
2704 cardtype->addTargetedChild("GO7007",
2705 new V4LConfigurationGroup(parent, *cardtype, "GO7007"));
2706 cardtype->addTargetedChild("MPEG",
2707 new MPEGConfigurationGroup(parent, *cardtype));
2708#endif // CONFIG_V4L2
2709
2710#if CONFIG_ASI
2711 cardtype->addTargetedChild("ASI",
2712 new ASIConfigurationGroup(parent, *cardtype));
2713#endif // CONFIG_ASI
2714
2715 // for testing without any actual tuner hardware:
2716 cardtype->addTargetedChild("IMPORT",
2717 new ImportConfigurationGroup(parent, *cardtype));
2718 cardtype->addTargetedChild("DEMO",
2719 new DemoConfigurationGroup(parent, *cardtype));
2720#ifndef Q_OS_WINDOWS
2721 cardtype->addTargetedChild("EXTERNAL",
2722 new ExternalConfigurationGroup(parent,
2723 *cardtype));
2724#endif
2725}
2726
2727CaptureCard::CaptureCard(bool use_card_group)
2728 : m_id(new ID)
2729{
2730 addChild(m_id);
2731 if (use_card_group)
2732 CaptureCardGroup(*this);
2733 addChild(new Hostname(*this));
2734}
2735
2737{
2738 int cardid = getCardID();
2739 if (cardid <= 0)
2740 return {};
2741 return CardUtil::GetRawInputType(cardid);
2742}
2743
2745{
2747 QString qstr =
2748 "SELECT cardid, videodevice, cardtype, displayname "
2749 "FROM capturecard "
2750 "WHERE hostname = :HOSTNAME AND parentid = 0 "
2751 "ORDER BY cardid";
2752
2753 query.prepare(qstr);
2754 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
2755
2756 if (!query.exec())
2757 {
2758 MythDB::DBError("CaptureCard::fillSelections", query);
2759 return;
2760 }
2761
2763
2764 while (query.next())
2765 {
2766 uint cardid = query.value(0).toUInt();
2767 QString videodevice = query.value(1).toString();
2768 QString cardtype = query.value(2).toString();
2769 QString displayname = query.value(3).toString();
2770
2771 QString label = QString("%1 (%2)")
2772 .arg(CardUtil::GetDeviceLabel(cardtype, videodevice), displayname);
2773
2774 auto *card = new CaptureCard();
2775 card->loadByID(cardid);
2776 card->setLabel(label);
2777 setting->addChild(card);
2778 }
2779}
2780
2782{
2783 m_id->setValue(cardid);
2784 Load();
2785}
2786
2788{
2789 return true;
2790}
2791
2793{
2795}
2796
2797
2799{
2800 uint init_cardid = getCardID();
2801 QString init_dev = CardUtil::GetVideoDevice(init_cardid);
2802
2804
2806
2808
2809 uint cardid = getCardID();
2810 QString type = CardUtil::GetRawInputType(cardid);
2811 QString dev = CardUtil::GetVideoDevice(cardid);
2812
2813 if (dev != init_dev)
2814 {
2815 if (!init_dev.isEmpty())
2816 {
2817 uint init_groupid = CardUtil::GetDeviceInputGroup(init_cardid);
2818 CardUtil::UnlinkInputGroup(init_cardid, init_groupid);
2819 }
2820 if (!dev.isEmpty())
2821 {
2822 uint groupid =
2824 gCoreContext->GetHostName(), dev);
2825 CardUtil::LinkInputGroup(cardid, groupid);
2826 CardUtil::UnlinkInputGroup(0, groupid);
2827 }
2828 }
2829
2830 // Handle any cloning we may need to do
2832 {
2833 std::vector<uint> clones = CardUtil::GetChildInputIDs(cardid);
2834 for (uint clone : clones)
2835 CardUtil::CloneCard(cardid, clone);
2836 }
2837}
2838
2840{
2841 if (getCardID() == 0)
2842 {
2843 Save();
2844 Load();
2845 }
2846}
2847
2849 CaptureCardComboBoxSetting(parent, false, "cardtype")
2850{
2851 setLabel(QObject::tr("Card type"));
2852 setHelpText(QObject::tr("Change the cardtype to the appropriate type for "
2853 "the capture card you are configuring."));
2854 fillSelections(this);
2855}
2856
2858{
2859#if CONFIG_DVB
2860 setting->addSelection(
2861 QObject::tr("DVB-T/S/C, ATSC or ISDB-T tuner card"), "DVB");
2862#endif // CONFIG_DVB
2863
2864#if CONFIG_V4L2
2865 setting->addSelection(
2866 QObject::tr("V4L2 encoder"), "V4L2ENC");
2867 setting->addSelection(
2868 QObject::tr("HD-PVR H.264 encoder"), "HDPVR");
2869#endif // CONFIG_V4L2
2870
2871#if CONFIG_HDHOMERUN
2872 setting->addSelection(
2873 QObject::tr("HDHomeRun networked tuner"), "HDHOMERUN");
2874#endif // CONFIG_HDHOMERUN
2875
2876#if CONFIG_SATIP
2877 setting->addSelection(
2878 QObject::tr("Sat>IP networked tuner"), "SATIP");
2879#endif // CONFIG_SATIP
2880
2881#if CONFIG_VBOX
2882 setting->addSelection(
2883 QObject::tr("V@Box TV Gateway networked tuner"), "VBOX");
2884#endif // CONFIG_VBOX
2885
2886#if CONFIG_FIREWIRE
2887 setting->addSelection(
2888 QObject::tr("FireWire cable box"), "FIREWIRE");
2889#endif // CONFIG_FIREWIRE
2890
2891#if CONFIG_CETON
2892 setting->addSelection(
2893 QObject::tr("Ceton Cablecard tuner"), "CETON");
2894#endif // CONFIG_CETON
2895
2896#if CONFIG_IPTV
2897 setting->addSelection(QObject::tr("IPTV recorder"), "FREEBOX");
2898#endif // CONFIG_IPTV
2899
2900#if CONFIG_V4L2
2901 setting->addSelection(
2902 QObject::tr("Analog to MPEG-2 encoder card (PVR-150/250/350, etc)"), "MPEG");
2903 setting->addSelection(
2904 QObject::tr("Analog to MJPEG encoder card (Matrox G200, DC10, etc)"), "MJPEG");
2905 setting->addSelection(
2906 QObject::tr("Analog to MPEG-4 encoder (Plextor ConvertX USB, etc)"),
2907 "GO7007");
2908 setting->addSelection(
2909 QObject::tr("Analog capture card"), "V4L");
2910#endif // CONFIG_V4L2
2911
2912#if CONFIG_ASI
2913 setting->addSelection(QObject::tr("DVEO ASI recorder"), "ASI");
2914#endif
2915
2916 setting->addSelection(QObject::tr("Import test recorder"), "IMPORT");
2917 setting->addSelection(QObject::tr("Demo test recorder"), "DEMO");
2918#ifndef Q_OS_WINDOWS
2919 setting->addSelection(QObject::tr("External (black box) recorder"),
2920 "EXTERNAL");
2921#endif
2922}
2923
2925 StandardSetting(new CaptureCardDBStorage(this, parent, "hostname"))
2926{
2927 setVisible(false);
2929}
2930
2932{
2933 delete GetStorage();
2934}
2935
2937{
2938 public:
2939 explicit InputName(const CardInput &parent) :
2940 MythUIComboBoxSetting(new CardInputDBStorage(this, parent, "inputname"))
2941 {
2942 setLabel(QObject::tr("Input name"));
2943 };
2944
2945 ~InputName() override
2946 {
2947 delete GetStorage();
2948 }
2949
2950 void Load(void) override // StandardSetting
2951 {
2954 };
2955
2957 clearSelections();
2958 addSelection(QObject::tr("(None)"), "None");
2959 auto *storage = dynamic_cast<CardInputDBStorage*>(GetStorage());
2960 if (storage == nullptr)
2961 return;
2962 uint cardid = storage->getInputID();
2963 QString type = CardUtil::GetRawInputType(cardid);
2964 QString device = CardUtil::GetVideoDevice(cardid);
2965 QStringList inputs;
2966 CardUtil::GetDeviceInputNames(device, type, inputs);
2967 while (!inputs.isEmpty())
2968 {
2969 addSelection(inputs.front());
2970 inputs.pop_front();
2971 }
2972 };
2973};
2974
2976{
2977 public:
2979 {
2980 setLabel(QObject::tr("Delivery system"));
2981 setHelpText(QObject::tr(
2982 "This shows the delivery system (modulation), for instance DVB-T2, "
2983 "that you have selected when you configured the capture card. "
2984 "This must be the same as the modulation used by the video source. "));
2985 };
2986};
2987
2989{
2991
2992 public:
2993 explicit InputDisplayName(const CardInput &parent) :
2994 MythUITextEditSetting(new CardInputDBStorage(this, parent, "displayname")), m_parent(parent)
2995 {
2996 setLabel(QObject::tr("Display name"));
2997 setHelpText(QObject::tr(
2998 "This name is displayed on screen when Live TV begins "
2999 "and in various other places. Make sure the last two "
3000 "characters are unique for each input or use a "
3001 "slash ('/') to designate the unique portion."));
3002 };
3003
3005 {
3006 delete GetStorage();
3007 }
3008 void Load(void) override {
3010 if (getValue().isEmpty())
3011 setValue(tr("Input %1").arg(m_parent.getInputID()));
3012 }
3013 private:
3015};
3016
3018{
3019 public:
3020 CardInputComboBoxSetting(const CardInput &parent, const QString &setting) :
3021 MythUIComboBoxSetting(new CardInputDBStorage(this, parent, setting))
3022 {
3023 }
3024
3026 {
3027 delete GetStorage();
3028 }
3029};
3030
3032{
3033 public:
3034 explicit SourceID(const CardInput &parent) :
3035 CardInputComboBoxSetting(parent, "sourceid")
3036 {
3037 setLabel(QObject::tr("Video source"));
3038 addSelection(QObject::tr("(None)"), "0");
3039 };
3040
3041 void Load(void) override // StandardSetting
3042 {
3045 };
3046
3048 clearSelections();
3049 addSelection(QObject::tr("(None)"), "0");
3051 };
3052};
3053
3055{
3056 public:
3057 InputGroup(const CardInput &parent, uint group_num) :
3058 m_cardInput(parent),
3059 m_groupNum(group_num)
3060 {
3061 setLabel(QObject::tr("Input group") +
3062 QString(" %1").arg(m_groupNum + 1));
3063 setHelpText(QObject::tr(
3064 "Leave as 'Generic' unless this input is shared with "
3065 "another device. Only one of the inputs in an input "
3066 "group will be allowed to record at any given time."));
3067 }
3068
3069 void Load(void) override; // StandardSetting
3070
3071 void Save(void) override // StandardSetting
3072 {
3073 uint inputid = m_cardInput.getInputID();
3074 uint new_groupid = getValue().toUInt();
3075
3076 if (m_groupId && (m_groupId != new_groupid))
3077 CardUtil::UnlinkInputGroup(inputid, m_groupId);
3078
3079 if (new_groupid)
3080 CardUtil::LinkInputGroup(inputid, new_groupid);
3081 }
3082
3083 virtual void Save(const QString& /*destination*/) { Save(); }
3084
3085 private:
3088 uint m_groupId {0};
3089};
3090
3092{
3093#if 0
3094 LOG(VB_GENERAL, LOG_DEBUG, QString("InputGroup::Load() %1 %2")
3095 .arg(m_groupNum).arg(m_cardInput.getInputID()));
3096#endif
3097
3098 uint inputid = m_cardInput.getInputID();
3099 QMap<uint, uint> grpcnt;
3100 std::vector<QString> names;
3101 std::vector<uint> grpid;
3102 std::vector<uint> selected_groupids;
3103
3104 names.push_back(QObject::tr("Generic"));
3105 grpid.push_back(0);
3106 grpcnt[0]++;
3107
3109 query.prepare(
3110 "SELECT cardinputid, inputgroupid, inputgroupname "
3111 "FROM inputgroup "
3112 "WHERE inputgroupname LIKE 'user:%' "
3113 "ORDER BY inputgroupid, cardinputid, inputgroupname");
3114
3115 if (!query.exec())
3116 {
3117 MythDB::DBError("InputGroup::Load()", query);
3118 }
3119 else
3120 {
3121 while (query.next())
3122 {
3123 uint groupid = query.value(1).toUInt();
3124 if ((inputid != 0U) && (query.value(0).toUInt() == inputid))
3125 selected_groupids.push_back(groupid);
3126
3127 grpcnt[groupid]++;
3128
3129 if (grpcnt[groupid] == 1)
3130 {
3131 names.push_back(query.value(2).toString().mid(5, -1));
3132 grpid.push_back(groupid);
3133 }
3134 }
3135 }
3136
3137 // makes sure we select something
3138 m_groupId = 0;
3139 if (m_groupNum < selected_groupids.size())
3140 m_groupId = selected_groupids[m_groupNum];
3141
3142#if 0
3143 LOG(VB_GENERAL, LOG_DEBUG, QString("Group num: %1 id: %2")
3144 .arg(m_groupNum).arg(m_groupId));
3145 {
3146 QString msg;
3147 for (uint i = 0; i < selected_groupids.size(); i++)
3148 msg += QString("%1 ").arg(selected_groupids[i]);
3149 LOG(VB_GENERAL, LOG_DEBUG, msg);
3150 }
3151#endif
3152
3153 // add selections to combobox
3154 clearSelections();
3155 uint index = 0;
3156 for (size_t i = 0; i < names.size(); i++)
3157 {
3158 bool sel = (m_groupId == grpid[i]);
3159 index = sel ? i : index;
3160
3161#if 0
3162 LOG(VB_GENERAL, LOG_DEBUG, QString("grpid %1, name '%2', i %3, s %4")
3163 .arg(grpid[i]).arg(names[i]) .arg(index).arg(sel ? "T" : "F"));
3164#endif
3165
3166 addSelection(names[i], QString::number(grpid[i]), sel);
3167 }
3168
3169#if 0
3170 LOG(VB_GENERAL, LOG_DEBUG, QString("Group index: %1").arg(index));
3171#endif
3172
3173 if (!names.empty())
3174 setValue(index);
3175
3177}
3178
3180{
3181 public:
3182 explicit QuickTune(const CardInput &parent) :
3183 CardInputComboBoxSetting(parent, "quicktune")
3184 {
3185 setLabel(QObject::tr("Use quick tuning"));
3186 addSelection(QObject::tr("Never"), "0", true);
3187 addSelection(QObject::tr("Live TV only"), "1", false);
3188 addSelection(QObject::tr("Always"), "2", false);
3189 setHelpText(QObject::tr(
3190 "If enabled, MythTV will tune using only the "
3191 "MPEG program number. The program numbers "
3192 "change more often than DVB or ATSC tuning "
3193 "parameters, so this is slightly less reliable. "
3194 "This will also inhibit EIT gathering during "
3195 "Live TV and recording."));
3196 };
3197};
3198
3200{
3201 public:
3202 explicit ExternalChannelCommand(const CardInput &parent) :
3203 MythUITextEditSetting(new CardInputDBStorage(this, parent, "externalcommand"))
3204 {
3205 setLabel(QObject::tr("External channel change command"));
3206 setValue("");
3207 setHelpText(QObject::tr("If specified, this command will be run to "
3208 "change the channel for inputs which have an external "
3209 "tuner device such as a cable box. The first argument "
3210 "will be the channel number."));
3211 };
3212
3214 {
3215 delete GetStorage();
3216 }
3217};
3218
3220{
3221 public:
3222 explicit PresetTuner(const CardInput &parent) :
3223 MythUITextEditSetting(new CardInputDBStorage(this, parent, "tunechan"))
3224 {
3225 setLabel(QObject::tr("Preset tuner to channel"));
3226 setValue("");
3227 setHelpText(QObject::tr("Leave this blank unless you have an external "
3228 "tuner that is connected to the tuner input of your card. "
3229 "If so, you will need to specify the preset channel for "
3230 "the signal (normally 3 or 4)."));
3231 };
3232
3233 ~PresetTuner() override
3234 {
3235 delete GetStorage();
3236 }
3237};
3238
3239void StartingChannel::SetSourceID(const QString &sourceid)
3240{
3241 clearSelections();
3242 if (sourceid.isEmpty() || !sourceid.toUInt())
3243 return;
3244
3245 // Get the existing starting channel
3246 auto *storage = dynamic_cast<CardInputDBStorage*>(GetStorage());
3247 if (storage == nullptr)
3248 return;
3249 int inputId = storage->getInputID();
3250 QString startChan = CardUtil::GetStartChannel(inputId);
3251
3252 ChannelInfoList channels = ChannelUtil::GetAllChannels(sourceid.toUInt());
3253
3254 if (channels.empty())
3255 {
3256 addSelection(tr("Please add channels to this source"),
3257 startChan.isEmpty() ? "0" : startChan);
3258 return;
3259 }
3260
3261 // If there are channels sort them, then add theme
3262 // (selecting the old start channel if it is there).
3263 QString order = gCoreContext->GetSetting("ChannelOrdering", "channum");
3264 ChannelUtil::SortChannels(channels, order);
3265 bool has_visible = false;
3266 for (size_t i = 0; i < channels.size() && !has_visible; i++)
3267 has_visible |= channels[i].m_visible;
3268
3269 for (auto & channel : channels)
3270 {
3271 const QString channum = channel.m_chanNum;
3272 bool sel = channum == startChan;
3273 if (!has_visible || channel.m_visible || sel)
3274 {
3275 addSelection(channum, channum, sel);
3276 }
3277 }
3278}
3279
3281{
3282 public:
3283 explicit InputPriority(const CardInput &parent) :
3284 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "recpriority"),
3285 -99, 99, 1)
3286 {
3287 setLabel(QObject::tr("Input priority"));
3288 setValue(0);
3289 setHelpText(QObject::tr("If the input priority is not equal for "
3290 "all inputs, the scheduler may choose to record a show "
3291 "at a later time so that it can record on an input with "
3292 "a higher value."));
3293 };
3294
3296 {
3297 delete GetStorage();
3298 }
3299};
3300
3302{
3303 public:
3304 ScheduleOrder(const CardInput &parent, int _value) :
3305 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "schedorder"),
3306 0, 99, 1)
3307 {
3308 setLabel(QObject::tr("Schedule order"));
3309 setValue(_value);
3310 setHelpText(QObject::tr("If priorities and other factors are equal "
3311 "the scheduler will choose the available "
3312 "input with the lowest, non-zero value. "
3313 "Setting this value to zero will make the "
3314 "input unavailable to the scheduler."));
3315 };
3316
3318 {
3319 delete GetStorage();
3320 }
3321};
3322
3324{
3325 public:
3326 LiveTVOrder(const CardInput &parent, int _value) :
3327 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "livetvorder"),
3328 0, 99, 1)
3329 {
3330 setLabel(QObject::tr("Live TV order"));
3331 setValue(_value);
3332 setHelpText(QObject::tr("When entering Live TV, the available, local "
3333 "input with the lowest, non-zero value will "
3334 "be used. If no local inputs are available, "
3335 "the available, remote input with the lowest, "
3336 "non-zero value will be used. "
3337 "Setting this value to zero will make the "
3338 "input unavailable to live TV."));
3339 };
3340
3341 ~LiveTVOrder() override
3342 {
3343 delete GetStorage();
3344 }
3345};
3346
3348{
3349 public:
3350 explicit DishNetEIT(const CardInput &parent) :
3352 "dishnet_eit"))
3353 {
3354 setLabel(QObject::tr("Use DishNet long-term EIT data"));
3355 setValue(false);
3357 QObject::tr(
3358 "If you point your satellite dish toward DishNet's birds, "
3359 "you may wish to enable this feature. For best results, "
3360 "enable general EIT collection as well."));
3361 };
3362
3363 ~DishNetEIT() override
3364 {
3365 delete GetStorage();
3366 }
3367};
3368
3369CardInput::CardInput(const QString & cardtype, const QString & device,
3370 int _cardid) :
3371 m_id(new ID()),
3372 m_inputName(new InputName(*this)),
3373 m_sourceId(new SourceID(*this)),
3374 m_startChan(new StartingChannel(*this)),
3375 m_scan(new ButtonStandardSetting(tr("Scan for channels"))),
3376 m_srcFetch(new ButtonStandardSetting(tr("Fetch channels from listings source"))),
3377 m_externalInputSettings(new DiSEqCDevSettings()),
3378 m_inputGrp0(new InputGroup(*this, 0)),
3379 m_inputGrp1(new InputGroup(*this, 1))
3380{
3381 addChild(m_id);
3382
3384 {
3386 _cardid, true));
3387 }
3388
3389 // Delivery system for DVB, input name for other,
3390 // same field capturecard/inputname for both
3391 if ("DVB" == cardtype)
3392 {
3393 auto *ds = new DeliverySystem();
3394 ds->setValue(CardUtil::GetDeliverySystemFromDB(_cardid));
3395 addChild(ds);
3396 }
3397 else if (CardUtil::IsV4L(cardtype))
3398 {
3400 }
3401 addChild(new InputDisplayName(*this));
3403
3404 if (CardUtil::IsEncoder(cardtype) || CardUtil::IsUnscanable(cardtype))
3405 {
3406 addChild(new ExternalChannelCommand(*this));
3407 if (CardUtil::HasTuner(cardtype, device))
3408 addChild(new PresetTuner(*this));
3409 }
3410 else
3411 {
3412 addChild(new QuickTune(*this));
3413 if ("DVB" == cardtype)
3414 addChild(new DishNetEIT(*this));
3415 }
3416
3418 tr("Use channel scanner to find channels for this input."));
3419
3421 tr("This uses the listings data source to "
3422 "provide the channels for this input.") + " " +
3423 tr("This can take a long time to run."));
3424
3427
3429
3430 auto *interact = new GroupSetting();
3431
3432 interact->setLabel(QObject::tr("Interactions between inputs"));
3433 if (CardUtil::IsTunerSharingCapable(cardtype))
3434 {
3435 m_instanceCount = new InstanceCount(*this);
3436 interact->addChild(m_instanceCount);
3437 m_schedGroup = new SchedGroup(*this);
3438 interact->addChild(m_schedGroup);
3439 }
3440 interact->addChild(new InputPriority(*this));
3441 interact->addChild(new ScheduleOrder(*this, _cardid));
3442 interact->addChild(new LiveTVOrder(*this, _cardid));
3443
3444 auto *ingrpbtn =
3445 new ButtonStandardSetting(QObject::tr("Create a New Input Group"));
3446 ingrpbtn->setHelpText(
3447 QObject::tr("Input groups are only needed when two or more cards "
3448 "share the same resource such as a FireWire card and "
3449 "an analog card input controlling the same set top box."));
3450 interact->addChild(ingrpbtn);
3451 interact->addChild(m_inputGrp0);
3452 interact->addChild(m_inputGrp1);
3453
3454 addChild(interact);
3455
3456 setObjectName("CardInput");
3457 SetSourceID("-1");
3458
3461 connect(m_sourceId, qOverload<const QString&>(&StandardSetting::valueChanged),
3463 connect(m_sourceId, qOverload<const QString&>(&StandardSetting::valueChanged),
3464 this, &CardInput::SetSourceID);
3465 connect(ingrpbtn, &ButtonStandardSetting::clicked,
3467}
3468
3470{
3472 {
3474 m_externalInputSettings = nullptr;
3475 }
3476}
3477
3478void CardInput::SetSourceID(const QString &sourceid)
3479{
3480 uint cid = m_id->getValue().toUInt();
3481 QString raw_card_type = CardUtil::GetRawInputType(cid);
3482 bool enable = (sourceid.toInt() > 0);
3483 m_scan->setEnabled(enable && !raw_card_type.isEmpty() &&
3484 !CardUtil::IsUnscanable(raw_card_type));
3485 m_srcFetch->setEnabled(enable);
3486}
3487
3488QString CardInput::getSourceName(void) const
3489{
3490 return m_sourceId->getValueLabel();
3491}
3492
3494{
3495 m_inputGrp0->Save();
3496 m_inputGrp1->Save();
3497
3498 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
3499 auto *settingdialog =
3500 new MythTextInputDialog(popupStack, tr("Enter new group name"));
3501
3502 if (settingdialog->Create())
3503 {
3504 connect(settingdialog, &MythTextInputDialog::haveResult,
3506 popupStack->AddScreen(settingdialog);
3507 }
3508 else
3509 {
3510 delete settingdialog;
3511 }
3512}
3513
3514void CardInput::CreateNewInputGroupSlot(const QString& name)
3515{
3516 if (name.isEmpty())
3517 {
3518 ShowOkPopup(tr("Sorry, this Input Group name cannot be blank."));
3519 return;
3520 }
3521
3522 QString new_name = QString("user:") + name;
3523
3525 query.prepare("SELECT inputgroupname "
3526 "FROM inputgroup "
3527 "WHERE inputgroupname = :GROUPNAME");
3528 query.bindValue(":GROUPNAME", new_name);
3529
3530 if (!query.exec())
3531 {
3532 MythDB::DBError("CreateNewInputGroup 1", query);
3533 return;
3534 }
3535
3536 if (query.next())
3537 {
3538 ShowOkPopup(tr("Sorry, this Input Group name is already in use."));
3539 return;
3540 }
3541
3542 uint inputgroupid = CardUtil::CreateInputGroup(new_name);
3543
3544 m_inputGrp0->Load();
3545 m_inputGrp1->Load();
3546
3547 if (m_inputGrp0->getValue().toUInt() == 0U)
3548 {
3550 m_inputGrp0->getValueIndex(QString::number(inputgroupid)));
3551 }
3552 else
3553 {
3555 m_inputGrp1->getValueIndex(QString::number(inputgroupid)));
3556 }
3557}
3558
3560{
3561 uint srcid = m_sourceId->getValue().toUInt();
3562 uint crdid = m_id->getValue().toUInt();
3563 QString in = m_inputName->getValue();
3564
3565#if CONFIG_BACKEND
3566 uint num_channels_before = SourceUtil::GetChannelCount(srcid);
3567
3568 Save(); // save info for scanner.
3569
3570 QString cardtype = CardUtil::GetRawInputType(crdid);
3571 if (CardUtil::IsUnscanable(cardtype))
3572 {
3573 LOG(VB_GENERAL, LOG_ERR,
3574 QString("Sorry, %1 cards do not yet support scanning.")
3575 .arg(cardtype));
3576 return;
3577 }
3578
3580 auto *ssd = new StandardSettingDialog(mainStack, "generalsettings",
3581 new ScanWizard(srcid, crdid, in));
3582
3583 if (ssd->Create())
3584 {
3585 connect(ssd, &StandardSettingDialog::Exiting, this,
3586 [srcid, this, num_channels_before]()
3587 {
3588 if (SourceUtil::GetChannelCount(srcid))
3589 m_startChan->SetSourceID(QString::number(srcid));
3590 if (num_channels_before)
3591 {
3592 m_startChan->Load();
3593 m_startChan->Save();
3594 }
3595 });
3596 mainStack->AddScreen(ssd);
3597 }
3598 else
3599 {
3600 delete ssd;
3601 }
3602
3603#else
3604 LOG(VB_GENERAL, LOG_ERR, "You must compile the backend "
3605 "to be able to scan for channels");
3606#endif
3607}
3608
3610{
3611 uint srcid = m_sourceId->getValue().toUInt();
3612 uint crdid = m_id->getValue().toUInt();
3613
3614 uint num_channels_before = SourceUtil::GetChannelCount(srcid);
3615
3616 if (crdid && srcid)
3617 {
3618 Save(); // save info for fetch..
3619
3620 QString cardtype = CardUtil::GetRawInputType(crdid);
3621
3622 if (!CardUtil::IsCableCardPresent(crdid, cardtype) &&
3623 !CardUtil::IsUnscanable(cardtype) &&
3624 !CardUtil::IsEncoder(cardtype) &&
3625 cardtype != "HDHOMERUN" &&
3626 !num_channels_before)
3627 {
3628 LOG(VB_GENERAL, LOG_ERR, "Skipping channel fetch, you need to "
3629 "scan for channels first.");
3630 return;
3631 }
3632
3634 }
3635
3636 if (SourceUtil::GetChannelCount(srcid))
3637 m_startChan->SetSourceID(QString::number(srcid));
3638 if (num_channels_before)
3639 {
3640 m_startChan->Load();
3641 m_startChan->Save();
3642 }
3643}
3644
3646{
3647 QString cardinputidTag(":WHERECARDID");
3648
3649 QString query("cardid = " + cardinputidTag);
3650
3651 bindings.insert(cardinputidTag, m_parent.getInputID());
3652
3653 return query;
3654}
3655
3657{
3658 QString cardinputidTag(":SETCARDID");
3659 QString colTag(":SET" + GetColumnName().toUpper());
3660
3661 QString query("cardid = " + cardinputidTag + ", " +
3662 GetColumnName() + " = " + colTag);
3663
3664 bindings.insert(cardinputidTag, m_parent.getInputID());
3665 bindings.insert(colTag, m_user->GetDBValue());
3666
3667 return query;
3668}
3669
3670void CardInput::loadByID(int inputid)
3671{
3672 m_id->setValue(inputid);
3673 m_externalInputSettings->Load(inputid);
3675}
3676
3677void CardInput::loadByInput(int _cardid, const QString& _inputname)
3678{
3680 query.prepare("SELECT cardid FROM capturecard "
3681 "WHERE cardid = :CARDID AND inputname = :INPUTNAME");
3682 query.bindValue(":CARDID", _cardid);
3683 query.bindValue(":INPUTNAME", _inputname);
3684
3685 if (query.exec() && query.isActive() && query.next())
3686 {
3687 loadByID(query.value(0).toInt());
3688 }
3689}
3690
3692{
3693 uint cardid = m_id->getValue().toUInt();
3696
3697 uint icount = 1;
3698 if (m_instanceCount)
3699 icount = m_instanceCount->getValue().toUInt();
3700
3701 CardUtil::InputSetMaxRecordings(cardid, icount);
3702}
3703
3705{
3706 return m_parent.getInputID();
3707}
3708
3710{
3711 return m_parent.getCardID();
3712}
3713
3715{
3716 emit Clicked(m_value);
3717}
3718
3719void CaptureCardEditor::AddSelection(const QString &label, const CCESlot slot)
3720{
3721 auto *button = new ButtonStandardSetting(label);
3722 connect(button, &ButtonStandardSetting::clicked, this, slot);
3723 addChild(button);
3724}
3725
3726void CaptureCardEditor::AddSelection(const QString &label, const CCESlotConst slot)
3727{
3728 auto *button = new ButtonStandardSetting(label);
3729 connect(button, &ButtonStandardSetting::clicked, this, slot);
3730 addChild(button);
3731}
3732
3734{
3736 tr("Are you sure you want to delete "
3737 "ALL capture cards on %1?").arg(gCoreContext->GetHostName()),
3739 true);
3740}
3741
3743{
3745 tr("Are you sure you want to delete "
3746 "ALL capture cards?"),
3748 true);
3749}
3750
3752{
3753 auto *card = new CaptureCard();
3754 card->setLabel(tr("New capture card"));
3755 card->Load();
3756 addChild(card);
3757 emit settingsChanged(this);
3758}
3759
3761{
3762 if (!doDelete)
3763 return;
3764
3766 Load();
3767 emit settingsChanged(this);
3768}
3769
3771{
3772 if (!doDelete)
3773 return;
3774
3776
3777 cards.prepare(
3778 "SELECT cardid "
3779 "FROM capturecard "
3780 "WHERE hostname = :HOSTNAME");
3781 cards.bindValue(":HOSTNAME", gCoreContext->GetHostName());
3782
3783 if (!cards.exec() || !cards.isActive())
3784 {
3786 tr("Error getting list of cards for this host. "
3787 "Unable to delete capturecards for %1")
3788 .arg(gCoreContext->GetHostName()));
3789
3790 MythDB::DBError("Selecting cardids for deletion", cards);
3791 return;
3792 }
3793
3794 while (cards.next())
3795 CardUtil::DeleteInput(cards.value(0).toUInt());
3796
3797 Load();
3798 emit settingsChanged(this);
3799}
3800
3802{
3803 setLabel(tr("Capture cards"));
3804}
3805
3807{
3808 clearSettings();
3809 AddSelection(QObject::tr("(New capture card)"), &CaptureCardEditor::AddNewCard);
3810 AddSelection(QObject::tr("(Delete all capture cards on %1)")
3811 .arg(gCoreContext->GetHostName()),
3813 AddSelection(QObject::tr("(Delete all capture cards)"),
3816}
3817
3819{
3820 setLabel(tr("Video sources"));
3821}
3822
3824{
3825 clearSettings();
3826 AddSelection(QObject::tr("(New video source)"), &VideoSourceEditor::NewSource);
3827 AddSelection(QObject::tr("(Delete all video sources)"),
3831}
3832
3833void VideoSourceEditor::AddSelection(const QString &label, const VSESlot slot)
3834{
3835 auto *button = new ButtonStandardSetting(label);
3836 connect(button, &ButtonStandardSetting::clicked, this, slot);
3837 addChild(button);
3838}
3839
3840void VideoSourceEditor::AddSelection(const QString &label, const VSESlotConst slot)
3841{
3842 auto *button = new ButtonStandardSetting(label);
3843 connect(button, &ButtonStandardSetting::clicked, this, slot);
3844 addChild(button);
3845}
3846
3848{
3850 tr("Are you sure you want to delete "
3851 "ALL video sources?"),
3853 true);
3854}
3855
3857{
3858 if (!doDelete)
3859 return;
3860
3862 Load();
3863 emit settingsChanged(this);
3864}
3865
3867{
3868 auto *source = new VideoSource();
3869 source->setLabel(tr("New video source"));
3870 source->Load();
3871 addChild(source);
3872 emit settingsChanged(this);
3873}
3874
3876{
3877 setLabel(tr("Input connections"));
3878}
3879
3881{
3882 m_cardInputs.clear();
3883 clearSettings();
3884
3885 // We do this manually because we want custom labels. If
3886 // SelectSetting provided a facility to edit the labels, we
3887 // could use CaptureCard::fillSelections
3888
3890 query.prepare(
3891 "SELECT cardid, videodevice, cardtype, displayname "
3892 "FROM capturecard "
3893 "WHERE hostname = :HOSTNAME "
3894 " AND parentid = 0 "
3895 "ORDER BY cardid");
3896 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
3897
3898 if (!query.exec())
3899 {
3900 MythDB::DBError("CardInputEditor::load", query);
3901 return;
3902 }
3903
3904 while (query.next())
3905 {
3906 uint cardid = query.value(0).toUInt();
3907 QString videodevice = query.value(1).toString();
3908 QString cardtype = query.value(2).toString();
3909 QString displayname = query.value(3).toString();
3910
3911 auto *cardinput = new CardInput(cardtype, videodevice, cardid);
3912 cardinput->loadByID(cardid);
3913 QString inputlabel = QString("%1 (%2) -> %3")
3914 .arg(CardUtil::GetDeviceLabel(cardtype, videodevice),
3915 displayname, cardinput->getSourceName());
3916 m_cardInputs.push_back(cardinput);
3917 cardinput->setLabel(inputlabel);
3918 addChild(cardinput);
3919 }
3920
3922}
3923
3924#if CONFIG_DVB
3925static QString remove_chaff(const QString &name)
3926{
3927 // Trim off some of the chaff.
3928 QString short_name = name;
3929 if (short_name.startsWith("LG Electronics"))
3930 short_name = short_name.right(short_name.length() - 15);
3931 if (short_name.startsWith("Oren"))
3932 short_name = short_name.right(short_name.length() - 5);
3933 if (short_name.startsWith("Nextwave"))
3934 short_name = short_name.right(short_name.length() - 9);
3935 if (short_name.startsWith("frontend", Qt::CaseInsensitive))
3936 short_name = short_name.left(short_name.length() - 9);
3937 if (short_name.endsWith("VSB/QAM"))
3938 short_name = short_name.left(short_name.length() - 8);
3939 if (short_name.endsWith("VSB"))
3940 short_name = short_name.left(short_name.length() - 4);
3941 if (short_name.endsWith("DVB-T"))
3942 short_name = short_name.left(short_name.length() - 6);
3943
3944 // It would be infinitely better if DVB allowed us to query
3945 // the vendor ID. But instead we have to guess based on the
3946 // demodulator name. This means cards like the Air2PC HD5000
3947 // and DViCO Fusion HDTV cards are not identified correctly.
3948 short_name = short_name.simplified();
3949 if (short_name.startsWith("or51211", Qt::CaseInsensitive))
3950 short_name = "pcHDTV HD-2000";
3951 else if (short_name.startsWith("or51132", Qt::CaseInsensitive))
3952 short_name = "pcHDTV HD-3000";
3953 else if (short_name.startsWith("bcm3510", Qt::CaseInsensitive))
3954 short_name = "Air2PC v1";
3955 else if (short_name.startsWith("nxt2002", Qt::CaseInsensitive) ||
3956 short_name.startsWith("nxt200x", Qt::CaseInsensitive))
3957 short_name = "Air2PC v2";
3958 else if (short_name.startsWith("lgdt3302", Qt::CaseInsensitive))
3959 short_name = "DViCO HDTV3";
3960 else if (short_name.startsWith("lgdt3303", Qt::CaseInsensitive))
3961 short_name = "DViCO v2 or Air2PC v3 or pcHDTV HD-5500";
3962
3963 return short_name;
3964}
3965#endif // CONFIG_DVB
3966
3967void DVBConfigurationGroup::reloadDiseqcTree(const QString &videodevice)
3968{
3969 if (m_diseqcTree)
3970 m_diseqcTree->Load(videodevice);
3971
3972 if (m_cardType->getValue() == "DVB-S" ||
3973 m_cardType->getValue() == "DVB-S2" )
3974 {
3975 m_diseqcBtn->setVisible(true);
3976 }
3977 else
3978 {
3979 m_diseqcBtn->setVisible(false);
3980 }
3981 emit getParent()->settingsChanged(this);
3982}
3983
3984void DVBConfigurationGroup::probeCard(const QString &videodevice)
3985{
3986 if (videodevice.isEmpty())
3987 {
3988 m_cardName->setValue("");
3989 m_cardType->setValue("");
3990 return;
3991 }
3992
3993 if ((m_parent.getCardID() != 0) && m_parent.GetRawCardType() != "DVB")
3994 {
3995 m_cardName->setValue("");
3996 m_cardType->setValue("");
3997 return;
3998 }
3999
4000#if CONFIG_DVB
4001 QString frontend_name = CardUtil::ProbeDVBFrontendName(videodevice);
4002 QString subtype = CardUtil::ProbeDVBType(videodevice);
4003
4004 QString err_open = tr("Could not open card %1").arg(videodevice);
4005 QString err_other = tr("Could not get card info for card %1").arg(videodevice);
4006
4007 switch (CardUtil::toInputType(subtype))
4008 {
4010 m_cardName->setValue(err_open);
4011 m_cardType->setValue(strerror(errno));
4012 break;
4014 m_cardName->setValue(err_other);
4015 m_cardType->setValue("Unknown error");
4016 break;
4018 m_cardName->setValue(err_other);
4019 m_cardType->setValue(strerror(errno));
4020 break;
4022 m_cardType->setValue("DVB-S");
4023 m_cardName->setValue(frontend_name);
4026 break;
4028 m_cardType->setValue("DVB-S2");
4029 m_cardName->setValue(frontend_name);
4032 break;
4034 m_cardType->setValue("DVB-C");
4035 m_cardName->setValue(frontend_name);
4038 break;
4040 m_cardType->setValue("DVB-T2");
4041 m_cardName->setValue(frontend_name);
4044 break;
4046 {
4047 m_cardType->setValue("DVB-T");
4048 m_cardName->setValue(frontend_name);
4051 if (frontend_name.toLower().indexOf("usb") >= 0)
4052 {
4055 }
4056
4057 // slow down tuning for buggy drivers
4058 if ((frontend_name == "DiBcom 3000P/M-C DVB-T") ||
4059 (frontend_name ==
4060 "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"))
4061 {
4062 m_tuningDelay->setValueMs(200ms);
4063 }
4064
4065#if 0 // frontends on hybrid DVB-T/Analog cards
4066 QString short_name = remove_chaff(frontend_name);
4067 m_buttonAnalog->setVisible(
4068 short_name.startsWith("zarlink zl10353",
4069 Qt::CaseInsensitive) ||
4070 short_name.startsWith("wintv hvr 900 m/r: 65008/a1c0",
4071 Qt::CaseInsensitive) ||
4072 short_name.startsWith("philips tda10046h",
4073 Qt::CaseInsensitive));
4074#endif
4075 }
4076 break;
4078 {
4079 QString short_name = remove_chaff(frontend_name);
4080 m_cardType->setValue("ATSC");
4081 m_cardName->setValue(short_name);
4084
4085 // According to #1779 and #1935 the AverMedia 180 needs
4086 // a 3000 ms signal timeout, at least for QAM tuning.
4087 if (frontend_name == "Nextwave NXT200X VSB/QAM frontend")
4088 {
4091 }
4092
4093#if 0 // frontends on hybrid DVB-T/Analog cards
4094 if (frontend_name.toLower().indexOf("usb") < 0)
4095 {
4096 m_buttonAnalog->setVisible(
4097 short_name.startsWith("pchdtv", Qt::CaseInsensitive) ||
4098 short_name.startsWith("dvico", Qt::CaseInsensitive) ||
4099 short_name.startsWith("nextwave", Qt::CaseInsensitive));
4100 }
4101#endif
4102 }
4103 break;
4104 default:
4105 break;
4106 }
4107
4108 // Create selection list of all delivery systems of this card
4109 {
4111 QStringList delsyslist = CardUtil::ProbeDeliverySystems(videodevice);
4112 for (const auto & item : std::as_const(delsyslist))
4113 {
4114 LOG(VB_GENERAL, LOG_DEBUG, QString("DVBCardType: add deliverysystem:%1")
4115 .arg(item));
4116
4117 m_cardType->addSelection(item, item);
4118 }
4119
4120 // Default value, used if not already defined in capturecard/inputname
4121 QString delsys = CardUtil::ProbeDefaultDeliverySystem(videodevice);
4122 if (!delsys.isEmpty())
4123 {
4124 m_cardType->setValue(delsys);
4125 }
4126 }
4127#
4128#else
4129 m_cardType->setValue(QString("Recompile with DVB-Support!"));
4130#endif
4131}
4132
4134 QString dev, QString type) :
4135 CaptureCardComboBoxSetting(parent, false, "audiodevice"),
4136 m_lastDevice(std::move(dev)), m_lastCardType(std::move(type))
4137{
4138 setLabel(QObject::tr("Audio input"));
4139 setHelpText(QObject::tr("If there is more than one audio input, "
4140 "select which one to use."));
4141 int cardid = parent.getCardID();
4142 if (cardid <= 0)
4143 return;
4144
4147}
4148
4149int TunerCardAudioInput::fillSelections(const QString &device)
4150{
4152
4153 if (device.isEmpty())
4154 return 0;
4155
4156 m_lastDevice = device;
4157 QStringList inputs =
4159
4160 for (uint i = 0; i < (uint)inputs.size(); i++)
4161 {
4162 addSelection(inputs[i], QString::number(i),
4163 m_lastDevice == QString::number(i));
4164 }
4165 return inputs.size();
4166}
4167
4169 CardType& cardType) :
4170 m_parent(a_parent),
4171 m_cardNum(new DVBCardNum(a_parent)),
4172 m_cardName(new DVBCardName()),
4173 m_cardType(new DVBCardType(a_parent)),
4174 m_signalTimeout(new SignalTimeout(a_parent, 0.5s, 0.25s)),
4175 m_tuningDelay(new DVBTuningDelay(a_parent)),
4176 m_diseqcTree(new DiSEqCDevTree()),
4177 m_diseqcBtn(new DeviceTree(*m_diseqcTree))
4178{
4179 setVisible(false);
4180
4181 m_channelTimeout = new ChannelTimeout(m_parent, 3s, 1.75s);
4182
4183 cardType.addTargetedChild("DVB", m_cardNum);
4184
4185 cardType.addTargetedChild("DVB", m_cardName);
4186 cardType.addTargetedChild("DVB", m_cardType);
4187
4188 cardType.addTargetedChild("DVB", m_signalTimeout);
4189 cardType.addTargetedChild("DVB", m_channelTimeout);
4190
4191 cardType.addTargetedChild("DVB", new EmptyAudioDevice(m_parent));
4192 cardType.addTargetedChild("DVB", new EmptyVBIDevice(m_parent));
4193
4194 cardType.addTargetedChild("DVB", new DVBNoSeqStart(m_parent));
4195 cardType.addTargetedChild("DVB", new DVBOnDemand(m_parent));
4196 cardType.addTargetedChild("DVB", new DVBEITScan(m_parent));
4197
4198 m_diseqcBtn->setLabel(tr("DiSEqC (Switch, LNB and Rotor Configuration)"));
4199 m_diseqcBtn->setHelpText(tr("Input and satellite settings."));
4200
4201 cardType.addTargetedChild("DVB", m_tuningDelay);
4202 cardType.addTargetedChild("DVB", m_diseqcBtn);
4203 m_tuningDelay->setVisible(false);
4204
4205 connect(m_cardNum, qOverload<const QString&>(&StandardSetting::valueChanged),
4207 connect(m_cardNum, qOverload<const QString&>(&StandardSetting::valueChanged),
4209}
4210
4212{
4213 if (m_diseqcTree)
4214 {
4215 delete m_diseqcTree;
4216 m_diseqcTree = nullptr;
4217 }
4218}
4219
4221{
4223 m_diseqcBtn->Load();
4225 if (m_cardType->getValue() == "DVB-S" ||
4226 m_cardType->getValue() == "DVB-S2" ||
4228 {
4229 m_diseqcBtn->setVisible(true);
4230 }
4231}
4232
4234{
4238}
4239
4240// -----------------------
4241// SAT>IP configuration
4242// -----------------------
4243#if CONFIG_SATIP
4244
4245class DiSEqCPosition : public MythUISpinBoxSetting
4246{
4247 public:
4248 explicit DiSEqCPosition(const CaptureCard &parent, int value, int min_val) :
4249 MythUISpinBoxSetting(new CaptureCardDBStorage(this, parent, "dvb_diseqc_type"),
4250 min_val, 0xff, 1)
4251 {
4252 setLabel(QObject::tr("DiSEqC position"));
4253 setHelpText(QObject::tr("Position of the LNB on the DiSEqC switch. "
4254 "Leave at 1 if there is no DiSEqC switch "
4255 "and the LNB is directly connected to the SatIP server. "
4256 "This value is used as signal source (attribute src) in "
4257 "the SatIP tune command."));
4258 setValue(value);
4259 };
4260
4261 ~DiSEqCPosition() override
4262 {
4263 delete GetStorage();
4264 }
4265};
4266
4267SatIPConfigurationGroup::SatIPConfigurationGroup
4268 (CaptureCard& a_parent, CardType &a_cardtype) :
4269 m_parent(a_parent),
4270 m_deviceId(new SatIPDeviceID(a_parent))
4271{
4272 setVisible(false);
4273
4274 FillDeviceList();
4275
4276 m_friendlyName = new SatIPDeviceAttribute(tr("Friendly name"), tr("Friendly name of the Sat>IP server"));
4277 m_tunerType = new SatIPDeviceAttribute(tr("Tuner type"), tr("Type of the selected tuner"));
4278 m_tunerIndex = new SatIPDeviceAttribute(tr("Tuner index"), tr("Index of the tuner on the Sat>IP server"));
4279
4280 m_deviceIdList = new SatIPDeviceIDList(
4281 m_deviceId, m_friendlyName, m_tunerType, m_tunerIndex, &m_deviceList, m_parent);
4282
4283 a_cardtype.addTargetedChild("SATIP", m_deviceIdList);
4284 a_cardtype.addTargetedChild("SATIP", m_friendlyName);
4285 a_cardtype.addTargetedChild("SATIP", m_tunerType);
4286 a_cardtype.addTargetedChild("SATIP", m_tunerIndex);
4287 a_cardtype.addTargetedChild("SATIP", m_deviceId);
4288 a_cardtype.addTargetedChild("SATIP", new SignalTimeout(m_parent, 7s, 1s));
4289 a_cardtype.addTargetedChild("SATIP", new ChannelTimeout(m_parent, 10s, 2s));
4290 a_cardtype.addTargetedChild("SATIP", new DVBEITScan(m_parent));
4291 a_cardtype.addTargetedChild("SATIP", new DiSEqCPosition(m_parent, 1, 1));
4292
4293 connect(m_deviceIdList, &SatIPDeviceIDList::NewTuner,
4294 m_deviceId, &SatIPDeviceID::SetTuner);
4295};
4296
4297void SatIPConfigurationGroup::FillDeviceList(void)
4298{
4299 m_deviceList.clear();
4300
4301 // Find devices on the network
4302 // Returns each devices as "deviceid friendlyname ip tunerno tunertype"
4303 QStringList devs = CardUtil::ProbeVideoDevices("SATIP");
4304
4305 for (const auto & dev : std::as_const(devs))
4306 {
4307 QStringList devparts = dev.split(" ");
4308 const QString& id = devparts.value(0);
4309 const QString& name = devparts.value(1);
4310 const QString& ip = devparts.value(2);
4311 const QString& tunerno = devparts.value(3);
4312 const QString& tunertype = devparts.value(4);
4313
4314 SatIPDevice device;
4315 device.m_deviceId = id;
4316 device.m_cardIP = ip;
4317 device.m_inUse = false;
4318 device.m_friendlyName = name;
4319 device.m_tunerNo = tunerno;
4320 device.m_tunerType = tunertype;
4321 device.m_mythDeviceId = QString("%1:%2:%3").arg(id, tunertype, tunerno);
4322
4323 QString friendlyIdentifier = QString("%1, %2, Tuner #%3").arg(name, tunertype, tunerno);
4324
4325 m_deviceList[device.m_mythDeviceId] = device;
4326
4327 LOG(VB_CHANNEL, LOG_DEBUG, QString("SatIP: Add %1 '%2' '%3'")
4328 .arg(device.m_mythDeviceId, device.m_friendlyName, friendlyIdentifier));
4329 }
4330
4331 // Now find configured devices
4332 // Returns each devices as "deviceid friendlyname ip tunerno tunertype"
4333 QStringList db = CardUtil::GetVideoDevices("SATIP");
4334 for (const auto& dev : std::as_const(db))
4335 {
4336 auto dit = m_deviceList.find(dev);
4337 if (dit != m_deviceList.end())
4338 {
4339 (*dit).m_inUse = true;
4340 }
4341 }
4342};
4343
4344SatIPDeviceIDList::SatIPDeviceIDList(
4345 SatIPDeviceID *deviceId,
4346 SatIPDeviceAttribute *friendlyName,
4347 SatIPDeviceAttribute *tunerType,
4348 SatIPDeviceAttribute *tunerIndex,
4349 SatIPDeviceList *deviceList,
4350 const CaptureCard &parent) :
4351 m_deviceId(deviceId),
4352 m_friendlyName(friendlyName),
4353 m_tunerType(tunerType),
4354 m_tunerIndex(tunerIndex),
4355 m_deviceList(deviceList),
4356 m_parent(parent)
4357{
4358 setLabel(tr("Available devices"));
4359 setHelpText(tr("Device IP or ID, tuner number and tuner type of available Sat>IP device"));
4360
4361 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
4362 this, &SatIPDeviceIDList::UpdateDevices);
4363};
4364
4365void SatIPDeviceIDList::Load(void)
4366{
4367 clearSelections();
4368
4369 int cardid = m_parent.getCardID();
4370 QString device = CardUtil::GetVideoDevice(cardid);
4371
4372 fillSelections(device);
4373};
4374
4375void SatIPDeviceIDList::UpdateDevices(const QString &v)
4376{
4377 SatIPDevice dev = (*m_deviceList)[v];
4378 m_deviceId->setValue(dev.m_mythDeviceId);
4379 m_friendlyName->setValue(dev.m_friendlyName);
4380 m_tunerType->setValue(dev.m_tunerType);
4381 m_tunerIndex->setValue(dev.m_tunerNo);
4382};
4383
4384void SatIPDeviceIDList::fillSelections(const QString &cur)
4385{
4386 clearSelections();
4387
4388 std::vector<QString> names;
4389 std::vector<QString> devs;
4390 QMap<QString, bool> in_use;
4391
4392 const QString& current = cur;
4393 QString sel;
4394
4395 SatIPDeviceList::iterator it = m_deviceList->begin();
4396 for(; it != m_deviceList->end(); ++it)
4397 {
4398 QString friendlyIdentifier = QString("%1, %2, Tuner #%3")
4399 .arg((*it).m_friendlyName, (*it).m_tunerType, (*it).m_tunerNo);
4400 names.push_back(friendlyIdentifier);
4401
4402 devs.push_back(it.key());
4403 in_use[it.key()] = (*it).m_inUse;
4404 }
4405
4406 for (const auto& it2s : devs)
4407 {
4408 sel = (current == it2s) ? it2s : sel;
4409 }
4410
4411 QString usestr = QString(" -- ");
4412 usestr += tr("Warning: already in use");
4413
4414 for (uint i = 0; i < devs.size(); ++i)
4415 {
4416 const QString& dev = devs[i];
4417 const QString& name = names[i];
4418 bool dev_in_use = (dev == sel) ? false : in_use[devs[i]];
4419 QString desc = name + (dev_in_use ? usestr : "");
4420 addSelection(desc, dev, dev == sel);
4421 }
4422};
4423
4424SatIPDeviceID::SatIPDeviceID(const CaptureCard &parent) :
4425 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, "videodevice")),
4426 m_parent(parent)
4427{
4428 setLabel(tr("Device ID"));
4429 setHelpText(tr("Device ID of the Sat>IP tuner"));
4430 setEnabled(true);
4431 setReadOnly(true);
4432};
4433
4434SatIPDeviceID::~SatIPDeviceID()
4435{
4436 delete GetStorage();
4437}
4438
4439void SatIPDeviceID::Load(void)
4440{
4442};
4443
4444void SatIPDeviceID::SetTuner(const QString &tuner)
4445{
4446 setValue(tuner);
4447};
4448
4449SatIPDeviceAttribute::SatIPDeviceAttribute(const QString& label, const QString& helptext)
4450{
4451 setLabel(label);
4452 setHelpText(helptext);
4453};
4454#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:3280
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:2676
static bool IsUnscanable(const QString &rawtype)
Definition: cardutil.h:160
static uint CreateInputGroup(const QString &name)
Definition: cardutil.cpp:2039
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:1571
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:2081
static QString GetStartChannel(uint inputid)
Definition: cardutil.cpp:1802
static QString GetDeviceLabel(const QString &inputtype, const QString &videodevice)
Definition: cardutil.cpp:2654
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:3209
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:2169
static bool LinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:2118
static QString GetVideoDevice(uint inputid)
Definition: cardutil.h:296
static bool IsInNeedOfExternalInputConf(uint inputid)
Definition: cardutil.cpp:2325
static void ClearVideoDeviceCache()
Definition: cardutil.cpp:447
static uint CloneCard(uint src_inputid, uint dst_inputid)
Definition: cardutil.cpp:1560
static bool IsV4L(const QString &rawtype)
Definition: cardutil.h:147
static std::vector< uint > GetChildInputIDs(uint inputid)
Definition: cardutil.cpp:1381
static QString GetDeviceName(dvb_dev_type_t type, const QString &device)
Definition: cardutil.cpp:2989
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:137
static bool DeleteInput(uint inputid)
Definition: cardutil.cpp:2839
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:2369
static bool DeleteAllInputs(void)
Definition: cardutil.cpp:2909
static QStringList ProbeAudioInputs(const QString &device, const QString &inputtype=QString())
Definition: cardutil.cpp:2558
static uint GetDeviceInputGroup(uint inputid)
Definition: cardutil.cpp:2094
ChannelTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::milliseconds min_val)
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)
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:165
bool Load(uint card_input_id)
Loads configuration chain from DB for specified card input id.
Definition: diseqc.cpp:131
DVB-S device tree class.
Definition: diseqc.h:75
bool Load(const QString &device)
Loads the device tree from the database.
Definition: diseqc.cpp:314
bool Store(uint cardid, const QString &device="")
Stores the device tree to the database.
Definition: diseqc.cpp:425
static bool Exists(int cardid)
Check if a Diseqc device tree exists.
Definition: diseqc.cpp:396
static void InvalidateTrees(void)
Invalidate cached trees.
Definition: diseqc.cpp:248
~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
virtual void addSelection(const QString &label, QString value=QString(), bool select=false)
QString getValueLabel(void) const
virtual 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::milliseconds value, std::chrono::duration< T > min_secs)
SignalTimeout(const CaptureCard &parent, std::chrono::duration< T > 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:175
static bool DeleteSource(uint sourceid)
Definition: sourceutil.cpp:537
static bool DeleteAllSources(void)
Definition: sourceutil.cpp:593
static uint GetChannelCount(uint sourceid)
Definition: sourceutil.cpp:139
static bool UpdateChannelsFromListings(uint sourceid, const QString &inputtype=QString(), bool wait=false)
Definition: sourceutil.cpp:403
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) override
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) override
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
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