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 devs.reserve(m_deviceList->size());
1827 for (auto it = m_deviceList->begin(); it != m_deviceList->end(); ++it)
1828 {
1829 devs.push_back(it.key());
1830 in_use[it.key()] = (*it).m_inUse;
1831 }
1832
1833 QString man_addr = VBoxDeviceIDList::tr("Manually Enter IP Address");
1834 QString sel = man_addr;
1835 devs.push_back(sel);
1836
1837 for (const auto & dev : devs)
1838 sel = (current == dev) ? dev : sel;
1839
1840 QString usestr = QString(" -- ");
1841 usestr += QObject::tr("Warning: already in use");
1842
1843 for (const auto & dev : devs)
1844 {
1845 QString desc = dev + (in_use[dev] ? usestr : "");
1846 addSelection(desc, dev, dev == sel);
1847 }
1848
1849 if (current != cur)
1850 {
1852 }
1853 else if (sel == man_addr && !current.isEmpty())
1854 {
1855 // Populate the proper values for IP address and tuner
1856 QStringList selection = current.split("-");
1857
1858 m_cardIp->SetOldValue(selection.first());
1859 m_cardTuner->SetOldValue(selection.last());
1860
1861 m_cardIp->setValue(selection.first());
1862 m_cardTuner->setValue(selection.last());
1863 }
1864}
1865
1867{
1869
1870 int cardid = m_parent.getCardID();
1871 QString device = CardUtil::GetVideoDevice(cardid);
1872 fillSelections(device);
1873}
1874
1876{
1877 if (v == VBoxDeviceIDList::tr("Manually Enter IP Address"))
1878 {
1879 m_cardIp->setEnabled(true);
1880 m_cardTuner->setEnabled(true);
1881 }
1882 else if (!v.isEmpty())
1883 {
1884 if (m_oldValue == VBoxDeviceIDList::tr("Manually Enter IP Address"))
1885 {
1886 m_cardIp->setEnabled(false);
1887 m_cardTuner->setEnabled(false);
1888 }
1889 m_deviceId->setValue(v);
1890
1891 // Update _cardip and _cardtuner
1893 m_cardTuner->setValue(QString("%1").arg((*m_deviceList)[v].m_tunerNo));
1895 }
1896 m_oldValue = v;
1897};
1898
1899// -----------------------
1900// IPTV Configuration
1901// -----------------------
1902
1904{
1905 public:
1906 explicit IPTVHost(const CaptureCard &parent) :
1907 CaptureCardTextEditSetting(parent, "videodevice")
1908 {
1909 setValue("http://mafreebox.freebox.fr/freeboxtv/playlist.m3u");
1910 setLabel(QObject::tr("M3U URL"));
1912 QObject::tr("URL of M3U file containing RTSP/RTP/UDP/HTTP channel URLs,"
1913 " example for HDHomeRun: http://<ipv4>/lineup.m3u and for Freebox:"
1914 " http://mafreebox.freebox.fr/freeboxtv/playlist.m3u."
1915 ));
1916 }
1917};
1918
1919static void IPTVConfigurationGroup(CaptureCard& parent, CardType& cardType)
1920{
1921 cardType.addTargetedChild("FREEBOX", new IPTVHost(parent));
1922 cardType.addTargetedChild("FREEBOX", new ChannelTimeout(parent, 30s, 1.75s));
1923 cardType.addTargetedChild("FREEBOX", new EmptyAudioDevice(parent));
1924 cardType.addTargetedChild("FREEBOX", new EmptyVBIDevice(parent));
1925}
1926
1928{
1929 public:
1930 explicit ASIDevice(const CaptureCard &parent) :
1931 CaptureCardComboBoxSetting(parent, true, "videodevice")
1932 {
1933 setLabel(QObject::tr("ASI device"));
1934 fillSelections(QString());
1935 };
1936
1940 void fillSelections(const QString &current)
1941 {
1943
1944 // Get devices from filesystem
1945 QStringList sdevs = CardUtil::ProbeVideoDevices("ASI");
1946
1947 // Add current if needed
1948 if (!current.isEmpty() && !sdevs.contains(current))
1949 {
1950 // QList doesn't always play well with std::ranges
1951 // NOLINTNEXTLINE(modernize-use-ranges)
1952 std::stable_sort(sdevs.begin(), sdevs.end());
1953 }
1954
1955 // Get devices from DB
1956 QStringList db = CardUtil::GetVideoDevices("ASI");
1957
1958 // Figure out which physical devices are already in use
1959 // by another card defined in the DB, and select a device
1960 // for new configs (preferring non-conflicing devices).
1961 QMap<QString,bool> in_use;
1962 QString sel = current;
1963 for (const QString& dev : std::as_const(sdevs))
1964 {
1965 in_use[dev] = db.contains(dev);
1966 if (sel.isEmpty() && !in_use[dev])
1967 sel = dev;
1968 }
1969
1970 // Unfortunately all devices are conflicted, select first device.
1971 if (sel.isEmpty() && !sdevs.empty())
1972 sel = sdevs[0];
1973
1974 QString usestr = QString(" -- ");
1975 usestr += QObject::tr("Warning: already in use");
1976
1977 // Add the devices to the UI
1978 bool found = false;
1979 for (const QString& dev : std::as_const(sdevs))
1980 {
1981 QString desc = dev + (in_use[dev] ? usestr : "");
1982 desc = (current == dev) ? dev : desc;
1983 addSelection(desc, dev, dev == sel);
1984 found |= (dev == sel);
1985 }
1986
1987 // If a configured device isn't on the list, add it with warning
1988 if (!found && !current.isEmpty())
1989 {
1990 QString desc = current + " -- " +
1991 QObject::tr("Warning: unable to open");
1992 addSelection(desc, current, true);
1993 }
1994 }
1995
1996 void Load(void) override // StandardSetting
1997 {
1999 addSelection(QString());
2000 GetStorage()->Load();
2002 }
2003};
2004
2006 CardType &cardType):
2007 m_parent(a_parent),
2008 m_device(new ASIDevice(m_parent)),
2009 m_cardInfo(new TransTextEditSetting())
2010{
2011 setVisible(false);
2012 m_cardInfo->setLabel(tr("Status"));
2013 m_cardInfo->setEnabled(false);
2014
2015 cardType.addTargetedChild("ASI", m_device);
2016 cardType.addTargetedChild("ASI", new EmptyAudioDevice(m_parent));
2017 cardType.addTargetedChild("ASI", new EmptyVBIDevice(m_parent));
2018 cardType.addTargetedChild("ASI", m_cardInfo);
2019
2020 connect(m_device, qOverload<const QString&>(&StandardSetting::valueChanged),
2022
2024};
2025
2026void ASIConfigurationGroup::probeCard([[maybe_unused]] const QString &device)
2027{
2028#if CONFIG_ASI
2029 if (device.isEmpty())
2030 {
2031 m_cardInfo->setValue("");
2032 return;
2033 }
2034
2035 if ((m_parent.getCardID() != 0) && m_parent.GetRawCardType() != "ASI")
2036 {
2037 m_cardInfo->setValue("");
2038 return;
2039 }
2040
2041 QString error;
2042 int device_num = CardUtil::GetASIDeviceNumber(device, &error);
2043 if (device_num < 0)
2044 {
2045 m_cardInfo->setValue(tr("Not a valid DVEO ASI card"));
2046 LOG(VB_GENERAL, LOG_WARNING,
2047 "ASIConfigurationGroup::probeCard(), Warning: " + error);
2048 return;
2049 }
2050 m_cardInfo->setValue(tr("Valid DVEO ASI card"));
2051#else
2052 m_cardInfo->setValue(QString("Not compiled with ASI support"));
2053#endif
2054}
2055
2057 CardType& a_cardtype):
2058 m_parent(a_parent),
2059 m_info(new GroupSetting()), m_size(new GroupSetting())
2060{
2061 setVisible(false);
2062 auto *device = new FileDevice(m_parent);
2063 device->setHelpText(tr("A local file used to simulate a recording."
2064 " Leave empty to use MythEvents to trigger an"
2065 " external program to import recording files."));
2066 a_cardtype.addTargetedChild("IMPORT", device);
2067
2068 a_cardtype.addTargetedChild("IMPORT", new EmptyAudioDevice(m_parent));
2069 a_cardtype.addTargetedChild("IMPORT", new EmptyVBIDevice(m_parent));
2070
2071 m_info->setLabel(tr("File info"));
2072 a_cardtype.addTargetedChild("IMPORT", m_info);
2073
2074 m_size->setLabel(tr("File size"));
2075 a_cardtype.addTargetedChild("IMPORT", m_size);
2076
2077 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2079
2080 probeCard(device->getValue());
2081};
2082
2083void ImportConfigurationGroup::probeCard(const QString &device)
2084{
2085 QString ci;
2086 QString cs;
2087 QFileInfo fileInfo(device);
2088
2089 // For convenience, ImportRecorder allows both formats:
2090 if (device.startsWith("file:", Qt::CaseInsensitive))
2091 fileInfo.setFile(device.mid(5));
2092
2093 if (fileInfo.exists())
2094 {
2095 if (fileInfo.isReadable() && (fileInfo.isFile()))
2096 {
2097 ci = HTTPRequest::TestMimeType(fileInfo.absoluteFilePath());
2098 cs = tr("%1 MB").arg(fileInfo.size() / 1024 / 1024);
2099 }
2100 else
2101 {
2102 ci = tr("File not readable");
2103 }
2104 }
2105 else
2106 {
2107 ci = tr("File %1 does not exist").arg(device);
2108 }
2109
2110 m_info->setValue(ci);
2111 m_size->setValue(cs);
2112}
2113
2114// -----------------------
2115// VBox Configuration
2116// -----------------------
2117
2119 (CaptureCard& a_parent, CardType& a_cardtype) :
2120 m_parent(a_parent),
2121 m_desc(new GroupSetting()),
2122 m_deviceId(new VBoxDeviceID(a_parent)),
2123 m_cardIp(new VBoxIP()),
2124 m_cardTuner(new VBoxTunerIndex())
2125{
2126 setVisible(false);
2127
2128 // Fill Device list
2130
2131 m_desc->setLabel(tr("Description"));
2134
2135 a_cardtype.addTargetedChild("VBOX", m_deviceIdList);
2136 a_cardtype.addTargetedChild("VBOX", new EmptyAudioDevice(m_parent));
2137 a_cardtype.addTargetedChild("VBOX", new EmptyVBIDevice(m_parent));
2138 a_cardtype.addTargetedChild("VBOX", m_deviceId);
2139 a_cardtype.addTargetedChild("VBOX", m_desc);
2140 a_cardtype.addTargetedChild("VBOX", m_cardIp);
2141 a_cardtype.addTargetedChild("VBOX", m_cardTuner);
2142 a_cardtype.addTargetedChild("VBOX", new SignalTimeout(m_parent, 7s, 1s));
2143 a_cardtype.addTargetedChild("VBOX", new ChannelTimeout(m_parent, 10s, 1.75s));
2144
2145 connect(m_cardIp, &VBoxIP::NewIP,
2149};
2150
2152{
2153 m_deviceList.clear();
2154
2155 // Find physical devices first
2156 // ProbeVideoDevices returns "deviceid ip tunerno tunertype"
2157 QStringList devs = CardUtil::ProbeVideoDevices("VBOX");
2158
2159 for (const auto & dev : std::as_const(devs))
2160 {
2161 QStringList devinfo = dev.split(" ");
2162 const QString& id = devinfo.at(0);
2163 const QString& ip = devinfo.at(1);
2164 const QString& tunerNo = devinfo.at(2);
2165 const QString& tunerType = devinfo.at(3);
2166
2167 VBoxDevice tmpdevice;
2168 tmpdevice.m_deviceId = id;
2169 tmpdevice.m_desc = CardUtil::GetVBoxdesc(id, ip, tunerNo, tunerType);
2170 tmpdevice.m_cardIp = ip;
2171 tmpdevice.m_inUse = false;
2172 tmpdevice.m_discovered = true;
2173 tmpdevice.m_tunerNo = tunerNo;
2174 tmpdevice.m_tunerType = tunerType;
2175 tmpdevice.m_mythDeviceId = id + "-" + tunerNo + "-" + tunerType;
2176 m_deviceList[tmpdevice.m_mythDeviceId] = tmpdevice;
2177 }
2178
2179 // Now find configured devices
2180
2181 // returns "ip.ip.ip.ip-n-type" or deviceid-n-type values
2182 QStringList db = CardUtil::GetVideoDevices("VBOX");
2183
2184 for (const auto & dev : std::as_const(db))
2185 {
2186 QMap<QString, VBoxDevice>::iterator dit;
2187 dit = m_deviceList.find(dev);
2188
2189 if (dit != m_deviceList.end())
2190 (*dit).m_inUse = true;
2191 }
2192}
2193
2194// -----------------------
2195// Ceton Configuration
2196// -----------------------
2197#if CONFIG_CETON
2198CetonSetting::CetonSetting(QString label, const QString& helptext)
2199{
2200 setLabel(std::move(label));
2201 setHelpText(helptext);
2202 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
2203 this, &CetonSetting::UpdateDevices);
2204}
2205
2206void CetonSetting::UpdateDevices(const QString &v)
2207{
2208 if (isEnabled())
2209 emit NewValue(v);
2210}
2211
2212void CetonSetting::LoadValue(const QString &value)
2213{
2214 setValue(value);
2215}
2216
2217CetonDeviceID::CetonDeviceID(const CaptureCard &parent) :
2218 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, "videodevice")),
2219 m_parent(parent)
2220{
2221 setLabel(tr("Device ID"));
2222 setHelpText(tr("Device ID of Ceton device"));
2223}
2224
2225CetonDeviceID::~CetonDeviceID()
2226{
2227 delete GetStorage();
2228}
2229
2230void CetonDeviceID::SetIP(const QString &ip)
2231{
2232 static const QRegularExpression ipV4Regex
2233 { "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){4}$" };
2234 auto match = ipV4Regex.match(ip + ".");
2235 if (match.hasMatch())
2236 {
2237 m_ip = ip;
2238 setValue(QString("%1-RTP.%3").arg(m_ip, m_tuner));
2239 }
2240}
2241
2242void CetonDeviceID::SetTuner(const QString &tuner)
2243{
2244 static const QRegularExpression oneDigit { "^\\d$" };
2245 auto match = oneDigit.match(tuner);
2246 if (match.hasMatch())
2247 {
2248 m_tuner = tuner;
2249 setValue(QString("%1-RTP.%2").arg(m_ip, m_tuner));
2250 }
2251}
2252
2253void CetonDeviceID::Load(void)
2254{
2255 GetStorage()->Load();
2256 UpdateValues();
2257}
2258
2259void CetonDeviceID::UpdateValues(void)
2260{
2261 static const QRegularExpression newstyle { R"(^([0-9.]+)-(\d|RTP)\.(\d)$)" };
2262 auto match = newstyle.match(getValue());
2263 if (match.hasMatch())
2264 {
2265 emit LoadedIP(match.captured(1));
2266 emit LoadedTuner(match.captured(3));
2267 }
2268}
2269
2270void CetonSetting::CetonConfigurationGroup(CaptureCard& parent, CardType& cardtype)
2271{
2272 auto *deviceid = new CetonDeviceID(parent);
2273 auto *desc = new GroupSetting();
2274 desc->setLabel(tr("CetonConfigurationGroup", "Description"));
2275 auto *ip = new CetonSetting(tr("IP Address"),
2276 tr("IP Address of the Ceton device (192.168.200.1 by default)"));
2277 auto *tuner = new CetonSetting(tr("Tuner"),
2278 tr("Number of the tuner on the Ceton device (first tuner is number 0)"));
2279
2280 cardtype.addTargetedChild("CETON", ip);
2281 cardtype.addTargetedChild("CETON", tuner);
2282 cardtype.addTargetedChild("CETON", deviceid);
2283 cardtype.addTargetedChild("CETON", desc);
2284 cardtype.addTargetedChild("CETON", new SignalTimeout(parent, 1s, 0.25s));
2285 cardtype.addTargetedChild("CETON", new ChannelTimeout(parent, 3s, 1.75s));
2286
2287 QObject::connect(ip, &CetonSetting::NewValue,
2288 deviceid, &CetonDeviceID::SetIP);
2289 QObject::connect(tuner, &CetonSetting::NewValue,
2290 deviceid, &CetonDeviceID::SetTuner);
2291
2292 QObject::connect(deviceid, &CetonDeviceID::LoadedIP,
2293 ip, &CetonSetting::LoadValue);
2294 QObject::connect(deviceid, &CetonDeviceID::LoadedTuner,
2295 tuner, &CetonSetting::LoadValue);
2296}
2297#endif
2298
2299// Override database schema default, set schedgroup false
2301{
2302 public:
2303 explicit SchedGroupFalse(const CaptureCard &parent) :
2305 "schedgroup"))
2306 {
2307 setValue(false);
2308 setVisible(false);
2309 };
2310
2312 {
2313 delete GetStorage();
2314 }
2315};
2316
2318 CardType& cardtype,
2319 const QString &inputtype) :
2320 m_parent(parent),
2321 m_cardInfo(new GroupSetting()),
2322 m_vbiDev(new VBIDevice(m_parent))
2323{
2324 setVisible(false);
2325 QRegularExpression drv { "^(?!ivtv|hdpvr|(saa7164(.*))).*$" };
2326 auto *device = new VideoDevice(m_parent, 0, 15, QString(), drv);
2327
2328 m_cardInfo->setLabel(tr("Probed info"));
2329 m_cardInfo->setReadOnly(true);
2330
2331 cardtype.addTargetedChild(inputtype, device);
2332 cardtype.addTargetedChild(inputtype, m_cardInfo);
2333 cardtype.addTargetedChild(inputtype, m_vbiDev);
2334 cardtype.addTargetedChild(inputtype, new AudioDevice(m_parent));
2335 cardtype.addTargetedChild(inputtype, new AudioRateLimit(m_parent));
2336 cardtype.addTargetedChild(inputtype, new SkipBtAudio(m_parent));
2337
2338 // Override database schema default, set schedgroup false
2339 cardtype.addTargetedChild(inputtype, new SchedGroupFalse(m_parent));
2340
2341 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2343
2344 probeCard(device->getValue());
2345};
2346
2347void V4LConfigurationGroup::probeCard(const QString &device)
2348{
2349 QString cn = tr("Failed to open");
2350 QString ci = cn;
2351 QString dn;
2352
2353 QByteArray adevice = device.toLatin1();
2354 int videofd = open(adevice.constData(), O_RDWR);
2355 if (videofd >= 0)
2356 {
2357 if (!CardUtil::GetV4LInfo(videofd, cn, dn))
2358 ci = cn = tr("Failed to probe");
2359 else if (!dn.isEmpty())
2360 ci = cn + " [" + dn + "]";
2361 close(videofd);
2362 }
2363
2364 m_cardInfo->setValue(ci);
2365 m_vbiDev->setFilter(cn, dn);
2366}
2367
2369 CardType &cardtype) :
2370 m_parent(parent),
2371 m_vbiDevice(new VBIDevice(parent)),
2372 m_cardInfo(new GroupSetting())
2373{
2374 setVisible(false);
2375 QRegularExpression drv { "^(ivtv|(saa7164(.*)))$" };
2376 m_device = new VideoDevice(m_parent, 0, 15, QString(), drv);
2377 m_vbiDevice->setVisible(false);
2378
2379 m_cardInfo->setLabel(tr("Probed info"));
2380 m_cardInfo->setReadOnly(true);
2381
2382 cardtype.addTargetedChild("MPEG", m_device);
2383 cardtype.addTargetedChild("MPEG", m_vbiDevice);
2384 cardtype.addTargetedChild("MPEG", m_cardInfo);
2385 cardtype.addTargetedChild("MPEG", new ChannelTimeout(m_parent, 12s, 2s));
2386
2387 // Override database schema default, set schedgroup false
2388 cardtype.addTargetedChild("MPEG", new SchedGroupFalse(m_parent));
2389
2390 connect(m_device, qOverload<const QString&>(&StandardSetting::valueChanged),
2392
2394}
2395
2396void MPEGConfigurationGroup::probeCard(const QString &device)
2397{
2398 QString cn = tr("Failed to open");
2399 QString ci = cn;
2400 QString dn;
2401
2402 QByteArray adevice = device.toLatin1();
2403 int videofd = open(adevice.constData(), O_RDWR);
2404 if (videofd >= 0)
2405 {
2406 if (!CardUtil::GetV4LInfo(videofd, cn, dn))
2407 ci = cn = tr("Failed to probe");
2408 else if (!dn.isEmpty())
2409 ci = cn + " [" + dn + "]";
2410 close(videofd);
2411 }
2412
2413 m_cardInfo->setValue(ci);
2414 m_vbiDevice->setVisible(dn!="ivtv");
2415 m_vbiDevice->setFilter(cn, dn);
2416}
2417
2419 CardType &a_cardtype) :
2420 m_parent(a_parent),
2421 m_info(new GroupSetting()), m_size(new GroupSetting())
2422{
2423 setVisible(false);
2424 auto *device = new FileDevice(m_parent);
2425 device->setHelpText(tr("A local MPEG file used to simulate a recording."));
2426
2427 a_cardtype.addTargetedChild("DEMO", device);
2428
2429 a_cardtype.addTargetedChild("DEMO", new EmptyAudioDevice(m_parent));
2430 a_cardtype.addTargetedChild("DEMO", new EmptyVBIDevice(m_parent));
2431
2432 m_info->setLabel(tr("File info"));
2433 a_cardtype.addTargetedChild("DEMO", m_info);
2434
2435 m_size->setLabel(tr("File size"));
2436 a_cardtype.addTargetedChild("DEMO", m_size);
2437
2438 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2440
2441 probeCard(device->getValue());
2442}
2443
2444void DemoConfigurationGroup::probeCard(const QString &device)
2445{
2446 QString ci;
2447 QString cs;
2448 QFileInfo fileInfo(device);
2449 if (fileInfo.exists())
2450 {
2451 if (fileInfo.isReadable() && (fileInfo.isFile()))
2452 {
2453 ci = HTTPRequest::TestMimeType(fileInfo.absoluteFilePath());
2454 cs = tr("%1 MB").arg(fileInfo.size() / 1024 / 1024);
2455 }
2456 else
2457 {
2458 ci = tr("File not readable");
2459 }
2460 }
2461 else
2462 {
2463 ci = tr("File does not exist");
2464 }
2465
2466 m_info->setValue(ci);
2467 m_size->setValue(cs);
2468}
2469
2470#ifndef Q_OS_WINDOWS
2471ExternalConfigurationGroup::ExternalConfigurationGroup(CaptureCard &a_parent,
2472 CardType &a_cardtype) :
2473 m_parent(a_parent),
2474 m_info(new GroupSetting())
2475{
2476 setVisible(false);
2477 auto *device = new CommandPath(m_parent);
2478 device->setLabel(tr("Command path"));
2479 device->setHelpText(tr("A 'black box' application controlled via stdin, status on "
2480 "stderr and TransportStream read from stdout.\n"
2481 "Use absolute path or path relative to the current directory."));
2482 a_cardtype.addTargetedChild("EXTERNAL", device);
2483
2484 m_info->setLabel(tr("File info"));
2485 a_cardtype.addTargetedChild("EXTERNAL", m_info);
2486
2487 a_cardtype.addTargetedChild("EXTERNAL",
2488 new ChannelTimeout(m_parent, 20s, 1.75s));
2489
2490 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2491 this, &ExternalConfigurationGroup::probeApp);
2492
2493 probeApp(device->getValue());
2494}
2495
2496void ExternalConfigurationGroup::probeApp(const QString & path)
2497{
2498 int idx1 = path.startsWith("file:", Qt::CaseInsensitive) ? 5 : 0;
2499 int idx2 = path.indexOf(' ', idx1);
2500
2501 QString ci;
2502 QFileInfo fileInfo(path.mid(idx1, idx2 - idx1));
2503
2504 if (fileInfo.exists())
2505 {
2506 ci = tr("File '%1' is valid.").arg(fileInfo.absoluteFilePath());
2507 if (!fileInfo.isReadable() || !fileInfo.isFile())
2508 ci = tr("WARNING: File '%1' is not readable.")
2509 .arg(fileInfo.absoluteFilePath());
2510 if (!fileInfo.isExecutable())
2511 ci = tr("WARNING: File '%1' is not executable.")
2512 .arg(fileInfo.absoluteFilePath());
2513 }
2514 else
2515 {
2516 ci = tr("WARNING: File '%1' does not exist.")
2517 .arg(fileInfo.absoluteFilePath());
2518 }
2519
2520 m_info->setValue(ci);
2521 m_info->setHelpText(ci);
2522}
2523#endif // !defined( Q_OS_WINDOWS )
2524
2526 CardType &a_cardtype) :
2527 m_parent(a_parent), m_cardInfo(new GroupSetting()),
2528 m_audioInput(new TunerCardAudioInput(m_parent, QString(), "HDPVR"))
2529{
2530 setVisible(false);
2531
2532 auto *device = new VideoDevice(m_parent, 0, 15, QString(),
2533 QRegularExpression("^hdpvr$"));
2534
2535 m_cardInfo->setLabel(tr("Probed info"));
2536 m_cardInfo->setReadOnly(true);
2537
2538 a_cardtype.addTargetedChild("HDPVR", device);
2539 a_cardtype.addTargetedChild("HDPVR", new EmptyAudioDevice(m_parent));
2540 a_cardtype.addTargetedChild("HDPVR", new EmptyVBIDevice(m_parent));
2541 a_cardtype.addTargetedChild("HDPVR", m_cardInfo);
2542 a_cardtype.addTargetedChild("HDPVR", m_audioInput);
2543 a_cardtype.addTargetedChild("HDPVR", new ChannelTimeout(m_parent, 15s, 2s));
2544
2545 // Override database schema default, set schedgroup false
2546 a_cardtype.addTargetedChild("HDPVR", new SchedGroupFalse(m_parent));
2547
2548 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2550
2551 probeCard(device->getValue());
2552}
2553
2554void HDPVRConfigurationGroup::probeCard(const QString &device)
2555{
2556 QString cn = tr("Failed to open");
2557 QString ci = cn;
2558 QString dn;
2559
2560 int videofd = open(device.toLocal8Bit().constData(), O_RDWR);
2561 if (videofd >= 0)
2562 {
2563 if (!CardUtil::GetV4LInfo(videofd, cn, dn))
2564 ci = cn = tr("Failed to probe");
2565 else if (!dn.isEmpty())
2566 ci = cn + " [" + dn + "]";
2567 close(videofd);
2569 }
2570
2571 m_cardInfo->setValue(ci);
2572}
2573
2575 m_parent(parent),
2576 m_cardInfo(new GroupSetting())
2577{
2578 setVisible(false);
2579
2580 m_device = new VideoDevice(m_parent, 0, 15);
2581
2582 setLabel(QObject::tr("V4L2 encoder devices (multirec capable)"));
2583
2584 m_cardInfo->setLabel(tr("Probed info"));
2585 m_cardInfo->setReadOnly(true);
2586
2587 cardtype.addTargetedChild("V4L2ENC", m_device);
2588 cardtype.addTargetedChild("V4L2ENC", m_cardInfo);
2589
2590 // Override database schema default, set schedgroup false
2591 cardtype.addTargetedChild("V4L2ENC", new SchedGroupFalse(m_parent));
2592
2593 connect(m_device, qOverload<const QString&>(&StandardSetting::valueChanged),
2595
2596 const QString &device_name = m_device->getValue();
2597 if (!device_name.isEmpty())
2598 probeCard(device_name);
2599}
2600
2601void V4L2encGroup::probeCard([[maybe_unused]] const QString &device_name)
2602{
2603#if CONFIG_V4L2
2604 QString card_name = tr("Failed to open");
2605 QString card_info = card_name;
2606 V4L2util v4l2(device_name);
2607
2608 if (!v4l2.IsOpen())
2609 {
2610 m_driverName = tr("Failed to probe");
2611 return;
2612 }
2613 m_driverName = v4l2.DriverName();
2614 card_name = v4l2.CardName();
2615
2616 if (!m_driverName.isEmpty())
2617 card_info = card_name + " [" + m_driverName + "]";
2618
2619 m_cardInfo->setValue(card_info);
2620
2621 if (m_device->getSubSettings()->empty())
2622 {
2623 auto* audioinput = new TunerCardAudioInput(m_parent, QString(), "V4L2");
2624 if (audioinput->fillSelections(device_name) > 1)
2625 {
2626 audioinput->setName("AudioInput");
2628 }
2629 else
2630 {
2631 delete audioinput;
2632 }
2633
2634 if (v4l2.HasSlicedVBI())
2635 {
2636 auto* vbidev = new VBIDevice(m_parent);
2637 if (vbidev->setFilter(card_name, m_driverName) > 0)
2638 {
2639 vbidev->setName("VBIDevice");
2641 }
2642 else
2643 {
2644 delete vbidev;
2645 }
2646 }
2647
2650 new ChannelTimeout(m_parent, 15s, 2s));
2651 }
2652#endif // CONFIG_V4L2
2653}
2654
2656{
2657 setLabel(QObject::tr("Capture Card Setup"));
2658
2659 auto* cardtype = new CardType(parent);
2660 parent.addChild(cardtype);
2661
2662#if CONFIG_DVB
2663 cardtype->addTargetedChild("DVB",
2664 new DVBConfigurationGroup(parent, *cardtype));
2665#endif // CONFIG_DVB
2666
2667#if CONFIG_V4L2
2668 cardtype->addTargetedChild("HDPVR",
2669 new HDPVRConfigurationGroup(parent, *cardtype));
2670#endif // CONFIG_V4L2
2671
2672#if CONFIG_HDHOMERUN
2673 cardtype->addTargetedChild("HDHOMERUN",
2674 new HDHomeRunConfigurationGroup(parent, *cardtype));
2675#endif // CONFIG_HDHOMERUN
2676
2677#if CONFIG_VBOX
2678 cardtype->addTargetedChild("VBOX",
2679 new VBoxConfigurationGroup(parent, *cardtype));
2680#endif // CONFIG_VBOX
2681
2682#if CONFIG_SATIP
2683 cardtype->addTargetedChild("SATIP",
2684 new SatIPConfigurationGroup(parent, *cardtype));
2685#endif // CONFIG_SATIP
2686
2687#if CONFIG_FIREWIRE
2688 FirewireConfigurationGroup(parent, *cardtype);
2689#endif // CONFIG_FIREWIRE
2690
2691#if CONFIG_CETON
2692 CetonSetting::CetonConfigurationGroup(parent, *cardtype);
2693#endif // CONFIG_CETON
2694
2695#if CONFIG_IPTV
2696 IPTVConfigurationGroup(parent, *cardtype);
2697#endif // CONFIG_IPTV
2698
2699#if CONFIG_V4L2
2700 cardtype->addTargetedChild("V4L2ENC", new V4L2encGroup(parent, *cardtype));
2701 cardtype->addTargetedChild("V4L",
2702 new V4LConfigurationGroup(parent, *cardtype, "V4L"));
2703 cardtype->addTargetedChild("MJPEG",
2704 new V4LConfigurationGroup(parent, *cardtype, "MJPEG"));
2705 cardtype->addTargetedChild("GO7007",
2706 new V4LConfigurationGroup(parent, *cardtype, "GO7007"));
2707 cardtype->addTargetedChild("MPEG",
2708 new MPEGConfigurationGroup(parent, *cardtype));
2709#endif // CONFIG_V4L2
2710
2711#if CONFIG_ASI
2712 cardtype->addTargetedChild("ASI",
2713 new ASIConfigurationGroup(parent, *cardtype));
2714#endif // CONFIG_ASI
2715
2716 // for testing without any actual tuner hardware:
2717 cardtype->addTargetedChild("IMPORT",
2718 new ImportConfigurationGroup(parent, *cardtype));
2719 cardtype->addTargetedChild("DEMO",
2720 new DemoConfigurationGroup(parent, *cardtype));
2721#ifndef Q_OS_WINDOWS
2722 cardtype->addTargetedChild("EXTERNAL",
2723 new ExternalConfigurationGroup(parent,
2724 *cardtype));
2725#endif
2726}
2727
2728CaptureCard::CaptureCard(bool use_card_group)
2729 : m_id(new ID)
2730{
2731 addChild(m_id);
2732 if (use_card_group)
2733 CaptureCardGroup(*this);
2734 addChild(new Hostname(*this));
2735}
2736
2738{
2739 int cardid = getCardID();
2740 if (cardid <= 0)
2741 return {};
2742 return CardUtil::GetRawInputType(cardid);
2743}
2744
2746{
2748 QString qstr =
2749 "SELECT cardid, videodevice, cardtype, displayname "
2750 "FROM capturecard "
2751 "WHERE hostname = :HOSTNAME AND parentid = 0 "
2752 "ORDER BY cardid";
2753
2754 query.prepare(qstr);
2755 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
2756
2757 if (!query.exec())
2758 {
2759 MythDB::DBError("CaptureCard::fillSelections", query);
2760 return;
2761 }
2762
2764
2765 while (query.next())
2766 {
2767 uint cardid = query.value(0).toUInt();
2768 QString videodevice = query.value(1).toString();
2769 QString cardtype = query.value(2).toString();
2770 QString displayname = query.value(3).toString();
2771
2772 QString label = QString("%1 (%2)")
2773 .arg(CardUtil::GetDeviceLabel(cardtype, videodevice), displayname);
2774
2775 auto *card = new CaptureCard();
2776 card->loadByID(cardid);
2777 card->setLabel(label);
2778 setting->addChild(card);
2779 }
2780}
2781
2783{
2784 m_id->setValue(cardid);
2785 Load();
2786}
2787
2789{
2790 return true;
2791}
2792
2794{
2796}
2797
2798
2800{
2801 uint init_cardid = getCardID();
2802 QString init_dev = CardUtil::GetVideoDevice(init_cardid);
2803
2805
2807
2809
2810 uint cardid = getCardID();
2811 QString type = CardUtil::GetRawInputType(cardid);
2812 QString dev = CardUtil::GetVideoDevice(cardid);
2813
2814 if (dev != init_dev)
2815 {
2816 if (!init_dev.isEmpty())
2817 {
2818 uint init_groupid = CardUtil::GetDeviceInputGroup(init_cardid);
2819 CardUtil::UnlinkInputGroup(init_cardid, init_groupid);
2820 }
2821 if (!dev.isEmpty())
2822 {
2823 uint groupid =
2825 gCoreContext->GetHostName(), dev);
2826 CardUtil::LinkInputGroup(cardid, groupid);
2827 CardUtil::UnlinkInputGroup(0, groupid);
2828 }
2829 }
2830
2831 // Handle any cloning we may need to do
2833 {
2834 std::vector<uint> clones = CardUtil::GetChildInputIDs(cardid);
2835 for (uint clone : clones)
2836 CardUtil::CloneCard(cardid, clone);
2837 }
2838}
2839
2841{
2842 if (getCardID() == 0)
2843 {
2844 Save();
2845 Load();
2846 }
2847}
2848
2850 CaptureCardComboBoxSetting(parent, false, "cardtype")
2851{
2852 setLabel(QObject::tr("Card type"));
2853 setHelpText(QObject::tr("Change the cardtype to the appropriate type for "
2854 "the capture card you are configuring."));
2855 fillSelections(this);
2856}
2857
2859{
2860#if CONFIG_DVB
2861 setting->addSelection(
2862 QObject::tr("DVB-T/S/C, ATSC or ISDB-T tuner card"), "DVB");
2863#endif // CONFIG_DVB
2864
2865#if CONFIG_V4L2
2866 setting->addSelection(
2867 QObject::tr("V4L2 encoder"), "V4L2ENC");
2868 setting->addSelection(
2869 QObject::tr("HD-PVR H.264 encoder"), "HDPVR");
2870#endif // CONFIG_V4L2
2871
2872#if CONFIG_HDHOMERUN
2873 setting->addSelection(
2874 QObject::tr("HDHomeRun networked tuner"), "HDHOMERUN");
2875#endif // CONFIG_HDHOMERUN
2876
2877#if CONFIG_SATIP
2878 setting->addSelection(
2879 QObject::tr("Sat>IP networked tuner"), "SATIP");
2880#endif // CONFIG_SATIP
2881
2882#if CONFIG_VBOX
2883 setting->addSelection(
2884 QObject::tr("V@Box TV Gateway networked tuner"), "VBOX");
2885#endif // CONFIG_VBOX
2886
2887#if CONFIG_FIREWIRE
2888 setting->addSelection(
2889 QObject::tr("FireWire cable box"), "FIREWIRE");
2890#endif // CONFIG_FIREWIRE
2891
2892#if CONFIG_CETON
2893 setting->addSelection(
2894 QObject::tr("Ceton Cablecard tuner"), "CETON");
2895#endif // CONFIG_CETON
2896
2897#if CONFIG_IPTV
2898 setting->addSelection(QObject::tr("IPTV recorder"), "FREEBOX");
2899#endif // CONFIG_IPTV
2900
2901#if CONFIG_V4L2
2902 setting->addSelection(
2903 QObject::tr("Analog to MPEG-2 encoder card (PVR-150/250/350, etc)"), "MPEG");
2904 setting->addSelection(
2905 QObject::tr("Analog to MJPEG encoder card (Matrox G200, DC10, etc)"), "MJPEG");
2906 setting->addSelection(
2907 QObject::tr("Analog to MPEG-4 encoder (Plextor ConvertX USB, etc)"),
2908 "GO7007");
2909 setting->addSelection(
2910 QObject::tr("Analog capture card"), "V4L");
2911#endif // CONFIG_V4L2
2912
2913#if CONFIG_ASI
2914 setting->addSelection(QObject::tr("DVEO ASI recorder"), "ASI");
2915#endif
2916
2917 setting->addSelection(QObject::tr("Import test recorder"), "IMPORT");
2918 setting->addSelection(QObject::tr("Demo test recorder"), "DEMO");
2919#ifndef Q_OS_WINDOWS
2920 setting->addSelection(QObject::tr("External (black box) recorder"),
2921 "EXTERNAL");
2922#endif
2923}
2924
2926 StandardSetting(new CaptureCardDBStorage(this, parent, "hostname"))
2927{
2928 setVisible(false);
2930}
2931
2933{
2934 delete GetStorage();
2935}
2936
2938{
2939 public:
2940 explicit InputName(const CardInput &parent) :
2941 MythUIComboBoxSetting(new CardInputDBStorage(this, parent, "inputname"))
2942 {
2943 setLabel(QObject::tr("Input name"));
2944 };
2945
2946 ~InputName() override
2947 {
2948 delete GetStorage();
2949 }
2950
2951 void Load(void) override // StandardSetting
2952 {
2955 };
2956
2958 clearSelections();
2959 addSelection(QObject::tr("(None)"), "None");
2960 auto *storage = dynamic_cast<CardInputDBStorage*>(GetStorage());
2961 if (storage == nullptr)
2962 return;
2963 uint cardid = storage->getInputID();
2964 QString type = CardUtil::GetRawInputType(cardid);
2965 QString device = CardUtil::GetVideoDevice(cardid);
2966 QStringList inputs;
2967 CardUtil::GetDeviceInputNames(device, type, inputs);
2968 while (!inputs.isEmpty())
2969 {
2970 addSelection(inputs.front());
2971 inputs.pop_front();
2972 }
2973 };
2974};
2975
2977{
2978 public:
2980 {
2981 setLabel(QObject::tr("Delivery system"));
2982 setHelpText(QObject::tr(
2983 "This shows the delivery system (modulation), for instance DVB-T2, "
2984 "that you have selected when you configured the capture card. "
2985 "This must be the same as the modulation used by the video source. "));
2986 };
2987};
2988
2990{
2992
2993 public:
2994 explicit InputDisplayName(const CardInput &parent) :
2995 MythUITextEditSetting(new CardInputDBStorage(this, parent, "displayname")), m_parent(parent)
2996 {
2997 setLabel(QObject::tr("Display name"));
2998 setHelpText(QObject::tr(
2999 "This name is displayed on screen when Live TV begins "
3000 "and in various other places. Make sure the last two "
3001 "characters are unique for each input or use a "
3002 "slash ('/') to designate the unique portion."));
3003 };
3004
3006 {
3007 delete GetStorage();
3008 }
3009 void Load(void) override {
3011 if (getValue().isEmpty())
3012 setValue(tr("Input %1").arg(m_parent.getInputID()));
3013 }
3014 private:
3016};
3017
3019{
3020 public:
3021 CardInputComboBoxSetting(const CardInput &parent, const QString &setting) :
3022 MythUIComboBoxSetting(new CardInputDBStorage(this, parent, setting))
3023 {
3024 }
3025
3027 {
3028 delete GetStorage();
3029 }
3030};
3031
3033{
3034 public:
3035 explicit SourceID(const CardInput &parent) :
3036 CardInputComboBoxSetting(parent, "sourceid")
3037 {
3038 setLabel(QObject::tr("Video source"));
3039 addSelection(QObject::tr("(None)"), "0");
3040 };
3041
3042 void Load(void) override // StandardSetting
3043 {
3046 };
3047
3049 clearSelections();
3050 addSelection(QObject::tr("(None)"), "0");
3052 };
3053};
3054
3056{
3057 public:
3058 InputGroup(const CardInput &parent, uint group_num) :
3059 m_cardInput(parent),
3060 m_groupNum(group_num)
3061 {
3062 setLabel(QObject::tr("Input group") +
3063 QString(" %1").arg(m_groupNum + 1));
3064 setHelpText(QObject::tr(
3065 "Leave as 'Generic' unless this input is shared with "
3066 "another device. Only one of the inputs in an input "
3067 "group will be allowed to record at any given time."));
3068 }
3069
3070 void Load(void) override; // StandardSetting
3071
3072 void Save(void) override // StandardSetting
3073 {
3074 uint inputid = m_cardInput.getInputID();
3075 uint new_groupid = getValue().toUInt();
3076
3077 if (m_groupId && (m_groupId != new_groupid))
3078 CardUtil::UnlinkInputGroup(inputid, m_groupId);
3079
3080 if (new_groupid)
3081 CardUtil::LinkInputGroup(inputid, new_groupid);
3082 }
3083
3084 virtual void Save(const QString& /*destination*/) { Save(); }
3085
3086 private:
3089 uint m_groupId {0};
3090};
3091
3093{
3094#if 0
3095 LOG(VB_GENERAL, LOG_DEBUG, QString("InputGroup::Load() %1 %2")
3096 .arg(m_groupNum).arg(m_cardInput.getInputID()));
3097#endif
3098
3099 uint inputid = m_cardInput.getInputID();
3100 QMap<uint, uint> grpcnt;
3101 std::vector<QString> names;
3102 std::vector<uint> grpid;
3103 std::vector<uint> selected_groupids;
3104
3105 names.push_back(QObject::tr("Generic"));
3106 grpid.push_back(0);
3107 grpcnt[0]++;
3108
3110 query.prepare(
3111 "SELECT cardinputid, inputgroupid, inputgroupname "
3112 "FROM inputgroup "
3113 "WHERE inputgroupname LIKE 'user:%' "
3114 "ORDER BY inputgroupid, cardinputid, inputgroupname");
3115
3116 if (!query.exec())
3117 {
3118 MythDB::DBError("InputGroup::Load()", query);
3119 }
3120 else
3121 {
3122 while (query.next())
3123 {
3124 uint groupid = query.value(1).toUInt();
3125 if ((inputid != 0U) && (query.value(0).toUInt() == inputid))
3126 selected_groupids.push_back(groupid);
3127
3128 grpcnt[groupid]++;
3129
3130 if (grpcnt[groupid] == 1)
3131 {
3132 names.push_back(query.value(2).toString().mid(5, -1));
3133 grpid.push_back(groupid);
3134 }
3135 }
3136 }
3137
3138 // makes sure we select something
3139 m_groupId = 0;
3140 if (m_groupNum < selected_groupids.size())
3141 m_groupId = selected_groupids[m_groupNum];
3142
3143#if 0
3144 LOG(VB_GENERAL, LOG_DEBUG, QString("Group num: %1 id: %2")
3145 .arg(m_groupNum).arg(m_groupId));
3146 {
3147 QString msg;
3148 for (uint i = 0; i < selected_groupids.size(); i++)
3149 msg += QString("%1 ").arg(selected_groupids[i]);
3150 LOG(VB_GENERAL, LOG_DEBUG, msg);
3151 }
3152#endif
3153
3154 // add selections to combobox
3155 clearSelections();
3156 uint index = 0;
3157 for (size_t i = 0; i < names.size(); i++)
3158 {
3159 bool sel = (m_groupId == grpid[i]);
3160 index = sel ? i : index;
3161
3162#if 0
3163 LOG(VB_GENERAL, LOG_DEBUG, QString("grpid %1, name '%2', i %3, s %4")
3164 .arg(grpid[i]).arg(names[i]) .arg(index).arg(sel ? "T" : "F"));
3165#endif
3166
3167 addSelection(names[i], QString::number(grpid[i]), sel);
3168 }
3169
3170#if 0
3171 LOG(VB_GENERAL, LOG_DEBUG, QString("Group index: %1").arg(index));
3172#endif
3173
3174 if (!names.empty())
3175 setValue(index);
3176
3178}
3179
3181{
3182 public:
3183 explicit QuickTune(const CardInput &parent) :
3184 CardInputComboBoxSetting(parent, "quicktune")
3185 {
3186 setLabel(QObject::tr("Use quick tuning"));
3187 addSelection(QObject::tr("Never"), "0", true);
3188 addSelection(QObject::tr("Live TV only"), "1", false);
3189 addSelection(QObject::tr("Always"), "2", false);
3190 setHelpText(QObject::tr(
3191 "If enabled, MythTV will tune using only the "
3192 "MPEG program number. The program numbers "
3193 "change more often than DVB or ATSC tuning "
3194 "parameters, so this is slightly less reliable. "
3195 "This will also inhibit EIT gathering during "
3196 "Live TV and recording."));
3197 };
3198};
3199
3201{
3202 public:
3203 explicit ExternalChannelCommand(const CardInput &parent) :
3204 MythUITextEditSetting(new CardInputDBStorage(this, parent, "externalcommand"))
3205 {
3206 setLabel(QObject::tr("External channel change command"));
3207 setValue("");
3208 setHelpText(QObject::tr("If specified, this command will be run to "
3209 "change the channel for inputs which have an external "
3210 "tuner device such as a cable box. The first argument "
3211 "will be the channel number."));
3212 };
3213
3215 {
3216 delete GetStorage();
3217 }
3218};
3219
3221{
3222 public:
3223 explicit PresetTuner(const CardInput &parent) :
3224 MythUITextEditSetting(new CardInputDBStorage(this, parent, "tunechan"))
3225 {
3226 setLabel(QObject::tr("Preset tuner to channel"));
3227 setValue("");
3228 setHelpText(QObject::tr("Leave this blank unless you have an external "
3229 "tuner that is connected to the tuner input of your card. "
3230 "If so, you will need to specify the preset channel for "
3231 "the signal (normally 3 or 4)."));
3232 };
3233
3234 ~PresetTuner() override
3235 {
3236 delete GetStorage();
3237 }
3238};
3239
3240void StartingChannel::SetSourceID(const QString &sourceid)
3241{
3242 clearSelections();
3243 if (sourceid.isEmpty() || !sourceid.toUInt())
3244 return;
3245
3246 // Get the existing starting channel
3247 auto *storage = dynamic_cast<CardInputDBStorage*>(GetStorage());
3248 if (storage == nullptr)
3249 return;
3250 int inputId = storage->getInputID();
3251 QString startChan = CardUtil::GetStartChannel(inputId);
3252
3253 ChannelInfoList channels = ChannelUtil::GetAllChannels(sourceid.toUInt());
3254
3255 if (channels.empty())
3256 {
3257 addSelection(tr("Please add channels to this source"),
3258 startChan.isEmpty() ? "0" : startChan);
3259 return;
3260 }
3261
3262 // If there are channels sort them, then add theme
3263 // (selecting the old start channel if it is there).
3264 QString order = gCoreContext->GetSetting("ChannelOrdering", "channum");
3265 ChannelUtil::SortChannels(channels, order);
3266 bool has_visible = false;
3267 for (size_t i = 0; i < channels.size() && !has_visible; i++)
3268 has_visible |= channels[i].m_visible;
3269
3270 for (auto & channel : channels)
3271 {
3272 const QString channum = channel.m_chanNum;
3273 bool sel = channum == startChan;
3274 if (!has_visible || channel.m_visible || sel)
3275 {
3276 addSelection(channum, channum, sel);
3277 }
3278 }
3279}
3280
3282{
3283 public:
3284 explicit InputPriority(const CardInput &parent) :
3285 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "recpriority"),
3286 -99, 99, 1)
3287 {
3288 setLabel(QObject::tr("Input priority"));
3289 setValue(0);
3290 setHelpText(QObject::tr("If the input priority is not equal for "
3291 "all inputs, the scheduler may choose to record a show "
3292 "at a later time so that it can record on an input with "
3293 "a higher value."));
3294 };
3295
3297 {
3298 delete GetStorage();
3299 }
3300};
3301
3303{
3304 public:
3305 ScheduleOrder(const CardInput &parent, int _value) :
3306 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "schedorder"),
3307 0, 99, 1)
3308 {
3309 setLabel(QObject::tr("Schedule order"));
3310 setValue(_value);
3311 setHelpText(QObject::tr("If priorities and other factors are equal "
3312 "the scheduler will choose the available "
3313 "input with the lowest, non-zero value. "
3314 "Setting this value to zero will make the "
3315 "input unavailable to the scheduler."));
3316 };
3317
3319 {
3320 delete GetStorage();
3321 }
3322};
3323
3325{
3326 public:
3327 LiveTVOrder(const CardInput &parent, int _value) :
3328 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "livetvorder"),
3329 0, 99, 1)
3330 {
3331 setLabel(QObject::tr("Live TV order"));
3332 setValue(_value);
3333 setHelpText(QObject::tr("When entering Live TV, the available, local "
3334 "input with the lowest, non-zero value will "
3335 "be used. If no local inputs are available, "
3336 "the available, remote input with the lowest, "
3337 "non-zero value will be used. "
3338 "Setting this value to zero will make the "
3339 "input unavailable to live TV."));
3340 };
3341
3342 ~LiveTVOrder() override
3343 {
3344 delete GetStorage();
3345 }
3346};
3347
3349{
3350 public:
3351 explicit DishNetEIT(const CardInput &parent) :
3353 "dishnet_eit"))
3354 {
3355 setLabel(QObject::tr("Use DishNet long-term EIT data"));
3356 setValue(false);
3358 QObject::tr(
3359 "If you point your satellite dish toward DishNet's birds, "
3360 "you may wish to enable this feature. For best results, "
3361 "enable general EIT collection as well."));
3362 };
3363
3364 ~DishNetEIT() override
3365 {
3366 delete GetStorage();
3367 }
3368};
3369
3370CardInput::CardInput(const QString & cardtype, const QString & device,
3371 int _cardid) :
3372 m_id(new ID()),
3373 m_inputName(new InputName(*this)),
3374 m_sourceId(new SourceID(*this)),
3375 m_startChan(new StartingChannel(*this)),
3376 m_scan(new ButtonStandardSetting(tr("Scan for channels"))),
3377 m_srcFetch(new ButtonStandardSetting(tr("Fetch channels from listings source"))),
3378 m_externalInputSettings(new DiSEqCDevSettings()),
3379 m_inputGrp0(new InputGroup(*this, 0)),
3380 m_inputGrp1(new InputGroup(*this, 1))
3381{
3382 addChild(m_id);
3383
3385 {
3387 _cardid, true));
3388 }
3389
3390 // Delivery system for DVB, input name for other,
3391 // same field capturecard/inputname for both
3392 if ("DVB" == cardtype)
3393 {
3394 auto *ds = new DeliverySystem();
3395 ds->setValue(CardUtil::GetDeliverySystemFromDB(_cardid));
3396 addChild(ds);
3397 }
3398 else if (CardUtil::IsV4L(cardtype))
3399 {
3401 }
3402 addChild(new InputDisplayName(*this));
3404
3405 if (CardUtil::IsEncoder(cardtype) || CardUtil::IsUnscanable(cardtype))
3406 {
3407 addChild(new ExternalChannelCommand(*this));
3408 if (CardUtil::HasTuner(cardtype, device))
3409 addChild(new PresetTuner(*this));
3410 }
3411 else
3412 {
3413 addChild(new QuickTune(*this));
3414 if ("DVB" == cardtype)
3415 addChild(new DishNetEIT(*this));
3416 }
3417
3419 tr("Use channel scanner to find channels for this input."));
3420
3422 tr("This uses the listings data source to "
3423 "provide the channels for this input.") + " " +
3424 tr("This can take a long time to run."));
3425
3428
3430
3431 auto *interact = new GroupSetting();
3432
3433 interact->setLabel(QObject::tr("Interactions between inputs"));
3434 if (CardUtil::IsTunerSharingCapable(cardtype))
3435 {
3436 m_instanceCount = new InstanceCount(*this);
3437 interact->addChild(m_instanceCount);
3438 m_schedGroup = new SchedGroup(*this);
3439 interact->addChild(m_schedGroup);
3440 }
3441 interact->addChild(new InputPriority(*this));
3442 interact->addChild(new ScheduleOrder(*this, _cardid));
3443 interact->addChild(new LiveTVOrder(*this, _cardid));
3444
3445 auto *ingrpbtn =
3446 new ButtonStandardSetting(QObject::tr("Create a New Input Group"));
3447 ingrpbtn->setHelpText(
3448 QObject::tr("Input groups are only needed when two or more cards "
3449 "share the same resource such as a FireWire card and "
3450 "an analog card input controlling the same set top box."));
3451 interact->addChild(ingrpbtn);
3452 interact->addChild(m_inputGrp0);
3453 interact->addChild(m_inputGrp1);
3454
3455 addChild(interact);
3456
3457 setObjectName("CardInput");
3458 SetSourceID("-1");
3459
3462 connect(m_sourceId, qOverload<const QString&>(&StandardSetting::valueChanged),
3464 connect(m_sourceId, qOverload<const QString&>(&StandardSetting::valueChanged),
3465 this, &CardInput::SetSourceID);
3466 connect(ingrpbtn, &ButtonStandardSetting::clicked,
3468}
3469
3471{
3473 {
3475 m_externalInputSettings = nullptr;
3476 }
3477}
3478
3479void CardInput::SetSourceID(const QString &sourceid)
3480{
3481 uint cid = m_id->getValue().toUInt();
3482 QString raw_card_type = CardUtil::GetRawInputType(cid);
3483 bool enable = (sourceid.toInt() > 0);
3484 m_scan->setEnabled(enable && !raw_card_type.isEmpty() &&
3485 !CardUtil::IsUnscanable(raw_card_type));
3486 m_srcFetch->setEnabled(enable);
3487}
3488
3489QString CardInput::getSourceName(void) const
3490{
3491 return m_sourceId->getValueLabel();
3492}
3493
3495{
3496 m_inputGrp0->Save();
3497 m_inputGrp1->Save();
3498
3499 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
3500 auto *settingdialog =
3501 new MythTextInputDialog(popupStack, tr("Enter new group name"));
3502
3503 if (settingdialog->Create())
3504 {
3505 connect(settingdialog, &MythTextInputDialog::haveResult,
3507 popupStack->AddScreen(settingdialog);
3508 }
3509 else
3510 {
3511 delete settingdialog;
3512 }
3513}
3514
3515void CardInput::CreateNewInputGroupSlot(const QString& name)
3516{
3517 if (name.isEmpty())
3518 {
3519 ShowOkPopup(tr("Sorry, this Input Group name cannot be blank."));
3520 return;
3521 }
3522
3523 QString new_name = QString("user:") + name;
3524
3526 query.prepare("SELECT inputgroupname "
3527 "FROM inputgroup "
3528 "WHERE inputgroupname = :GROUPNAME");
3529 query.bindValue(":GROUPNAME", new_name);
3530
3531 if (!query.exec())
3532 {
3533 MythDB::DBError("CreateNewInputGroup 1", query);
3534 return;
3535 }
3536
3537 if (query.next())
3538 {
3539 ShowOkPopup(tr("Sorry, this Input Group name is already in use."));
3540 return;
3541 }
3542
3543 uint inputgroupid = CardUtil::CreateInputGroup(new_name);
3544
3545 m_inputGrp0->Load();
3546 m_inputGrp1->Load();
3547
3548 if (m_inputGrp0->getValue().toUInt() == 0U)
3549 {
3551 m_inputGrp0->getValueIndex(QString::number(inputgroupid)));
3552 }
3553 else
3554 {
3556 m_inputGrp1->getValueIndex(QString::number(inputgroupid)));
3557 }
3558}
3559
3561{
3562 uint srcid = m_sourceId->getValue().toUInt();
3563 uint crdid = m_id->getValue().toUInt();
3564 QString in = m_inputName->getValue();
3565
3566#if CONFIG_BACKEND
3567 uint num_channels_before = SourceUtil::GetChannelCount(srcid);
3568
3569 Save(); // save info for scanner.
3570
3571 QString cardtype = CardUtil::GetRawInputType(crdid);
3572 if (CardUtil::IsUnscanable(cardtype))
3573 {
3574 LOG(VB_GENERAL, LOG_ERR,
3575 QString("Sorry, %1 cards do not yet support scanning.")
3576 .arg(cardtype));
3577 return;
3578 }
3579
3581 auto *ssd = new StandardSettingDialog(mainStack, "generalsettings",
3582 new ScanWizard(srcid, crdid, in));
3583
3584 if (ssd->Create())
3585 {
3586 connect(ssd, &StandardSettingDialog::Exiting, this,
3587 [srcid, this, num_channels_before]()
3588 {
3589 if (SourceUtil::GetChannelCount(srcid))
3590 m_startChan->SetSourceID(QString::number(srcid));
3591 if (num_channels_before)
3592 {
3593 m_startChan->Load();
3594 m_startChan->Save();
3595 }
3596 });
3597 mainStack->AddScreen(ssd);
3598 }
3599 else
3600 {
3601 delete ssd;
3602 }
3603
3604#else
3605 LOG(VB_GENERAL, LOG_ERR, "You must compile the backend "
3606 "to be able to scan for channels");
3607#endif
3608}
3609
3611{
3612 uint srcid = m_sourceId->getValue().toUInt();
3613 uint crdid = m_id->getValue().toUInt();
3614
3615 uint num_channels_before = SourceUtil::GetChannelCount(srcid);
3616
3617 if (crdid && srcid)
3618 {
3619 Save(); // save info for fetch..
3620
3621 QString cardtype = CardUtil::GetRawInputType(crdid);
3622
3623 if (!CardUtil::IsCableCardPresent(crdid, cardtype) &&
3624 !CardUtil::IsUnscanable(cardtype) &&
3625 !CardUtil::IsEncoder(cardtype) &&
3626 cardtype != "HDHOMERUN" &&
3627 !num_channels_before)
3628 {
3629 LOG(VB_GENERAL, LOG_ERR, "Skipping channel fetch, you need to "
3630 "scan for channels first.");
3631 return;
3632 }
3633
3635 }
3636
3637 if (SourceUtil::GetChannelCount(srcid))
3638 m_startChan->SetSourceID(QString::number(srcid));
3639 if (num_channels_before)
3640 {
3641 m_startChan->Load();
3642 m_startChan->Save();
3643 }
3644}
3645
3647{
3648 QString cardinputidTag(":WHERECARDID");
3649
3650 QString query("cardid = " + cardinputidTag);
3651
3652 bindings.insert(cardinputidTag, m_parent.getInputID());
3653
3654 return query;
3655}
3656
3658{
3659 QString cardinputidTag(":SETCARDID");
3660 QString colTag(":SET" + GetColumnName().toUpper());
3661
3662 QString query("cardid = " + cardinputidTag + ", " +
3663 GetColumnName() + " = " + colTag);
3664
3665 bindings.insert(cardinputidTag, m_parent.getInputID());
3666 bindings.insert(colTag, m_user->GetDBValue());
3667
3668 return query;
3669}
3670
3671void CardInput::loadByID(int inputid)
3672{
3673 m_id->setValue(inputid);
3674 m_externalInputSettings->Load(inputid);
3676}
3677
3678void CardInput::loadByInput(int _cardid, const QString& _inputname)
3679{
3681 query.prepare("SELECT cardid FROM capturecard "
3682 "WHERE cardid = :CARDID AND inputname = :INPUTNAME");
3683 query.bindValue(":CARDID", _cardid);
3684 query.bindValue(":INPUTNAME", _inputname);
3685
3686 if (query.exec() && query.isActive() && query.next())
3687 {
3688 loadByID(query.value(0).toInt());
3689 }
3690}
3691
3693{
3694 uint cardid = m_id->getValue().toUInt();
3697
3698 uint icount = 1;
3699 if (m_instanceCount)
3700 icount = m_instanceCount->getValue().toUInt();
3701
3702 CardUtil::InputSetMaxRecordings(cardid, icount);
3703}
3704
3706{
3707 return m_parent.getInputID();
3708}
3709
3711{
3712 return m_parent.getCardID();
3713}
3714
3716{
3717 emit Clicked(m_value);
3718}
3719
3720void CaptureCardEditor::AddSelection(const QString &label, const CCESlot slot)
3721{
3722 auto *button = new ButtonStandardSetting(label);
3723 connect(button, &ButtonStandardSetting::clicked, this, slot);
3724 addChild(button);
3725}
3726
3727void CaptureCardEditor::AddSelection(const QString &label, const CCESlotConst slot)
3728{
3729 auto *button = new ButtonStandardSetting(label);
3730 connect(button, &ButtonStandardSetting::clicked, this, slot);
3731 addChild(button);
3732}
3733
3735{
3737 tr("Are you sure you want to delete "
3738 "ALL capture cards on %1?").arg(gCoreContext->GetHostName()),
3740 true);
3741}
3742
3744{
3746 tr("Are you sure you want to delete "
3747 "ALL capture cards?"),
3749 true);
3750}
3751
3753{
3754 auto *card = new CaptureCard();
3755 card->setLabel(tr("New capture card"));
3756 card->Load();
3757 addChild(card);
3758 emit settingsChanged(this);
3759}
3760
3762{
3763 if (!doDelete)
3764 return;
3765
3767 Load();
3768 emit settingsChanged(this);
3769}
3770
3772{
3773 if (!doDelete)
3774 return;
3775
3777
3778 cards.prepare(
3779 "SELECT cardid "
3780 "FROM capturecard "
3781 "WHERE hostname = :HOSTNAME");
3782 cards.bindValue(":HOSTNAME", gCoreContext->GetHostName());
3783
3784 if (!cards.exec() || !cards.isActive())
3785 {
3787 tr("Error getting list of cards for this host. "
3788 "Unable to delete capturecards for %1")
3789 .arg(gCoreContext->GetHostName()));
3790
3791 MythDB::DBError("Selecting cardids for deletion", cards);
3792 return;
3793 }
3794
3795 while (cards.next())
3796 CardUtil::DeleteInput(cards.value(0).toUInt());
3797
3798 Load();
3799 emit settingsChanged(this);
3800}
3801
3803{
3804 setLabel(tr("Capture cards"));
3805}
3806
3808{
3809 clearSettings();
3810 AddSelection(QObject::tr("(New capture card)"), &CaptureCardEditor::AddNewCard);
3811 AddSelection(QObject::tr("(Delete all capture cards on %1)")
3812 .arg(gCoreContext->GetHostName()),
3814 AddSelection(QObject::tr("(Delete all capture cards)"),
3817}
3818
3820{
3821 setLabel(tr("Video sources"));
3822}
3823
3825{
3826 clearSettings();
3827 AddSelection(QObject::tr("(New video source)"), &VideoSourceEditor::NewSource);
3828 AddSelection(QObject::tr("(Delete all video sources)"),
3832}
3833
3834void VideoSourceEditor::AddSelection(const QString &label, const VSESlot slot)
3835{
3836 auto *button = new ButtonStandardSetting(label);
3837 connect(button, &ButtonStandardSetting::clicked, this, slot);
3838 addChild(button);
3839}
3840
3841void VideoSourceEditor::AddSelection(const QString &label, const VSESlotConst slot)
3842{
3843 auto *button = new ButtonStandardSetting(label);
3844 connect(button, &ButtonStandardSetting::clicked, this, slot);
3845 addChild(button);
3846}
3847
3849{
3851 tr("Are you sure you want to delete "
3852 "ALL video sources?"),
3854 true);
3855}
3856
3858{
3859 if (!doDelete)
3860 return;
3861
3863 Load();
3864 emit settingsChanged(this);
3865}
3866
3868{
3869 auto *source = new VideoSource();
3870 source->setLabel(tr("New video source"));
3871 source->Load();
3872 addChild(source);
3873 emit settingsChanged(this);
3874}
3875
3877{
3878 setLabel(tr("Input connections"));
3879}
3880
3882{
3883 m_cardInputs.clear();
3884 clearSettings();
3885
3886 // We do this manually because we want custom labels. If
3887 // SelectSetting provided a facility to edit the labels, we
3888 // could use CaptureCard::fillSelections
3889
3891 query.prepare(
3892 "SELECT cardid, videodevice, cardtype, displayname "
3893 "FROM capturecard "
3894 "WHERE hostname = :HOSTNAME "
3895 " AND parentid = 0 "
3896 "ORDER BY cardid");
3897 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
3898
3899 if (!query.exec())
3900 {
3901 MythDB::DBError("CardInputEditor::load", query);
3902 return;
3903 }
3904
3905 while (query.next())
3906 {
3907 uint cardid = query.value(0).toUInt();
3908 QString videodevice = query.value(1).toString();
3909 QString cardtype = query.value(2).toString();
3910 QString displayname = query.value(3).toString();
3911
3912 auto *cardinput = new CardInput(cardtype, videodevice, cardid);
3913 cardinput->loadByID(cardid);
3914 QString inputlabel = QString("%1 (%2) -> %3")
3915 .arg(CardUtil::GetDeviceLabel(cardtype, videodevice),
3916 displayname, cardinput->getSourceName());
3917 m_cardInputs.push_back(cardinput);
3918 cardinput->setLabel(inputlabel);
3919 addChild(cardinput);
3920 }
3921
3923}
3924
3925#if CONFIG_DVB
3926static QString remove_chaff(const QString &name)
3927{
3928 // Trim off some of the chaff.
3929 QString short_name = name;
3930 if (short_name.startsWith("LG Electronics"))
3931 short_name = short_name.right(short_name.length() - 15);
3932 if (short_name.startsWith("Oren"))
3933 short_name = short_name.right(short_name.length() - 5);
3934 if (short_name.startsWith("Nextwave"))
3935 short_name = short_name.right(short_name.length() - 9);
3936 if (short_name.startsWith("frontend", Qt::CaseInsensitive))
3937 short_name = short_name.left(short_name.length() - 9);
3938 if (short_name.endsWith("VSB/QAM"))
3939 short_name = short_name.left(short_name.length() - 8);
3940 if (short_name.endsWith("VSB"))
3941 short_name = short_name.left(short_name.length() - 4);
3942 if (short_name.endsWith("DVB-T"))
3943 short_name = short_name.left(short_name.length() - 6);
3944
3945 // It would be infinitely better if DVB allowed us to query
3946 // the vendor ID. But instead we have to guess based on the
3947 // demodulator name. This means cards like the Air2PC HD5000
3948 // and DViCO Fusion HDTV cards are not identified correctly.
3949 short_name = short_name.simplified();
3950 if (short_name.startsWith("or51211", Qt::CaseInsensitive))
3951 short_name = "pcHDTV HD-2000";
3952 else if (short_name.startsWith("or51132", Qt::CaseInsensitive))
3953 short_name = "pcHDTV HD-3000";
3954 else if (short_name.startsWith("bcm3510", Qt::CaseInsensitive))
3955 short_name = "Air2PC v1";
3956 else if (short_name.startsWith("nxt2002", Qt::CaseInsensitive) ||
3957 short_name.startsWith("nxt200x", Qt::CaseInsensitive))
3958 short_name = "Air2PC v2";
3959 else if (short_name.startsWith("lgdt3302", Qt::CaseInsensitive))
3960 short_name = "DViCO HDTV3";
3961 else if (short_name.startsWith("lgdt3303", Qt::CaseInsensitive))
3962 short_name = "DViCO v2 or Air2PC v3 or pcHDTV HD-5500";
3963
3964 return short_name;
3965}
3966#endif // CONFIG_DVB
3967
3968void DVBConfigurationGroup::reloadDiseqcTree(const QString &videodevice)
3969{
3970 if (m_diseqcTree)
3971 m_diseqcTree->Load(videodevice);
3972
3973 if (m_cardType->getValue() == "DVB-S" ||
3974 m_cardType->getValue() == "DVB-S2" )
3975 {
3976 m_diseqcBtn->setVisible(true);
3977 }
3978 else
3979 {
3980 m_diseqcBtn->setVisible(false);
3981 }
3982 emit getParent()->settingsChanged(this);
3983}
3984
3985void DVBConfigurationGroup::probeCard(const QString &videodevice)
3986{
3987 if (videodevice.isEmpty())
3988 {
3989 m_cardName->setValue("");
3990 m_cardType->setValue("");
3991 return;
3992 }
3993
3994 if ((m_parent.getCardID() != 0) && m_parent.GetRawCardType() != "DVB")
3995 {
3996 m_cardName->setValue("");
3997 m_cardType->setValue("");
3998 return;
3999 }
4000
4001#if CONFIG_DVB
4002 QString frontend_name = CardUtil::ProbeDVBFrontendName(videodevice);
4003 QString subtype = CardUtil::ProbeDVBType(videodevice);
4004
4005 QString err_open = tr("Could not open card %1").arg(videodevice);
4006 QString err_other = tr("Could not get card info for card %1").arg(videodevice);
4007
4008 switch (CardUtil::toInputType(subtype))
4009 {
4011 m_cardName->setValue(err_open);
4012 m_cardType->setValue(strerror(errno));
4013 break;
4015 m_cardName->setValue(err_other);
4016 m_cardType->setValue("Unknown error");
4017 break;
4019 m_cardName->setValue(err_other);
4020 m_cardType->setValue(strerror(errno));
4021 break;
4023 m_cardType->setValue("DVB-S");
4024 m_cardName->setValue(frontend_name);
4027 break;
4029 m_cardType->setValue("DVB-S2");
4030 m_cardName->setValue(frontend_name);
4033 break;
4035 m_cardType->setValue("DVB-C");
4036 m_cardName->setValue(frontend_name);
4039 break;
4041 m_cardType->setValue("DVB-T2");
4042 m_cardName->setValue(frontend_name);
4045 break;
4047 {
4048 m_cardType->setValue("DVB-T");
4049 m_cardName->setValue(frontend_name);
4052 if (frontend_name.toLower().indexOf("usb") >= 0)
4053 {
4056 }
4057
4058 // slow down tuning for buggy drivers
4059 if ((frontend_name == "DiBcom 3000P/M-C DVB-T") ||
4060 (frontend_name ==
4061 "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"))
4062 {
4063 m_tuningDelay->setValueMs(200ms);
4064 }
4065
4066#if 0 // frontends on hybrid DVB-T/Analog cards
4067 QString short_name = remove_chaff(frontend_name);
4068 m_buttonAnalog->setVisible(
4069 short_name.startsWith("zarlink zl10353",
4070 Qt::CaseInsensitive) ||
4071 short_name.startsWith("wintv hvr 900 m/r: 65008/a1c0",
4072 Qt::CaseInsensitive) ||
4073 short_name.startsWith("philips tda10046h",
4074 Qt::CaseInsensitive));
4075#endif
4076 }
4077 break;
4079 {
4080 QString short_name = remove_chaff(frontend_name);
4081 m_cardType->setValue("ATSC");
4082 m_cardName->setValue(short_name);
4085
4086 // According to #1779 and #1935 the AverMedia 180 needs
4087 // a 3000 ms signal timeout, at least for QAM tuning.
4088 if (frontend_name == "Nextwave NXT200X VSB/QAM frontend")
4089 {
4092 }
4093
4094#if 0 // frontends on hybrid DVB-T/Analog cards
4095 if (frontend_name.toLower().indexOf("usb") < 0)
4096 {
4097 m_buttonAnalog->setVisible(
4098 short_name.startsWith("pchdtv", Qt::CaseInsensitive) ||
4099 short_name.startsWith("dvico", Qt::CaseInsensitive) ||
4100 short_name.startsWith("nextwave", Qt::CaseInsensitive));
4101 }
4102#endif
4103 }
4104 break;
4105 default:
4106 break;
4107 }
4108
4109 // Create selection list of all delivery systems of this card
4110 {
4112 QStringList delsyslist = CardUtil::ProbeDeliverySystems(videodevice);
4113 for (const auto & item : std::as_const(delsyslist))
4114 {
4115 LOG(VB_GENERAL, LOG_DEBUG, QString("DVBCardType: add deliverysystem:%1")
4116 .arg(item));
4117
4118 m_cardType->addSelection(item, item);
4119 }
4120
4121 // Default value, used if not already defined in capturecard/inputname
4122 QString delsys = CardUtil::ProbeDefaultDeliverySystem(videodevice);
4123 if (!delsys.isEmpty())
4124 {
4125 m_cardType->setValue(delsys);
4126 }
4127 }
4128#
4129#else
4130 m_cardType->setValue(QString("Recompile with DVB-Support!"));
4131#endif
4132}
4133
4135 QString dev, QString type) :
4136 CaptureCardComboBoxSetting(parent, false, "audiodevice"),
4137 m_lastDevice(std::move(dev)), m_lastCardType(std::move(type))
4138{
4139 setLabel(QObject::tr("Audio input"));
4140 setHelpText(QObject::tr("If there is more than one audio input, "
4141 "select which one to use."));
4142 int cardid = parent.getCardID();
4143 if (cardid <= 0)
4144 return;
4145
4148}
4149
4150int TunerCardAudioInput::fillSelections(const QString &device)
4151{
4153
4154 if (device.isEmpty())
4155 return 0;
4156
4157 m_lastDevice = device;
4158 QStringList inputs =
4160
4161 for (uint i = 0; i < (uint)inputs.size(); i++)
4162 {
4163 addSelection(inputs[i], QString::number(i),
4164 m_lastDevice == QString::number(i));
4165 }
4166 return inputs.size();
4167}
4168
4170 CardType& cardType) :
4171 m_parent(a_parent),
4172 m_cardNum(new DVBCardNum(a_parent)),
4173 m_cardName(new DVBCardName()),
4174 m_cardType(new DVBCardType(a_parent)),
4175 m_signalTimeout(new SignalTimeout(a_parent, 0.5s, 0.25s)),
4176 m_tuningDelay(new DVBTuningDelay(a_parent)),
4177 m_diseqcTree(new DiSEqCDevTree()),
4178 m_diseqcBtn(new DeviceTree(*m_diseqcTree))
4179{
4180 setVisible(false);
4181
4182 m_channelTimeout = new ChannelTimeout(m_parent, 3s, 1.75s);
4183
4184 cardType.addTargetedChild("DVB", m_cardNum);
4185
4186 cardType.addTargetedChild("DVB", m_cardName);
4187 cardType.addTargetedChild("DVB", m_cardType);
4188
4189 cardType.addTargetedChild("DVB", m_signalTimeout);
4190 cardType.addTargetedChild("DVB", m_channelTimeout);
4191
4192 cardType.addTargetedChild("DVB", new EmptyAudioDevice(m_parent));
4193 cardType.addTargetedChild("DVB", new EmptyVBIDevice(m_parent));
4194
4195 cardType.addTargetedChild("DVB", new DVBNoSeqStart(m_parent));
4196 cardType.addTargetedChild("DVB", new DVBOnDemand(m_parent));
4197 cardType.addTargetedChild("DVB", new DVBEITScan(m_parent));
4198
4199 m_diseqcBtn->setLabel(tr("DiSEqC (Switch, LNB and Rotor Configuration)"));
4200 m_diseqcBtn->setHelpText(tr("Input and satellite settings."));
4201
4202 cardType.addTargetedChild("DVB", m_tuningDelay);
4203 cardType.addTargetedChild("DVB", m_diseqcBtn);
4204 m_tuningDelay->setVisible(false);
4205
4206 connect(m_cardNum, qOverload<const QString&>(&StandardSetting::valueChanged),
4208 connect(m_cardNum, qOverload<const QString&>(&StandardSetting::valueChanged),
4210}
4211
4213{
4214 if (m_diseqcTree)
4215 {
4216 delete m_diseqcTree;
4217 m_diseqcTree = nullptr;
4218 }
4219}
4220
4222{
4224 m_diseqcBtn->Load();
4226 if (m_cardType->getValue() == "DVB-S" ||
4227 m_cardType->getValue() == "DVB-S2" ||
4229 {
4230 m_diseqcBtn->setVisible(true);
4231 }
4232}
4233
4235{
4239}
4240
4241// -----------------------
4242// SAT>IP configuration
4243// -----------------------
4244#if CONFIG_SATIP
4245
4246class DiSEqCPosition : public MythUISpinBoxSetting
4247{
4248 public:
4249 explicit DiSEqCPosition(const CaptureCard &parent, int value, int min_val) :
4250 MythUISpinBoxSetting(new CaptureCardDBStorage(this, parent, "dvb_diseqc_type"),
4251 min_val, 0xff, 1)
4252 {
4253 setLabel(QObject::tr("DiSEqC position"));
4254 setHelpText(QObject::tr("Position of the LNB on the DiSEqC switch. "
4255 "Leave at 1 if there is no DiSEqC switch "
4256 "and the LNB is directly connected to the SatIP server. "
4257 "This value is used as signal source (attribute src) in "
4258 "the SatIP tune command."));
4259 setValue(value);
4260 };
4261
4262 ~DiSEqCPosition() override
4263 {
4264 delete GetStorage();
4265 }
4266};
4267
4268SatIPConfigurationGroup::SatIPConfigurationGroup
4269 (CaptureCard& a_parent, CardType &a_cardtype) :
4270 m_parent(a_parent),
4271 m_deviceId(new SatIPDeviceID(a_parent))
4272{
4273 setVisible(false);
4274
4275 FillDeviceList();
4276
4277 m_friendlyName = new SatIPDeviceAttribute(tr("Friendly name"), tr("Friendly name of the Sat>IP server"));
4278 m_tunerType = new SatIPDeviceAttribute(tr("Tuner type"), tr("Type of the selected tuner"));
4279 m_tunerIndex = new SatIPDeviceAttribute(tr("Tuner index"), tr("Index of the tuner on the Sat>IP server"));
4280
4281 m_deviceIdList = new SatIPDeviceIDList(
4282 m_deviceId, m_friendlyName, m_tunerType, m_tunerIndex, &m_deviceList, m_parent);
4283
4284 a_cardtype.addTargetedChild("SATIP", m_deviceIdList);
4285 a_cardtype.addTargetedChild("SATIP", m_friendlyName);
4286 a_cardtype.addTargetedChild("SATIP", m_tunerType);
4287 a_cardtype.addTargetedChild("SATIP", m_tunerIndex);
4288 a_cardtype.addTargetedChild("SATIP", m_deviceId);
4289 a_cardtype.addTargetedChild("SATIP", new SignalTimeout(m_parent, 7s, 1s));
4290 a_cardtype.addTargetedChild("SATIP", new ChannelTimeout(m_parent, 10s, 2s));
4291 a_cardtype.addTargetedChild("SATIP", new DVBEITScan(m_parent));
4292 a_cardtype.addTargetedChild("SATIP", new DiSEqCPosition(m_parent, 1, 1));
4293
4294 connect(m_deviceIdList, &SatIPDeviceIDList::NewTuner,
4295 m_deviceId, &SatIPDeviceID::SetTuner);
4296};
4297
4298void SatIPConfigurationGroup::FillDeviceList(void)
4299{
4300 m_deviceList.clear();
4301
4302 // Find devices on the network
4303 // Returns each devices as "deviceid friendlyname ip tunerno tunertype"
4304 QStringList devs = CardUtil::ProbeVideoDevices("SATIP");
4305
4306 for (const auto & dev : std::as_const(devs))
4307 {
4308 QStringList devparts = dev.split(" ");
4309 const QString& id = devparts.value(0);
4310 const QString& name = devparts.value(1);
4311 const QString& ip = devparts.value(2);
4312 const QString& tunerno = devparts.value(3);
4313 const QString& tunertype = devparts.value(4);
4314
4315 SatIPDevice device;
4316 device.m_deviceId = id;
4317 device.m_cardIP = ip;
4318 device.m_inUse = false;
4319 device.m_friendlyName = name;
4320 device.m_tunerNo = tunerno;
4321 device.m_tunerType = tunertype;
4322 device.m_mythDeviceId = QString("%1:%2:%3").arg(id, tunertype, tunerno);
4323
4324 QString friendlyIdentifier = QString("%1, %2, Tuner #%3").arg(name, tunertype, tunerno);
4325
4326 m_deviceList[device.m_mythDeviceId] = device;
4327
4328 LOG(VB_CHANNEL, LOG_DEBUG, QString("SatIP: Add %1 '%2' '%3'")
4329 .arg(device.m_mythDeviceId, device.m_friendlyName, friendlyIdentifier));
4330 }
4331
4332 // Now find configured devices
4333 // Returns each devices as "deviceid friendlyname ip tunerno tunertype"
4334 QStringList db = CardUtil::GetVideoDevices("SATIP");
4335 for (const auto& dev : std::as_const(db))
4336 {
4337 auto dit = m_deviceList.find(dev);
4338 if (dit != m_deviceList.end())
4339 {
4340 (*dit).m_inUse = true;
4341 }
4342 }
4343};
4344
4345SatIPDeviceIDList::SatIPDeviceIDList(
4346 SatIPDeviceID *deviceId,
4347 SatIPDeviceAttribute *friendlyName,
4348 SatIPDeviceAttribute *tunerType,
4349 SatIPDeviceAttribute *tunerIndex,
4350 SatIPDeviceList *deviceList,
4351 const CaptureCard &parent) :
4352 m_deviceId(deviceId),
4353 m_friendlyName(friendlyName),
4354 m_tunerType(tunerType),
4355 m_tunerIndex(tunerIndex),
4356 m_deviceList(deviceList),
4357 m_parent(parent)
4358{
4359 setLabel(tr("Available devices"));
4360 setHelpText(tr("Device IP or ID, tuner number and tuner type of available Sat>IP device"));
4361
4362 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
4363 this, &SatIPDeviceIDList::UpdateDevices);
4364};
4365
4366void SatIPDeviceIDList::Load(void)
4367{
4368 clearSelections();
4369
4370 int cardid = m_parent.getCardID();
4371 QString device = CardUtil::GetVideoDevice(cardid);
4372
4373 fillSelections(device);
4374};
4375
4376void SatIPDeviceIDList::UpdateDevices(const QString &v)
4377{
4378 SatIPDevice dev = (*m_deviceList)[v];
4379 m_deviceId->setValue(dev.m_mythDeviceId);
4380 m_friendlyName->setValue(dev.m_friendlyName);
4381 m_tunerType->setValue(dev.m_tunerType);
4382 m_tunerIndex->setValue(dev.m_tunerNo);
4383};
4384
4385void SatIPDeviceIDList::fillSelections(const QString &cur)
4386{
4387 clearSelections();
4388
4389 std::vector<QString> names;
4390 std::vector<QString> devs;
4391 QMap<QString, bool> in_use;
4392
4393 const QString& current = cur;
4394 QString sel;
4395
4396 names.reserve(m_deviceList->size());
4397 devs.reserve(m_deviceList->size());
4398 SatIPDeviceList::iterator it = m_deviceList->begin();
4399 for(; it != m_deviceList->end(); ++it)
4400 {
4401 QString friendlyIdentifier = QString("%1, %2, Tuner #%3")
4402 .arg((*it).m_friendlyName, (*it).m_tunerType, (*it).m_tunerNo);
4403 names.push_back(friendlyIdentifier);
4404
4405 devs.push_back(it.key());
4406 in_use[it.key()] = (*it).m_inUse;
4407 }
4408
4409 for (const auto& it2s : devs)
4410 {
4411 sel = (current == it2s) ? it2s : sel;
4412 }
4413
4414 QString usestr = QString(" -- ");
4415 usestr += tr("Warning: already in use");
4416
4417 for (uint i = 0; i < devs.size(); ++i)
4418 {
4419 const QString& dev = devs[i];
4420 const QString& name = names[i];
4421 bool dev_in_use = (dev == sel) ? false : in_use[devs[i]];
4422 QString desc = name + (dev_in_use ? usestr : "");
4423 addSelection(desc, dev, dev == sel);
4424 }
4425};
4426
4427SatIPDeviceID::SatIPDeviceID(const CaptureCard &parent) :
4428 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, "videodevice")),
4429 m_parent(parent)
4430{
4431 setLabel(tr("Device ID"));
4432 setHelpText(tr("Device ID of the Sat>IP tuner"));
4433 setEnabled(true);
4434 setReadOnly(true);
4435};
4436
4437SatIPDeviceID::~SatIPDeviceID()
4438{
4439 delete GetStorage();
4440}
4441
4442void SatIPDeviceID::Load(void)
4443{
4445};
4446
4447void SatIPDeviceID::SetTuner(const QString &tuner)
4448{
4449 setValue(tuner);
4450};
4451
4452SatIPDeviceAttribute::SatIPDeviceAttribute(const QString& label, const QString& helptext)
4453{
4454 setLabel(label);
4455 setHelpText(helptext);
4456};
4457#endif // CONFIG_SATIP
4458
4459#include "moc_videosource.cpp"
@ DVB_DEV_FRONTEND
Definition: cardutil.h:32
std::vector< ChannelInfo > ChannelInfoList
Definition: channelinfo.h:130
TransTextEditSetting * m_cardInfo
Definition: videosource.h:632
void probeCard(const QString &device)
ASIDevice * m_device
Definition: videosource.h:631
ASIConfigurationGroup(CaptureCard &parent, CardType &cardType)
CaptureCard & m_parent
Definition: videosource.h:630
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:853
void Load(void) override
void(CaptureCardEditor::*)(void) CCESlot
Definition: videosource.h:852
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:763
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:819
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:897
void SetSourceID(const QString &sourceid)
SourceID * m_sourceId
Definition: videosource.h:957
void CreateNewInputGroup()
void sourceFetch()
void Save(void) override
InputName * m_inputName
Definition: videosource.h:956
CardInput(const QString &cardtype, const QString &device, int cardid)
int getInputID(void) const
Definition: videosource.h:931
MythUISpinBoxSetting * m_instanceCount
Definition: videosource.h:964
QString getSourceName(void) const
void loadByID(int id)
MythUICheckBoxSetting * m_schedGroup
Definition: videosource.h:965
void loadByInput(int cardid, const QString &inputname)
~CardInput() override
StartingChannel * m_startChan
Definition: videosource.h:958
DiSEqCDevSettings * m_externalInputSettings
Definition: videosource.h:961
ButtonStandardSetting * m_srcFetch
Definition: videosource.h:960
ButtonStandardSetting * m_scan
Definition: videosource.h:959
InputGroup * m_inputGrp0
Definition: videosource.h:962
void channelScanner()
void CreateNewInputGroupSlot(const QString &name)
InputGroup * m_inputGrp1
Definition: videosource.h:963
static void fillSelections(MythUIComboBoxSetting *setting)
CardType(const CaptureCard &parent)
static int GetASIDeviceNumber(const QString &device, QString *error=nullptr)
Definition: cardutil.cpp:3282
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:2678
static bool IsUnscanable(const QString &rawtype)
Definition: cardutil.h:160
static uint CreateInputGroup(const QString &name)
Definition: cardutil.cpp:2040
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:753
static QString ProbeDVBType(const QString &device)
Definition: cardutil.cpp:732
static bool InputSetMaxRecordings(uint parentid, uint max_recordings)
Definition: cardutil.cpp:1572
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:2082
static QString GetStartChannel(uint inputid)
Definition: cardutil.cpp:1803
static QString GetDeviceLabel(const QString &inputtype, const QString &videodevice)
Definition: cardutil.cpp:2656
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:3211
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:2170
static bool LinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:2119
static QString GetVideoDevice(uint inputid)
Definition: cardutil.h:296
static bool IsInNeedOfExternalInputConf(uint inputid)
Definition: cardutil.cpp:2327
static void ClearVideoDeviceCache()
Definition: cardutil.cpp:447
static uint CloneCard(uint src_inputid, uint dst_inputid)
Definition: cardutil.cpp:1561
static bool IsV4L(const QString &rawtype)
Definition: cardutil.h:147
static std::vector< uint > GetChildInputIDs(uint inputid)
Definition: cardutil.cpp:1382
static QString GetDeviceName(dvb_dev_type_t type, const QString &device)
Definition: cardutil.cpp:2991
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:137
static bool DeleteInput(uint inputid)
Definition: cardutil.cpp:2841
static QString ProbeDefaultDeliverySystem(const QString &device)
Definition: cardutil.cpp:716
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:2371
static bool DeleteAllInputs(void)
Definition: cardutil.cpp:2911
static QStringList ProbeAudioInputs(const QString &device, const QString &inputtype=QString())
Definition: cardutil.cpp:2560
static uint GetDeviceInputGroup(uint inputid)
Definition: cardutil.cpp:2095
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:716
DVBCardType * m_cardType
Definition: videosource.h:712
DVBCardName * m_cardName
Definition: videosource.h:711
CaptureCard & m_parent
Definition: videosource.h:708
DVBConfigurationGroup(CaptureCard &a_parent, CardType &cardType)
~DVBConfigurationGroup() override
SignalTimeout * m_signalTimeout
Definition: videosource.h:713
ChannelTimeout * m_channelTimeout
Definition: videosource.h:714
DeviceTree * m_diseqcBtn
Definition: videosource.h:717
void Load(void) override
void Save(void) override
void probeCard(const QString &videodevice)
DVBCardNum * m_cardNum
Definition: videosource.h:710
DVBTuningDelay * m_tuningDelay
Definition: videosource.h:715
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:663
GroupSetting * m_size
Definition: videosource.h:664
DemoConfigurationGroup(CaptureCard &parent, CardType &cardtype)
void probeCard(const QString &device)
CaptureCard & m_parent
Definition: videosource.h:662
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:747
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:732
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:647
CaptureCard & m_parent
Definition: videosource.h:646
ImportConfigurationGroup(CaptureCard &parent, CardType &cardtype)
void probeCard(const QString &device)
GroupSetting * m_size
Definition: videosource.h:648
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:129
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:839
QVariant value(int i) const
Definition: mythdbcon.h:205
int size(void) const
Definition: mythdbcon.h:215
bool isActive(void) const
Definition: mythdbcon.h:216
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:620
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:890
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:814
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:552
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:181
static bool DeleteSource(uint sourceid)
Definition: sourceutil.cpp:543
static bool DeleteAllSources(void)
Definition: sourceutil.cpp:599
static uint GetChannelCount(uint sourceid)
Definition: sourceutil.cpp:145
static bool UpdateChannelsFromListings(uint sourceid, const QString &inputtype=QString(), bool wait=false)
Definition: sourceutil.cpp:409
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:611
CaptureCard & m_parent
Definition: videosource.h:609
GroupSetting * m_cardInfo
Definition: videosource.h:610
void probeCard(const QString &device)
QString m_driverName
Definition: videosource.h:613
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:1034
const CaptureCard & m_parent
Definition: videosource.h:1039
VBoxDeviceList * m_deviceList
Definition: videosource.h:1038
StandardSetting * m_desc
Definition: videosource.h:1035
VBoxTunerIndex * m_cardTuner
Definition: videosource.h:1037
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:1061
QString m_ip
Definition: videosource.h:1060
QString m_overrideDeviceId
Definition: videosource.h:1062
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:980
void setEnabled(bool e) override
void UpdateDevices(const QString &v)
QString m_oldValue
Definition: videosource.h:990
QString m_oldValue
Definition: videosource.h:1011
void UpdateDevices(const QString &v)
void setEnabled(bool e) override
void NewTuner(const QString &)
void SetOldValue(const QString &s)
Definition: videosource.h:1001
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:876
void ShowDeleteAllSourcesDialog(void) const
void(VideoSourceEditor::*)(void) const VSESlotConst
Definition: videosource.h:877
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:62
#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:101
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
QString GetConfDir(void)
Definition: mythdirs.cpp:282
#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