MythTV master
videosource.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
2
3// Standard UNIX C headers
4#include <unistd.h>
5#include <fcntl.h>
6#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(_WIN32)
7#include <sys/types.h>
8#else
9#include <sys/sysmacros.h>
10#endif
11#include <sys/stat.h>
12
13// C++ headers
14#include <algorithm>
15
16// Qt headers
17#include <QCoreApplication>
18#include <QCursor>
19#include <QDateTime>
20#include <QDir>
21#include <QFile>
22#include <QLayout>
23#include <QMap>
24#include <QStringList>
25#include <QTextStream>
26#include <utility>
27
28// MythTV headers
29#include "libmythbase/compat.h"
31#include "libmythbase/mythconfig.h"
33#include "libmythbase/mythdb.h"
39#include "libmythupnp/httprequest.h" // for TestMimeType()
40
41#include "cardutil.h"
42#include "channelinfo.h"
43#include "channelutil.h"
44#include "diseqcsettings.h"
45#include "frequencies.h"
47#include "scanwizard.h"
48#include "sourceutil.h"
49#include "v4l2util.h"
50#include "videosource.h"
51
52#if CONFIG_DVB
53#include "recorders/dvbtypes.h"
54#endif
55
56#if CONFIG_VBOX
57#include "recorders/vboxutils.h"
58#endif
59
60#if CONFIG_HDHOMERUN
61#include HDHOMERUN_HEADERFILE
62#endif
63
65 QString _card_types,
66 bool _must_have_mplexid) :
67 m_initialSourceId(_initial_sourceid),
68 m_cardTypes(std::move(_card_types)),
69 m_mustHaveMplexId(_must_have_mplexid)
70{
71 setLabel(tr("Video Source"));
73 QObject::tr(
74 "Select a video source that is connected to one "
75 "or more capture cards. Default is the video source "
76 "selected in the Channel Editor page."
77 ));
78}
79
81{
83
84 QString querystr =
85 "SELECT DISTINCT videosource.name, videosource.sourceid "
86 "FROM capturecard, videosource";
87
88 querystr += (m_mustHaveMplexId) ? ", channel " : " ";
89
90 querystr +=
91 "WHERE capturecard.sourceid = videosource.sourceid AND "
92 " capturecard.hostname = :HOSTNAME ";
93
94 if (!m_cardTypes.isEmpty())
95 {
96 querystr += QString(" AND capturecard.cardtype in %1 ")
97 .arg(m_cardTypes);
98 }
99
101 {
102 querystr +=
103 " AND channel.sourceid = videosource.sourceid "
104 " AND channel.mplexid != 32767 "
105 " AND channel.mplexid != 0 ";
106 }
107
108 query.prepare(querystr);
109 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
110
111 if (!query.exec() || !query.isActive() || query.size() <= 0)
112 return;
113
114 uint sel = 0;
115 uint cnt = 0;
116 for (; query.next(); cnt++)
117 {
118 addSelection(query.value(0).toString(),
119 query.value(1).toString());
120
121 sel = (query.value(1).toUInt() == m_initialSourceId) ? cnt : sel;
122 }
123
125 {
126 if (cnt)
127 setValue(sel);
128 }
129
131}
132
134 m_initialSourceId(_initial_sourceid)
135{
136 setLabel(tr("Video Source"));
138 QObject::tr(
139 "The video source that is "
140 "selected in the Channel Editor page."
141 ));
142}
143
145{
147
148 QString querystr =
149 "SELECT DISTINCT videosource.name, videosource.sourceid "
150 "FROM capturecard, videosource "
151 "WHERE capturecard.sourceid = videosource.sourceid AND "
152 " capturecard.hostname = :HOSTNAME AND "
153 " videosource.sourceid = :SOURCEID ";
154
155 query.prepare(querystr);
156 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
157 query.bindValue(":SOURCEID", m_initialSourceId);
158
159 if (!query.exec() || !query.isActive())
160 {
161 MythDB::DBError("VideoSourceShow::Load", query);
162 return;
163 }
164
165 if (query.next())
166 {
167 setValue(query.value(0).toString());
168 }
169}
170
172{
173 public:
174 explicit InstanceCount(const CardInput &parent) :
175 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "reclimit"),
176 1, 10, 1)
177 {
178 setLabel(QObject::tr("Max recordings"));
179 setValue(1);
181 QObject::tr(
182 "Maximum number of simultaneous recordings MythTV will "
183 "attempt using this device. If set to a value other than "
184 "1, MythTV can sometimes record multiple programs on "
185 "the same multiplex or overlapping copies of the same "
186 "program on a single channel."
187 ));
188 };
189
190 ~InstanceCount() override
191 {
192 delete GetStorage();
193 }
194};
195
197{
198 public:
199 explicit SchedGroup(const CardInput &parent) :
200 MythUICheckBoxSetting(new CardInputDBStorage(this, parent, "schedgroup"))
201 {
202 setLabel(QObject::tr("Schedule as group"));
203 setValue(true);
205 QObject::tr(
206 "Schedule all virtual inputs on this device as a group. "
207 "This is more efficient than scheduling each input "
208 "individually. Additional, virtual inputs will be "
209 "automatically added as needed to fulfill the recording "
210 "load."
211 ));
212 };
213
214 ~SchedGroup() override
215 {
216 delete GetStorage();
217 }
218};
219
221{
222 QString sourceidTag(":WHERESOURCEID");
223
224 QString query("sourceid = " + sourceidTag);
225
226 bindings.insert(sourceidTag, m_parent.getSourceID());
227
228 return query;
229}
230
232{
233 QString sourceidTag(":SETSOURCEID");
234 QString colTag(":SET" + GetColumnName().toUpper());
235
236 QString query("sourceid = " + sourceidTag + ", " +
237 GetColumnName() + " = " + colTag);
238
239 bindings.insert(sourceidTag, m_parent.getSourceID());
240 bindings.insert(colTag, m_user->GetDBValue());
241
242 return query;
243}
244
246{
247 QString cardidTag(":WHERECARDID");
248
249 QString query("cardid = " + cardidTag);
250
251 bindings.insert(cardidTag, m_parent.getCardID());
252
253 return query;
254}
255
257{
258 QString cardidTag(":SETCARDID");
259 QString colTag(":SET" + GetColumnName().toUpper());
260
261 QString query("cardid = " + cardidTag + ", " +
262 GetColumnName() + " = " + colTag);
263
264 bindings.insert(cardidTag, m_parent.getCardID());
265 bindings.insert(colTag, m_user->GetDBValue());
266
267 return query;
268}
269
271{
272 public:
273 explicit XMLTVGrabber(const VideoSource &parent) :
275 "xmltvgrabber")),
276 m_parent(parent)
277 {
278 setLabel(QObject::tr("Listings grabber"));
279 };
280
281 ~XMLTVGrabber() override
282 {
283 delete GetStorage();
284 }
285
286 void Load(void) override // StandardSetting
287 {
288 addTargetedChild("eitonly", new EITOnly_config(m_parent, this));
289 addTargetedChild("/bin/true", new NoGrabber_config(m_parent));
290
292 QObject::tr("Transmitted guide only (EIT)"), "eitonly");
293
294 addSelection(QObject::tr("No grabber"), "/bin/true");
295
296 QString gname;
297 QString d1;
298 QString d2;
299 QString d3;
301
302#ifdef _MSC_VER
303#pragma message( "tv_find_grabbers is not supported yet on windows." )
304 //-=>TODO:Screen doesn't show up if the call to MythSysemLegacy is executed
305#else
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#endif
352 }
353
354 void Save(void) override // StandardSetting
355 {
357
359 query.prepare(
360 "UPDATE videosource "
361 "SET userid=NULL, password=NULL "
362 "WHERE xmltvgrabber NOT IN ( 'technovera' )");
363 if (!query.exec())
364 MythDB::DBError("XMLTVGrabber::Save", query);
365 }
366
367 void LoadXMLTVGrabbers(QStringList name_list, QStringList prog_list)
368 {
369 if (name_list.size() != prog_list.size())
370 return;
371
372 QString selValue = getValue();
373 int selIndex = getValueIndex(selValue);
374 setValue(0);
375
376 for (uint i = 0; i < (uint) name_list.size(); i++)
377 {
378 addTargetedChild(prog_list[i],
379 new XMLTV_generic_config(m_parent, prog_list[i],
380 this));
381 addSelection(name_list[i], prog_list[i]);
382 }
383
384 if (!selValue.isEmpty())
385 selIndex = getValueIndex(selValue);
386 if (selIndex >= 0)
387 setValue(selIndex);
388 }
389private:
391
392private:
393 static QMutex m_lock;
394 static QStringList m_nameList;
395 static QStringList m_progList;
396};
397
398// Results of search for XMLTV grabbers
400QStringList XMLTVGrabber::m_nameList;
401QStringList XMLTVGrabber::m_progList;
402
404{
405 public:
407 std::chrono::milliseconds min_val,
408 std::chrono::milliseconds max_val,
409 std::chrono::milliseconds step,
410 const QString &setting) :
411 MythUISpinBoxSetting(new CaptureCardDBStorage(this, parent, setting),
412 min_val.count(), max_val.count(), step.count())
413 {
414 }
415
417 {
418 delete GetStorage();
419 }
420 // Handles integer milliseconds (compiler converts seconds to milliseconds)
421 void setValueMs (std::chrono::milliseconds newValue)
422 { setValue(newValue.count()); }
423 // Handle non-integer seconds
424 template<typename T, typename = std::enable_if_t<!std::is_integral<T>()>>
425 void setValueMs (std::chrono::duration<T> newSecs)
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)
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)
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, typename = std::enable_if_t<std::is_floating_point_v<T>> >
1110 SignalTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::duration<T> min_secs) :
1111 SignalTimeout(parent, value, duration_cast<std::chrono::milliseconds>(min_secs)) {};
1112 template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T>> >
1113 SignalTimeout(const CaptureCard &parent, std::chrono::duration<T> value, std::chrono::duration<T> min_secs) :
1114 SignalTimeout(parent,
1115 duration_cast<std::chrono::milliseconds>(value),
1116 duration_cast<std::chrono::milliseconds>(min_secs)) {};
1117};
1118
1120{
1121 public:
1122 // Handles integer milliseconds (compiler converts seconds to milliseconds)
1123 ChannelTimeout(const CaptureCard &parent, std::chrono::milliseconds value,
1124 std::chrono::milliseconds min_val) :
1125 CaptureCardSpinBoxSetting(parent, min_val, 65s, 250ms, "channel_timeout")
1126 {
1127 setLabel(QObject::tr("Tuning timeout (ms)"));
1128 setValueMs(value);
1129 setHelpText(QObject::tr(
1130 "Maximum time (in milliseconds) MythTV waits for "
1131 "a channel lock. For recordings, if this time is "
1132 "exceeded, the recording will be marked as failed."));
1133 };
1134 // Handle non-integer seconds
1135 template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T>> >
1136 ChannelTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::duration<T> min_secs) :
1137 ChannelTimeout(parent, value, duration_cast<std::chrono::milliseconds>(min_secs)) {};
1138 template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T>> >
1139 ChannelTimeout(const CaptureCard &parent, std::chrono::duration<T> value, std::chrono::duration<T> min_secs) :
1140 ChannelTimeout(parent, value, duration_cast<std::chrono::milliseconds>(min_secs)) {};
1141};
1142
1144{
1145 public:
1146 explicit AudioRateLimit(const CaptureCard &parent) :
1147 CaptureCardComboBoxSetting(parent, false, "audioratelimit")
1148 {
1149 setLabel(QObject::tr("Force audio sampling rate"));
1151 QObject::tr("If non-zero, override the audio sampling "
1152 "rate in the recording profile when this card is "
1153 "used. Use this if your capture card does not "
1154 "support all of the standard rates."));
1155 addSelection(QObject::tr("(None)"), "0");
1156 addSelection("32000");
1157 addSelection("44100");
1158 addSelection("48000");
1159 };
1160};
1161
1163{
1164 public:
1165 explicit SkipBtAudio(const CaptureCard &parent) :
1167 "skipbtaudio"))
1168 {
1169 setLabel(QObject::tr("Do not adjust volume"));
1171 QObject::tr("Enable this option for budget BT878 based "
1172 "DVB-T cards such as the AverTV DVB-T which "
1173 "require the audio volume to be left alone."));
1174 };
1175
1176 ~SkipBtAudio() override
1177 {
1178 delete GetStorage();
1179 }
1180};
1181
1183{
1184 public:
1185 explicit DVBCardNum(const CaptureCard &parent) :
1186 CaptureCardComboBoxSetting(parent, true, "videodevice")
1187 {
1188 setLabel(QObject::tr("DVB device"));
1190 QObject::tr("When you change this setting, the text below "
1191 "should change to the name and type of your card. "
1192 "If the card cannot be opened, an error message "
1193 "will be displayed."));
1194 fillSelections(QString());
1195 };
1196
1200 void fillSelections(const QString &current)
1201 {
1203
1204 // Get devices from filesystem
1205 QStringList sdevs = CardUtil::ProbeVideoDevices("DVB");
1206
1207 // Add current if needed
1208 if (!current.isEmpty() &&
1209 (std::find(sdevs.begin(), sdevs.end(), current) == sdevs.end()))
1210 {
1211 std::stable_sort(sdevs.begin(), sdevs.end());
1212 }
1213
1214 QStringList db = CardUtil::GetVideoDevices("DVB");
1215
1216 QMap<QString,bool> in_use;
1217 QString sel = current;
1218 for (const QString& dev : std::as_const(sdevs))
1219 {
1220 in_use[dev] = std::find(db.begin(), db.end(), dev) != db.end();
1221 if (sel.isEmpty() && !in_use[dev])
1222 sel = dev;
1223 }
1224
1225 if (sel.isEmpty() && !sdevs.empty())
1226 sel = sdevs[0];
1227
1228 QString usestr = QString(" -- ");
1229 usestr += QObject::tr("Warning: already in use");
1230
1231 for (const QString& dev : std::as_const(sdevs))
1232 {
1233 QString desc = dev + (in_use[dev] ? usestr : "");
1234 desc = (current == dev) ? dev : desc;
1235 addSelection(desc, dev, dev == sel);
1236 }
1237 }
1238
1239 void Load(void) override // StandardSetting
1240 {
1242 addSelection(QString());
1243
1245
1247 fillSelections(dev);
1248 }
1249};
1250
1251// Use capturecard/inputname to store the delivery system selection of the card
1253{
1254 public:
1255 explicit DVBCardType(const CaptureCard &parent) :
1256 CaptureCardComboBoxSetting(parent, false, "inputname")
1257 {
1258 setLabel(QObject::tr("Delivery system"));
1260 QObject::tr("If your card supports more than one delivery system "
1261 "then you can select here the one that you want to use."));
1262 };
1263};
1264
1266{
1267 public:
1269 {
1270 setLabel(QObject::tr("Frontend ID"));
1272 QObject::tr("Identification string reported by the card. "
1273 "If the message \"Could not get card info...\" appears "
1274 "the card can be in use by another program."));
1275 };
1276};
1277
1279{
1280 public:
1281 explicit DVBNoSeqStart(const CaptureCard &parent) :
1283 new CaptureCardDBStorage(this, parent, "dvb_wait_for_seqstart"))
1284 {
1285 setLabel(QObject::tr("Wait for SEQ start header"));
1286 setValue(true);
1288 QObject::tr("If enabled, drop packets from the start of a DVB "
1289 "recording until a sequence start header is seen."));
1290 };
1291
1293 {
1294 delete GetStorage();
1295 }
1296};
1297
1299{
1300 public:
1301 explicit DVBOnDemand(const CaptureCard &parent) :
1303 new CaptureCardDBStorage(this, parent, "dvb_on_demand"))
1304 {
1305 setLabel(QObject::tr("Open DVB card on demand"));
1306 setValue(true);
1308 QObject::tr("If enabled, only open the DVB card when required, "
1309 "leaving it free for other programs at other times."));
1310 };
1311
1312 ~DVBOnDemand() override
1313 {
1314 delete GetStorage();
1315 }
1316};
1317
1319{
1320 public:
1321 explicit DVBEITScan(const CaptureCard &parent) :
1323 new CaptureCardDBStorage(this, parent, "dvb_eitscan"))
1324 {
1325 setLabel(QObject::tr("Use DVB card for active EIT scan"));
1326 setValue(true);
1328 QObject::tr("If enabled, activate active scanning for "
1329 "program data (EIT). When this option is enabled "
1330 "the DVB card is constantly in use."));
1331 };
1332
1333 ~DVBEITScan() override
1334 {
1335 delete GetStorage();
1336 }
1337};
1338
1340{
1341 public:
1342 explicit DVBTuningDelay(const CaptureCard &parent) :
1343 CaptureCardSpinBoxSetting(parent, 0ms, 2s, 25ms, "dvb_tuning_delay")
1344 {
1345 setLabel(QObject::tr("DVB tuning delay (ms)"));
1346 setValueMs(0ms);
1348 QObject::tr("Some Linux DVB drivers, in particular for the "
1349 "Hauppauge Nova-T, require that we slow down "
1350 "the tuning process by specifying a delay "
1351 "(in milliseconds)."));
1352 };
1353};
1354
1356{
1357 public:
1358 explicit FirewireGUID(const CaptureCard &parent) :
1359 CaptureCardComboBoxSetting(parent, false, "videodevice")
1360 {
1361 setLabel(QObject::tr("GUID"));
1362#if CONFIG_FIREWIRE
1363 std::vector<AVCInfo> list = FirewireDevice::GetSTBList();
1364 for (auto & i : list)
1365 {
1366 QString guid = i.GetGUIDString();
1367 m_guidToAvcInfo[guid] = i;
1368 addSelection(guid);
1369 }
1370#endif // CONFIG_FIREWIRE
1371 }
1372
1373 AVCInfo GetAVCInfo(const QString &guid) const
1374 { return m_guidToAvcInfo[guid]; }
1375
1376 private:
1377 QMap<QString,AVCInfo> m_guidToAvcInfo;
1378};
1379
1381 const FirewireGUID *_guid) :
1382 CaptureCardComboBoxSetting(parent, false, "firewire_model"),
1383 m_guid(_guid)
1384{
1385 setLabel(QObject::tr("Cable box model"));
1386 addSelection(QObject::tr("Motorola Generic"), "MOTO GENERIC");
1387 addSelection(QObject::tr("SA/Cisco Generic"), "SA GENERIC");
1388 addSelection("DCH-3200");
1389 addSelection("DCX-3200");
1390 addSelection("DCT-3412");
1391 addSelection("DCT-3416");
1392 addSelection("DCT-6200");
1393 addSelection("DCT-6212");
1394 addSelection("DCT-6216");
1395 addSelection("QIP-6200");
1396 addSelection("QIP-7100");
1397 addSelection("PACE-550");
1398 addSelection("PACE-779");
1399 addSelection("SA3250HD");
1400 addSelection("SA4200HD");
1401 addSelection("SA4250HDC");
1402 addSelection("SA8300HD");
1403 QString help = QObject::tr(
1404 "Choose the model that most closely resembles your set top box. "
1405 "Depending on firmware revision SA4200HD may work better for a "
1406 "SA3250HD box.");
1408}
1409
1410void FirewireModel::SetGUID([[maybe_unused]] const QString &_guid)
1411{
1412#if CONFIG_FIREWIRE
1413 AVCInfo info = m_guid->GetAVCInfo(_guid);
1414 QString model = FirewireDevice::GetModelName(info.m_vendorid, info.m_modelid);
1415 setValue(std::max(getValueIndex(model), 0));
1416#endif // CONFIG_FIREWIRE
1417}
1418
1419void FirewireDesc::SetGUID([[maybe_unused]] const QString &_guid)
1420{
1421 setLabel(tr("Description"));
1422
1423#if CONFIG_FIREWIRE
1424 QString name = m_guid->GetAVCInfo(_guid).m_product_name;
1425 name.replace("Scientific-Atlanta", "SA");
1426 name.replace(", Inc.", "");
1427 name.replace("Explorer(R)", "");
1428 name = name.simplified();
1429 setValue((name.isEmpty()) ? "" : name);
1430#endif // CONFIG_FIREWIRE
1431}
1432
1434{
1435 public:
1436 explicit FirewireConnection(const CaptureCard &parent) :
1438 "firewire_connection"))
1439 {
1440 setLabel(QObject::tr("Connection Type"));
1441 addSelection(QObject::tr("Point to Point"),"0");
1442 addSelection(QObject::tr("Broadcast"),"1");
1443 }
1444
1446 {
1447 delete GetStorage();
1448 }
1449};
1450
1452{
1453 public:
1454 explicit FirewireSpeed(const CaptureCard &parent) :
1456 "firewire_speed"))
1457 {
1458 setLabel(QObject::tr("Speed"));
1459 addSelection(QObject::tr("100Mbps"),"0");
1460 addSelection(QObject::tr("200Mbps"),"1");
1461 addSelection(QObject::tr("400Mbps"),"2");
1462 addSelection(QObject::tr("800Mbps"),"3");
1463 }
1464
1466 {
1467 delete GetStorage();
1468 }
1469};
1470
1471#if CONFIG_FIREWIRE
1472static void FirewireConfigurationGroup(CaptureCard& parent, CardType& cardtype)
1473{
1474 auto *dev(new FirewireGUID(parent));
1475 auto *desc(new FirewireDesc(dev));
1476 auto *model(new FirewireModel(parent, dev));
1477 cardtype.addTargetedChild("FIREWIRE", dev);
1478 cardtype.addTargetedChild("FIREWIRE", new EmptyAudioDevice(parent));
1479 cardtype.addTargetedChild("FIREWIRE", new EmptyVBIDevice(parent));
1480 cardtype.addTargetedChild("FIREWIRE", desc);
1481 cardtype.addTargetedChild("FIREWIRE", model);
1482
1483#if CONFIG_FIREWIRE_LINUX
1484 cardtype.addTargetedChild("FIREWIRE", new FirewireConnection(parent));
1485 cardtype.addTargetedChild("FIREWIRE", new FirewireSpeed(parent));
1486#endif // CONFIG_FIREWIRE_LINUX
1487
1488 cardtype.addTargetedChild("FIREWIRE", new SignalTimeout(parent, 2s, 1s));
1489 cardtype.addTargetedChild("FIREWIRE", new ChannelTimeout(parent, 9s, 1.75s));
1490
1491 model->SetGUID(dev->getValue());
1492 desc->SetGUID(dev->getValue());
1493 QObject::connect(dev, qOverload<const QString&>(&StandardSetting::valueChanged),
1494 model, &FirewireModel::SetGUID);
1495 QObject::connect(dev, qOverload<const QString&>(&StandardSetting::valueChanged),
1496 desc, &FirewireDesc::SetGUID);
1497}
1498#endif
1499
1500#if CONFIG_HDHOMERUN
1501
1502// -----------------------
1503// HDHomeRun Configuration
1504// -----------------------
1505
1506HDHomeRunDeviceID::HDHomeRunDeviceID(const CaptureCard &parent,
1507 HDHomeRunConfigurationGroup &_group) :
1509 new CaptureCardDBStorage(this, parent, "videodevice")),
1510 m_group(_group)
1511{
1512 setVisible(false);
1513};
1514
1515HDHomeRunDeviceID::~HDHomeRunDeviceID()
1516{
1517 delete GetStorage();
1518}
1519
1520void HDHomeRunDeviceID::Load(void)
1521{
1523 m_group.SetDeviceCheckBoxes(getValue());
1524}
1525
1526void HDHomeRunDeviceID::Save(void)
1527{
1528 setValue(m_group.GetDeviceCheckBoxes());
1530}
1531
1532class HDHomeRunEITScan : public MythUICheckBoxSetting
1533{
1534 public:
1535 explicit HDHomeRunEITScan(const CaptureCard &parent) :
1537 new CaptureCardDBStorage(this, parent, "dvb_eitscan"))
1538 {
1539 setLabel(QObject::tr("Use HDHomeRun for active EIT scan"));
1540 setValue(true);
1542 QObject::tr("If enabled, activate active scanning for "
1543 "program data (EIT). When this option is enabled "
1544 "the HDHomeRun is constantly in use."));
1545 };
1546
1547 ~HDHomeRunEITScan() override
1548 {
1549 delete GetStorage();
1550 }
1551};
1552
1553
1554class UseHDHomeRunDevice : public TransMythUICheckBoxSetting
1555{
1556 public:
1557 explicit UseHDHomeRunDevice(QString &deviceid, QString &model,
1558 QString &ipaddr)
1559 {
1560 setLabel(QObject::tr("Use HDHomeRun %1 (%2 %3)")
1561 .arg(deviceid, model, ipaddr));
1562 setValue(false);
1564 QObject::tr("If enabled, use tuners from this HDHomeRun "
1565 "device."));
1566 };
1567};
1568
1569HDHomeRunConfigurationGroup::HDHomeRunConfigurationGroup
1570 (CaptureCard& a_parent, CardType &a_cardtype) :
1571 m_parent(a_parent),
1572 m_deviceId(new HDHomeRunDeviceID(a_parent, *this))
1573{
1574 setVisible(false);
1575
1576 // Fill Device list
1577 FillDeviceList();
1578
1579 QMap<QString, HDHomeRunDevice>::iterator dit;
1580 for (dit = m_deviceList.begin(); dit != m_deviceList.end(); ++dit)
1581 {
1582 HDHomeRunDevice &dev = *dit;
1583 dev.m_checkbox = new UseHDHomeRunDevice(
1584 dev.m_deviceId, dev.m_model, dev.m_cardIp);
1585 a_cardtype.addTargetedChild("HDHOMERUN", dev.m_checkbox);
1586 }
1587 a_cardtype.addTargetedChild("HDHOMERUN", new EmptyAudioDevice(m_parent));
1588 a_cardtype.addTargetedChild("HDHOMERUN", new EmptyVBIDevice(m_parent));
1589 a_cardtype.addTargetedChild("HDHOMERUN", m_deviceId);
1590
1591 auto *buttonRecOpt = new GroupSetting();
1592 buttonRecOpt->setLabel(tr("Recording Options"));
1593 buttonRecOpt->addChild(new SignalTimeout(m_parent, 3s, 0.25s));
1594 buttonRecOpt->addChild(new ChannelTimeout(m_parent, 6s, 1.75s));
1595 buttonRecOpt->addChild(new HDHomeRunEITScan(m_parent));
1596 a_cardtype.addTargetedChild("HDHOMERUN", buttonRecOpt);
1597};
1598
1599void HDHomeRunConfigurationGroup::FillDeviceList(void)
1600{
1601 m_deviceList.clear();
1602
1603 // Find physical devices first
1604 // ProbeVideoDevices returns "deviceid ip" pairs
1605 QStringList devs = CardUtil::ProbeVideoDevices("HDHOMERUN");
1606
1607 for (const auto & dev : std::as_const(devs))
1608 {
1609 QStringList devinfo = dev.split(" ");
1610 const QString& devid = devinfo.at(0);
1611 const QString& devip = devinfo.at(1);
1612 const QString& model = devinfo.at(2);
1613
1614 HDHomeRunDevice tmpdevice;
1615 tmpdevice.m_model = model;
1616 tmpdevice.m_cardIp = devip;
1617 tmpdevice.m_deviceId = devid;
1618 // Fully specify object. Checkboxes will be added later when
1619 // the configuration group is created.
1620 tmpdevice.m_checkbox = nullptr;
1621 m_deviceList[tmpdevice.m_deviceId] = tmpdevice;
1622 }
1623
1624#if 0
1625 // Debug dump of cards
1626 QMap<QString, HDHomeRunDevice>::iterator debugit;
1627 for (debugit = m_deviceList.begin(); debugit != m_deviceList.end(); ++debugit)
1628 {
1629 LOG(VB_GENERAL, LOG_DEBUG, QString("%1: %2 %3")
1630 .arg(debugit.key()).arg((*debugit).model)
1631 .arg((*debugit).cardip));
1632 }
1633#endif
1634}
1635
1636void HDHomeRunConfigurationGroup::SetDeviceCheckBoxes(const QString& devices)
1637{
1638 QStringList devstrs = devices.split(",");
1639 for (const QString& devstr : std::as_const(devstrs))
1640 {
1641 // Get the HDHomeRun device ID using libhdhomerun. We need to
1642 // do it this way because legacy configurations could use an
1643 // IP address and a tuner nubmer.
1644 QByteArray ba = devstr.toUtf8();
1645 hdhomerun_device_t *device = hdhomerun_device_create_from_str(
1646 ba.data(), nullptr);
1647 if (!device)
1648 continue;
1649 QString devid = QString("%1").arg(
1650 hdhomerun_device_get_device_id(device), 8, 16).toUpper();
1651 hdhomerun_device_destroy(device);
1652
1653 // If we know about this device, set its checkbox to on.
1654 QMap<QString, HDHomeRunDevice>::iterator dit;
1655 dit = m_deviceList.find(devid);
1656 if (dit != m_deviceList.end())
1657 (*dit).m_checkbox->setValue(true);
1658 }
1659}
1660
1661QString HDHomeRunConfigurationGroup::GetDeviceCheckBoxes(void)
1662{
1663 // Return a string listing each HDHomeRun device with its checbox
1664 // turned on.
1665 QStringList devstrs;
1666 QMap<QString, HDHomeRunDevice>::iterator dit;
1667 for (dit = m_deviceList.begin(); dit != m_deviceList.end(); ++dit)
1668 {
1669 if ((*dit).m_checkbox->boolValue())
1670 devstrs << (*dit).m_deviceId;
1671 }
1672 QString devices = devstrs.join(",");
1673 return devices;
1674}
1675
1676#endif
1677
1678// -----------------------
1679// VBOX Configuration
1680// -----------------------
1681
1683{
1684 setLabel(QObject::tr("IP Address"));
1685 setHelpText(QObject::tr("Device IP or ID of a VBox device. eg. '192.168.1.100' or 'vbox_3718'"));
1686 VBoxIP::setEnabled(false);
1687 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
1688 this, &VBoxIP::UpdateDevices);
1689};
1690
1692{
1694 if (e)
1695 {
1696 if (!m_oldValue.isEmpty())
1698 emit NewIP(getValue());
1699 }
1700 else
1701 {
1702 m_oldValue = getValue();
1703 }
1704}
1705
1706void VBoxIP::UpdateDevices(const QString &v)
1707{
1708 if (isEnabled())
1709 emit NewIP(v);
1710}
1711
1713{
1714 setLabel(QObject::tr("Tuner"));
1715 setHelpText(QObject::tr("Number and type of the tuner to use. eg '1-DVBT/T2'."));
1717 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
1719};
1720
1722{
1724 if (e) {
1725 if (!m_oldValue.isEmpty())
1727 emit NewTuner(getValue());
1728 }
1729 else
1730 {
1731 m_oldValue = getValue();
1732 }
1733}
1734
1735void VBoxTunerIndex::UpdateDevices(const QString &v)
1736{
1737 if (isEnabled())
1738 emit NewTuner(v);
1739}
1740
1742 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, "videodevice"))
1743{
1744 setLabel(tr("Device ID"));
1745 setHelpText(tr("Device ID of VBox device"));
1746 setReadOnly(true);
1747}
1748
1750{
1751 delete GetStorage();
1752}
1753
1754void VBoxDeviceID::SetIP(const QString &ip)
1755{
1756 m_ip = ip;
1757 setValue(QString("%1-%2").arg(m_ip, m_tuner));
1758}
1759
1760void VBoxDeviceID::SetTuner(const QString &tuner)
1761{
1762 m_tuner = tuner;
1763 setValue(QString("%1-%2").arg(m_ip, m_tuner));
1764}
1765
1766void VBoxDeviceID::SetOverrideDeviceID(const QString &deviceid)
1767{
1768 m_overrideDeviceId = deviceid;
1769 setValue(deviceid);
1770}
1771
1773{
1774 GetStorage()->Load();
1775 if (!m_overrideDeviceId.isEmpty())
1776 {
1778 m_overrideDeviceId.clear();
1779 }
1780}
1781
1783 VBoxDeviceID *deviceid,
1784 StandardSetting *desc,
1785 VBoxIP *cardip,
1786 VBoxTunerIndex *cardtuner,
1787 VBoxDeviceList *devicelist,
1788 const CaptureCard &parent) :
1789 m_deviceId(deviceid),
1790 m_desc(desc),
1791 m_cardIp(cardip),
1792 m_cardTuner(cardtuner),
1793 m_deviceList(devicelist),
1794 m_parent(parent)
1795{
1796 setLabel(QObject::tr("Available devices"));
1798 QObject::tr(
1799 "Device IP or ID, tuner number and tuner type of available VBox devices."));
1800
1801 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
1803};
1804
1806void VBoxDeviceIDList::fillSelections(const QString &cur)
1807{
1809
1810 std::vector<QString> devs;
1811 QMap<QString, bool> in_use;
1812
1813 const QString& current = cur;
1814
1815 for (auto it = m_deviceList->begin(); it != m_deviceList->end(); ++it)
1816 {
1817 devs.push_back(it.key());
1818 in_use[it.key()] = (*it).m_inUse;
1819 }
1820
1821 QString man_addr = VBoxDeviceIDList::tr("Manually Enter IP Address");
1822 QString sel = man_addr;
1823 devs.push_back(sel);
1824
1825 for (const auto & dev : devs)
1826 sel = (current == dev) ? dev : sel;
1827
1828 QString usestr = QString(" -- ");
1829 usestr += QObject::tr("Warning: already in use");
1830
1831 for (const auto & dev : devs)
1832 {
1833 QString desc = dev + (in_use[dev] ? usestr : "");
1834 addSelection(desc, dev, dev == sel);
1835 }
1836
1837 if (current != cur)
1838 {
1840 }
1841 else if (sel == man_addr && !current.isEmpty())
1842 {
1843 // Populate the proper values for IP address and tuner
1844 QStringList selection = current.split("-");
1845
1846 m_cardIp->SetOldValue(selection.first());
1847 m_cardTuner->SetOldValue(selection.last());
1848
1849 m_cardIp->setValue(selection.first());
1850 m_cardTuner->setValue(selection.last());
1851 }
1852}
1853
1855{
1857
1858 int cardid = m_parent.getCardID();
1859 QString device = CardUtil::GetVideoDevice(cardid);
1860 fillSelections(device);
1861}
1862
1864{
1865 if (v == VBoxDeviceIDList::tr("Manually Enter IP Address"))
1866 {
1867 m_cardIp->setEnabled(true);
1868 m_cardTuner->setEnabled(true);
1869 }
1870 else if (!v.isEmpty())
1871 {
1872 if (m_oldValue == VBoxDeviceIDList::tr("Manually Enter IP Address"))
1873 {
1874 m_cardIp->setEnabled(false);
1875 m_cardTuner->setEnabled(false);
1876 }
1877 m_deviceId->setValue(v);
1878
1879 // Update _cardip and _cardtuner
1881 m_cardTuner->setValue(QString("%1").arg((*m_deviceList)[v].m_tunerNo));
1883 }
1884 m_oldValue = v;
1885};
1886
1887// -----------------------
1888// IPTV Configuration
1889// -----------------------
1890
1892{
1893 public:
1894 explicit IPTVHost(const CaptureCard &parent) :
1895 CaptureCardTextEditSetting(parent, "videodevice")
1896 {
1897 setValue("http://mafreebox.freebox.fr/freeboxtv/playlist.m3u");
1898 setLabel(QObject::tr("M3U URL"));
1900 QObject::tr("URL of M3U file containing RTSP/RTP/UDP/HTTP channel URLs,"
1901 " example for HDHomeRun: http://<ipv4>/lineup.m3u and for Freebox:"
1902 " http://mafreebox.freebox.fr/freeboxtv/playlist.m3u."
1903 ));
1904 }
1905};
1906
1907static void IPTVConfigurationGroup(CaptureCard& parent, CardType& cardType)
1908{
1909 cardType.addTargetedChild("FREEBOX", new IPTVHost(parent));
1910 cardType.addTargetedChild("FREEBOX", new ChannelTimeout(parent, 30s, 1.75s));
1911 cardType.addTargetedChild("FREEBOX", new EmptyAudioDevice(parent));
1912 cardType.addTargetedChild("FREEBOX", new EmptyVBIDevice(parent));
1913}
1914
1916{
1917 public:
1918 explicit ASIDevice(const CaptureCard &parent) :
1919 CaptureCardComboBoxSetting(parent, true, "videodevice")
1920 {
1921 setLabel(QObject::tr("ASI device"));
1922 fillSelections(QString());
1923 };
1924
1928 void fillSelections(const QString &current)
1929 {
1931
1932 // Get devices from filesystem
1933 QStringList sdevs = CardUtil::ProbeVideoDevices("ASI");
1934
1935 // Add current if needed
1936 if (!current.isEmpty() &&
1937 (std::find(sdevs.begin(), sdevs.end(), current) == sdevs.end()))
1938 {
1939 std::stable_sort(sdevs.begin(), sdevs.end());
1940 }
1941
1942 // Get devices from DB
1943 QStringList db = CardUtil::GetVideoDevices("ASI");
1944
1945 // Figure out which physical devices are already in use
1946 // by another card defined in the DB, and select a device
1947 // for new configs (preferring non-conflicing devices).
1948 QMap<QString,bool> in_use;
1949 QString sel = current;
1950 for (const QString& dev : std::as_const(sdevs))
1951 {
1952 in_use[dev] = std::find(db.begin(), db.end(), dev) != db.end();
1953 if (sel.isEmpty() && !in_use[dev])
1954 sel = dev;
1955 }
1956
1957 // Unfortunately all devices are conflicted, select first device.
1958 if (sel.isEmpty() && !sdevs.empty())
1959 sel = sdevs[0];
1960
1961 QString usestr = QString(" -- ");
1962 usestr += QObject::tr("Warning: already in use");
1963
1964 // Add the devices to the UI
1965 bool found = false;
1966 for (const QString& dev : std::as_const(sdevs))
1967 {
1968 QString desc = dev + (in_use[dev] ? usestr : "");
1969 desc = (current == dev) ? dev : desc;
1970 addSelection(desc, dev, dev == sel);
1971 found |= (dev == sel);
1972 }
1973
1974 // If a configured device isn't on the list, add it with warning
1975 if (!found && !current.isEmpty())
1976 {
1977 QString desc = current + " -- " +
1978 QObject::tr("Warning: unable to open");
1979 addSelection(desc, current, true);
1980 }
1981 }
1982
1983 void Load(void) override // StandardSetting
1984 {
1986 addSelection(QString());
1987 GetStorage()->Load();
1989 }
1990};
1991
1993 CardType &cardType):
1994 m_parent(a_parent),
1995 m_device(new ASIDevice(m_parent)),
1996 m_cardInfo(new TransTextEditSetting())
1997{
1998 setVisible(false);
1999 m_cardInfo->setLabel(tr("Status"));
2000 m_cardInfo->setEnabled(false);
2001
2002 cardType.addTargetedChild("ASI", m_device);
2003 cardType.addTargetedChild("ASI", new EmptyAudioDevice(m_parent));
2004 cardType.addTargetedChild("ASI", new EmptyVBIDevice(m_parent));
2005 cardType.addTargetedChild("ASI", m_cardInfo);
2006
2007 connect(m_device, qOverload<const QString&>(&StandardSetting::valueChanged),
2009
2011};
2012
2013void ASIConfigurationGroup::probeCard([[maybe_unused]] const QString &device)
2014{
2015#if CONFIG_ASI
2016 if (device.isEmpty())
2017 {
2018 m_cardInfo->setValue("");
2019 return;
2020 }
2021
2022 if ((m_parent.getCardID() != 0) && m_parent.GetRawCardType() != "ASI")
2023 {
2024 m_cardInfo->setValue("");
2025 return;
2026 }
2027
2028 QString error;
2029 int device_num = CardUtil::GetASIDeviceNumber(device, &error);
2030 if (device_num < 0)
2031 {
2032 m_cardInfo->setValue(tr("Not a valid DVEO ASI card"));
2033 LOG(VB_GENERAL, LOG_WARNING,
2034 "ASIConfigurationGroup::probeCard(), Warning: " + error);
2035 return;
2036 }
2037 m_cardInfo->setValue(tr("Valid DVEO ASI card"));
2038#else
2039 m_cardInfo->setValue(QString("Not compiled with ASI support"));
2040#endif
2041}
2042
2044 CardType& a_cardtype):
2045 m_parent(a_parent),
2046 m_info(new GroupSetting()), m_size(new GroupSetting())
2047{
2048 setVisible(false);
2049 auto *device = new FileDevice(m_parent);
2050 device->setHelpText(tr("A local file used to simulate a recording."
2051 " Leave empty to use MythEvents to trigger an"
2052 " external program to import recording files."));
2053 a_cardtype.addTargetedChild("IMPORT", device);
2054
2055 a_cardtype.addTargetedChild("IMPORT", new EmptyAudioDevice(m_parent));
2056 a_cardtype.addTargetedChild("IMPORT", new EmptyVBIDevice(m_parent));
2057
2058 m_info->setLabel(tr("File info"));
2059 a_cardtype.addTargetedChild("IMPORT", m_info);
2060
2061 m_size->setLabel(tr("File size"));
2062 a_cardtype.addTargetedChild("IMPORT", m_size);
2063
2064 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2066
2067 probeCard(device->getValue());
2068};
2069
2070void ImportConfigurationGroup::probeCard(const QString &device)
2071{
2072 QString ci;
2073 QString cs;
2074 QFileInfo fileInfo(device);
2075
2076 // For convenience, ImportRecorder allows both formats:
2077 if (device.startsWith("file:", Qt::CaseInsensitive))
2078 fileInfo.setFile(device.mid(5));
2079
2080 if (fileInfo.exists())
2081 {
2082 if (fileInfo.isReadable() && (fileInfo.isFile()))
2083 {
2084 ci = HTTPRequest::TestMimeType(fileInfo.absoluteFilePath());
2085 cs = tr("%1 MB").arg(fileInfo.size() / 1024 / 1024);
2086 }
2087 else
2088 {
2089 ci = tr("File not readable");
2090 }
2091 }
2092 else
2093 {
2094 ci = tr("File %1 does not exist").arg(device);
2095 }
2096
2097 m_info->setValue(ci);
2098 m_size->setValue(cs);
2099}
2100
2101// -----------------------
2102// VBox Configuration
2103// -----------------------
2104
2106 (CaptureCard& a_parent, CardType& a_cardtype) :
2107 m_parent(a_parent),
2108 m_desc(new GroupSetting()),
2109 m_deviceId(new VBoxDeviceID(a_parent)),
2110 m_cardIp(new VBoxIP()),
2111 m_cardTuner(new VBoxTunerIndex())
2112{
2113 setVisible(false);
2114
2115 // Fill Device list
2117
2118 m_desc->setLabel(tr("Description"));
2121
2122 a_cardtype.addTargetedChild("VBOX", m_deviceIdList);
2123 a_cardtype.addTargetedChild("VBOX", new EmptyAudioDevice(m_parent));
2124 a_cardtype.addTargetedChild("VBOX", new EmptyVBIDevice(m_parent));
2125 a_cardtype.addTargetedChild("VBOX", m_deviceId);
2126 a_cardtype.addTargetedChild("VBOX", m_desc);
2127 a_cardtype.addTargetedChild("VBOX", m_cardIp);
2128 a_cardtype.addTargetedChild("VBOX", m_cardTuner);
2129 a_cardtype.addTargetedChild("VBOX", new SignalTimeout(m_parent, 7s, 1s));
2130 a_cardtype.addTargetedChild("VBOX", new ChannelTimeout(m_parent, 10s, 1.75s));
2131
2132 connect(m_cardIp, &VBoxIP::NewIP,
2136};
2137
2139{
2140 m_deviceList.clear();
2141
2142 // Find physical devices first
2143 // ProbeVideoDevices returns "deviceid ip tunerno tunertype"
2144 QStringList devs = CardUtil::ProbeVideoDevices("VBOX");
2145
2146 for (const auto & dev : std::as_const(devs))
2147 {
2148 QStringList devinfo = dev.split(" ");
2149 const QString& id = devinfo.at(0);
2150 const QString& ip = devinfo.at(1);
2151 const QString& tunerNo = devinfo.at(2);
2152 const QString& tunerType = devinfo.at(3);
2153
2154 VBoxDevice tmpdevice;
2155 tmpdevice.m_deviceId = id;
2156 tmpdevice.m_desc = CardUtil::GetVBoxdesc(id, ip, tunerNo, tunerType);
2157 tmpdevice.m_cardIp = ip;
2158 tmpdevice.m_inUse = false;
2159 tmpdevice.m_discovered = true;
2160 tmpdevice.m_tunerNo = tunerNo;
2161 tmpdevice.m_tunerType = tunerType;
2162 tmpdevice.m_mythDeviceId = id + "-" + tunerNo + "-" + tunerType;
2163 m_deviceList[tmpdevice.m_mythDeviceId] = tmpdevice;
2164 }
2165
2166 // Now find configured devices
2167
2168 // returns "ip.ip.ip.ip-n-type" or deviceid-n-type values
2169 QStringList db = CardUtil::GetVideoDevices("VBOX");
2170
2171 for (const auto & dev : std::as_const(db))
2172 {
2173 QMap<QString, VBoxDevice>::iterator dit;
2174 dit = m_deviceList.find(dev);
2175
2176 if (dit != m_deviceList.end())
2177 (*dit).m_inUse = true;
2178 }
2179}
2180
2181// -----------------------
2182// Ceton Configuration
2183// -----------------------
2184#if CONFIG_CETON
2185CetonSetting::CetonSetting(QString label, const QString& helptext)
2186{
2187 setLabel(std::move(label));
2188 setHelpText(helptext);
2189 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
2190 this, &CetonSetting::UpdateDevices);
2191}
2192
2193void CetonSetting::UpdateDevices(const QString &v)
2194{
2195 if (isEnabled())
2196 emit NewValue(v);
2197}
2198
2199void CetonSetting::LoadValue(const QString &value)
2200{
2201 setValue(value);
2202}
2203
2204CetonDeviceID::CetonDeviceID(const CaptureCard &parent) :
2205 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, "videodevice")),
2206 m_parent(parent)
2207{
2208 setLabel(tr("Device ID"));
2209 setHelpText(tr("Device ID of Ceton device"));
2210}
2211
2212CetonDeviceID::~CetonDeviceID()
2213{
2214 delete GetStorage();
2215}
2216
2217void CetonDeviceID::SetIP(const QString &ip)
2218{
2219 static const QRegularExpression ipV4Regex
2220 { "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){4}$" };
2221 auto match = ipV4Regex.match(ip + ".");
2222 if (match.hasMatch())
2223 {
2224 m_ip = ip;
2225 setValue(QString("%1-RTP.%3").arg(m_ip, m_tuner));
2226 }
2227}
2228
2229void CetonDeviceID::SetTuner(const QString &tuner)
2230{
2231 static const QRegularExpression oneDigit { "^\\d$" };
2232 auto match = oneDigit.match(tuner);
2233 if (match.hasMatch())
2234 {
2235 m_tuner = tuner;
2236 setValue(QString("%1-RTP.%2").arg(m_ip, m_tuner));
2237 }
2238}
2239
2240void CetonDeviceID::Load(void)
2241{
2242 GetStorage()->Load();
2243 UpdateValues();
2244}
2245
2246void CetonDeviceID::UpdateValues(void)
2247{
2248 static const QRegularExpression newstyle { R"(^([0-9.]+)-(\d|RTP)\.(\d)$)" };
2249 auto match = newstyle.match(getValue());
2250 if (match.hasMatch())
2251 {
2252 emit LoadedIP(match.captured(1));
2253 emit LoadedTuner(match.captured(3));
2254 }
2255}
2256
2257void CetonSetting::CetonConfigurationGroup(CaptureCard& parent, CardType& cardtype)
2258{
2259 auto *deviceid = new CetonDeviceID(parent);
2260 auto *desc = new GroupSetting();
2261 desc->setLabel(tr("CetonConfigurationGroup", "Description"));
2262 auto *ip = new CetonSetting(tr("IP Address"),
2263 tr("IP Address of the Ceton device (192.168.200.1 by default)"));
2264 auto *tuner = new CetonSetting(tr("Tuner"),
2265 tr("Number of the tuner on the Ceton device (first tuner is number 0)"));
2266
2267 cardtype.addTargetedChild("CETON", ip);
2268 cardtype.addTargetedChild("CETON", tuner);
2269 cardtype.addTargetedChild("CETON", deviceid);
2270 cardtype.addTargetedChild("CETON", desc);
2271 cardtype.addTargetedChild("CETON", new SignalTimeout(parent, 1s, 0.25s));
2272 cardtype.addTargetedChild("CETON", new ChannelTimeout(parent, 3s, 1.75s));
2273
2274 QObject::connect(ip, &CetonSetting::NewValue,
2275 deviceid, &CetonDeviceID::SetIP);
2276 QObject::connect(tuner, &CetonSetting::NewValue,
2277 deviceid, &CetonDeviceID::SetTuner);
2278
2279 QObject::connect(deviceid, &CetonDeviceID::LoadedIP,
2280 ip, &CetonSetting::LoadValue);
2281 QObject::connect(deviceid, &CetonDeviceID::LoadedTuner,
2282 tuner, &CetonSetting::LoadValue);
2283}
2284#endif
2285
2286// Override database schema default, set schedgroup false
2288{
2289 public:
2290 explicit SchedGroupFalse(const CaptureCard &parent) :
2292 "schedgroup"))
2293 {
2294 setValue(false);
2295 setVisible(false);
2296 };
2297
2299 {
2300 delete GetStorage();
2301 }
2302};
2303
2305 CardType& cardtype,
2306 const QString &inputtype) :
2307 m_parent(parent),
2308 m_cardInfo(new GroupSetting()),
2309 m_vbiDev(new VBIDevice(m_parent))
2310{
2311 setVisible(false);
2312 QRegularExpression drv { "^(?!ivtv|hdpvr|(saa7164(.*))).*$" };
2313 auto *device = new VideoDevice(m_parent, 0, 15, QString(), drv);
2314
2315 m_cardInfo->setLabel(tr("Probed info"));
2316 m_cardInfo->setReadOnly(true);
2317
2318 cardtype.addTargetedChild(inputtype, device);
2319 cardtype.addTargetedChild(inputtype, m_cardInfo);
2320 cardtype.addTargetedChild(inputtype, m_vbiDev);
2321 cardtype.addTargetedChild(inputtype, new AudioDevice(m_parent));
2322 cardtype.addTargetedChild(inputtype, new AudioRateLimit(m_parent));
2323 cardtype.addTargetedChild(inputtype, new SkipBtAudio(m_parent));
2324
2325 // Override database schema default, set schedgroup false
2326 cardtype.addTargetedChild(inputtype, new SchedGroupFalse(m_parent));
2327
2328 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2330
2331 probeCard(device->getValue());
2332};
2333
2334void V4LConfigurationGroup::probeCard(const QString &device)
2335{
2336 QString cn = tr("Failed to open");
2337 QString ci = cn;
2338 QString dn;
2339
2340 QByteArray adevice = device.toLatin1();
2341 int videofd = open(adevice.constData(), O_RDWR);
2342 if (videofd >= 0)
2343 {
2344 if (!CardUtil::GetV4LInfo(videofd, cn, dn))
2345 ci = cn = tr("Failed to probe");
2346 else if (!dn.isEmpty())
2347 ci = cn + " [" + dn + "]";
2348 close(videofd);
2349 }
2350
2351 m_cardInfo->setValue(ci);
2352 m_vbiDev->setFilter(cn, dn);
2353}
2354
2356 CardType &cardtype) :
2357 m_parent(parent),
2358 m_vbiDevice(new VBIDevice(parent)),
2359 m_cardInfo(new GroupSetting())
2360{
2361 setVisible(false);
2362 QRegularExpression drv { "^(ivtv|(saa7164(.*)))$" };
2363 m_device = new VideoDevice(m_parent, 0, 15, QString(), drv);
2364 m_vbiDevice->setVisible(false);
2365
2366 m_cardInfo->setLabel(tr("Probed info"));
2367 m_cardInfo->setReadOnly(true);
2368
2369 cardtype.addTargetedChild("MPEG", m_device);
2370 cardtype.addTargetedChild("MPEG", m_vbiDevice);
2371 cardtype.addTargetedChild("MPEG", m_cardInfo);
2372 cardtype.addTargetedChild("MPEG", new ChannelTimeout(m_parent, 12s, 2s));
2373
2374 // Override database schema default, set schedgroup false
2375 cardtype.addTargetedChild("MPEG", new SchedGroupFalse(m_parent));
2376
2377 connect(m_device, qOverload<const QString&>(&StandardSetting::valueChanged),
2379
2381}
2382
2383void MPEGConfigurationGroup::probeCard(const QString &device)
2384{
2385 QString cn = tr("Failed to open");
2386 QString ci = cn;
2387 QString dn;
2388
2389 QByteArray adevice = device.toLatin1();
2390 int videofd = open(adevice.constData(), O_RDWR);
2391 if (videofd >= 0)
2392 {
2393 if (!CardUtil::GetV4LInfo(videofd, cn, dn))
2394 ci = cn = tr("Failed to probe");
2395 else if (!dn.isEmpty())
2396 ci = cn + " [" + dn + "]";
2397 close(videofd);
2398 }
2399
2400 m_cardInfo->setValue(ci);
2401 m_vbiDevice->setVisible(dn!="ivtv");
2402 m_vbiDevice->setFilter(cn, dn);
2403}
2404
2406 CardType &a_cardtype) :
2407 m_parent(a_parent),
2408 m_info(new GroupSetting()), m_size(new GroupSetting())
2409{
2410 setVisible(false);
2411 auto *device = new FileDevice(m_parent);
2412 device->setHelpText(tr("A local MPEG file used to simulate a recording."));
2413
2414 a_cardtype.addTargetedChild("DEMO", device);
2415
2416 a_cardtype.addTargetedChild("DEMO", new EmptyAudioDevice(m_parent));
2417 a_cardtype.addTargetedChild("DEMO", new EmptyVBIDevice(m_parent));
2418
2419 m_info->setLabel(tr("File info"));
2420 a_cardtype.addTargetedChild("DEMO", m_info);
2421
2422 m_size->setLabel(tr("File size"));
2423 a_cardtype.addTargetedChild("DEMO", m_size);
2424
2425 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2427
2428 probeCard(device->getValue());
2429}
2430
2431void DemoConfigurationGroup::probeCard(const QString &device)
2432{
2433 QString ci;
2434 QString cs;
2435 QFileInfo fileInfo(device);
2436 if (fileInfo.exists())
2437 {
2438 if (fileInfo.isReadable() && (fileInfo.isFile()))
2439 {
2440 ci = HTTPRequest::TestMimeType(fileInfo.absoluteFilePath());
2441 cs = tr("%1 MB").arg(fileInfo.size() / 1024 / 1024);
2442 }
2443 else
2444 {
2445 ci = tr("File not readable");
2446 }
2447 }
2448 else
2449 {
2450 ci = tr("File does not exist");
2451 }
2452
2453 m_info->setValue(ci);
2454 m_size->setValue(cs);
2455}
2456
2457#ifndef _WIN32
2458ExternalConfigurationGroup::ExternalConfigurationGroup(CaptureCard &a_parent,
2459 CardType &a_cardtype) :
2460 m_parent(a_parent),
2461 m_info(new GroupSetting())
2462{
2463 setVisible(false);
2464 auto *device = new CommandPath(m_parent);
2465 device->setLabel(tr("Command path"));
2466 device->setHelpText(tr("A 'black box' application controlled via stdin, status on "
2467 "stderr and TransportStream read from stdout.\n"
2468 "Use absolute path or path relative to the current directory."));
2469 a_cardtype.addTargetedChild("EXTERNAL", device);
2470
2471 m_info->setLabel(tr("File info"));
2472 a_cardtype.addTargetedChild("EXTERNAL", m_info);
2473
2474 a_cardtype.addTargetedChild("EXTERNAL",
2475 new ChannelTimeout(m_parent, 20s, 1.75s));
2476
2477 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2478 this, &ExternalConfigurationGroup::probeApp);
2479
2480 probeApp(device->getValue());
2481}
2482
2483void ExternalConfigurationGroup::probeApp(const QString & path)
2484{
2485 int idx1 = path.startsWith("file:", Qt::CaseInsensitive) ? 5 : 0;
2486 int idx2 = path.indexOf(' ', idx1);
2487
2488 QString ci;
2489 QFileInfo fileInfo(path.mid(idx1, idx2 - idx1));
2490
2491 if (fileInfo.exists())
2492 {
2493 ci = tr("File '%1' is valid.").arg(fileInfo.absoluteFilePath());
2494 if (!fileInfo.isReadable() || !fileInfo.isFile())
2495 ci = tr("WARNING: File '%1' is not readable.")
2496 .arg(fileInfo.absoluteFilePath());
2497 if (!fileInfo.isExecutable())
2498 ci = tr("WARNING: File '%1' is not executable.")
2499 .arg(fileInfo.absoluteFilePath());
2500 }
2501 else
2502 {
2503 ci = tr("WARNING: File '%1' does not exist.")
2504 .arg(fileInfo.absoluteFilePath());
2505 }
2506
2507 m_info->setValue(ci);
2508 m_info->setHelpText(ci);
2509}
2510#endif // !defined( _WIN32 )
2511
2513 CardType &a_cardtype) :
2514 m_parent(a_parent), m_cardInfo(new GroupSetting()),
2515 m_audioInput(new TunerCardAudioInput(m_parent, QString(), "HDPVR"))
2516{
2517 setVisible(false);
2518
2519 auto *device = new VideoDevice(m_parent, 0, 15, QString(),
2520 QRegularExpression("^hdpvr$"));
2521
2522 m_cardInfo->setLabel(tr("Probed info"));
2523 m_cardInfo->setReadOnly(true);
2524
2525 a_cardtype.addTargetedChild("HDPVR", device);
2526 a_cardtype.addTargetedChild("HDPVR", new EmptyAudioDevice(m_parent));
2527 a_cardtype.addTargetedChild("HDPVR", new EmptyVBIDevice(m_parent));
2528 a_cardtype.addTargetedChild("HDPVR", m_cardInfo);
2529 a_cardtype.addTargetedChild("HDPVR", m_audioInput);
2530 a_cardtype.addTargetedChild("HDPVR", new ChannelTimeout(m_parent, 15s, 2s));
2531
2532 // Override database schema default, set schedgroup false
2533 a_cardtype.addTargetedChild("HDPVR", new SchedGroupFalse(m_parent));
2534
2535 connect(device, qOverload<const QString&>(&StandardSetting::valueChanged),
2537
2538 probeCard(device->getValue());
2539}
2540
2541void HDPVRConfigurationGroup::probeCard(const QString &device)
2542{
2543 QString cn = tr("Failed to open");
2544 QString ci = cn;
2545 QString dn;
2546
2547 int videofd = open(device.toLocal8Bit().constData(), O_RDWR);
2548 if (videofd >= 0)
2549 {
2550 if (!CardUtil::GetV4LInfo(videofd, cn, dn))
2551 ci = cn = tr("Failed to probe");
2552 else if (!dn.isEmpty())
2553 ci = cn + " [" + dn + "]";
2554 close(videofd);
2556 }
2557
2558 m_cardInfo->setValue(ci);
2559}
2560
2562 m_parent(parent),
2563 m_cardInfo(new GroupSetting())
2564{
2565 setVisible(false);
2566
2567 m_device = new VideoDevice(m_parent, 0, 15);
2568
2569 setLabel(QObject::tr("V4L2 encoder devices (multirec capable)"));
2570
2571 m_cardInfo->setLabel(tr("Probed info"));
2572 m_cardInfo->setReadOnly(true);
2573
2574 cardtype.addTargetedChild("V4L2ENC", m_device);
2575 cardtype.addTargetedChild("V4L2ENC", m_cardInfo);
2576
2577 // Override database schema default, set schedgroup false
2578 cardtype.addTargetedChild("V4L2ENC", new SchedGroupFalse(m_parent));
2579
2580 connect(m_device, qOverload<const QString&>(&StandardSetting::valueChanged),
2582
2583 const QString &device_name = m_device->getValue();
2584 if (!device_name.isEmpty())
2585 probeCard(device_name);
2586}
2587
2588void V4L2encGroup::probeCard([[maybe_unused]] const QString &device_name)
2589{
2590#if CONFIG_V4L2
2591 QString card_name = tr("Failed to open");
2592 QString card_info = card_name;
2593 V4L2util v4l2(device_name);
2594
2595 if (!v4l2.IsOpen())
2596 {
2597 m_driverName = tr("Failed to probe");
2598 return;
2599 }
2600 m_driverName = v4l2.DriverName();
2601 card_name = v4l2.CardName();
2602
2603 if (!m_driverName.isEmpty())
2604 card_info = card_name + " [" + m_driverName + "]";
2605
2606 m_cardInfo->setValue(card_info);
2607
2608 if (m_device->getSubSettings()->empty())
2609 {
2610 auto* audioinput = new TunerCardAudioInput(m_parent, QString(), "V4L2");
2611 if (audioinput->fillSelections(device_name) > 1)
2612 {
2613 audioinput->setName("AudioInput");
2615 }
2616 else
2617 {
2618 delete audioinput;
2619 }
2620
2621 if (v4l2.HasSlicedVBI())
2622 {
2623 auto* vbidev = new VBIDevice(m_parent);
2624 if (vbidev->setFilter(card_name, m_driverName) > 0)
2625 {
2626 vbidev->setName("VBIDevice");
2628 }
2629 else
2630 {
2631 delete vbidev;
2632 }
2633 }
2634
2637 new ChannelTimeout(m_parent, 15s, 2s));
2638 }
2639#endif // CONFIG_V4L2
2640}
2641
2643{
2644 setLabel(QObject::tr("Capture Card Setup"));
2645
2646 auto* cardtype = new CardType(parent);
2647 parent.addChild(cardtype);
2648
2649#if CONFIG_DVB
2650 cardtype->addTargetedChild("DVB",
2651 new DVBConfigurationGroup(parent, *cardtype));
2652#endif // CONFIG_DVB
2653
2654#if CONFIG_V4L2
2655 cardtype->addTargetedChild("HDPVR",
2656 new HDPVRConfigurationGroup(parent, *cardtype));
2657#endif // CONFIG_V4L2
2658
2659#if CONFIG_HDHOMERUN
2660 cardtype->addTargetedChild("HDHOMERUN",
2661 new HDHomeRunConfigurationGroup(parent, *cardtype));
2662#endif // CONFIG_HDHOMERUN
2663
2664#if CONFIG_VBOX
2665 cardtype->addTargetedChild("VBOX",
2666 new VBoxConfigurationGroup(parent, *cardtype));
2667#endif // CONFIG_VBOX
2668
2669#if CONFIG_SATIP
2670 cardtype->addTargetedChild("SATIP",
2671 new SatIPConfigurationGroup(parent, *cardtype));
2672#endif // CONFIG_SATIP
2673
2674#if CONFIG_FIREWIRE
2675 FirewireConfigurationGroup(parent, *cardtype);
2676#endif // CONFIG_FIREWIRE
2677
2678#if CONFIG_CETON
2679 CetonSetting::CetonConfigurationGroup(parent, *cardtype);
2680#endif // CONFIG_CETON
2681
2682#if CONFIG_IPTV
2683 IPTVConfigurationGroup(parent, *cardtype);
2684#endif // CONFIG_IPTV
2685
2686#if CONFIG_V4L2
2687 cardtype->addTargetedChild("V4L2ENC", new V4L2encGroup(parent, *cardtype));
2688 cardtype->addTargetedChild("V4L",
2689 new V4LConfigurationGroup(parent, *cardtype, "V4L"));
2690 cardtype->addTargetedChild("MJPEG",
2691 new V4LConfigurationGroup(parent, *cardtype, "MJPEG"));
2692 cardtype->addTargetedChild("GO7007",
2693 new V4LConfigurationGroup(parent, *cardtype, "GO7007"));
2694 cardtype->addTargetedChild("MPEG",
2695 new MPEGConfigurationGroup(parent, *cardtype));
2696#endif // CONFIG_V4L2
2697
2698#if CONFIG_ASI
2699 cardtype->addTargetedChild("ASI",
2700 new ASIConfigurationGroup(parent, *cardtype));
2701#endif // CONFIG_ASI
2702
2703 // for testing without any actual tuner hardware:
2704 cardtype->addTargetedChild("IMPORT",
2705 new ImportConfigurationGroup(parent, *cardtype));
2706 cardtype->addTargetedChild("DEMO",
2707 new DemoConfigurationGroup(parent, *cardtype));
2708#ifndef _WIN32
2709 cardtype->addTargetedChild("EXTERNAL",
2710 new ExternalConfigurationGroup(parent,
2711 *cardtype));
2712#endif
2713}
2714
2715CaptureCard::CaptureCard(bool use_card_group)
2716 : m_id(new ID)
2717{
2718 addChild(m_id);
2719 if (use_card_group)
2720 CaptureCardGroup(*this);
2721 addChild(new Hostname(*this));
2722}
2723
2725{
2726 int cardid = getCardID();
2727 if (cardid <= 0)
2728 return {};
2729 return CardUtil::GetRawInputType(cardid);
2730}
2731
2733{
2735 QString qstr =
2736 "SELECT cardid, videodevice, cardtype, displayname "
2737 "FROM capturecard "
2738 "WHERE hostname = :HOSTNAME AND parentid = 0 "
2739 "ORDER BY cardid";
2740
2741 query.prepare(qstr);
2742 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
2743
2744 if (!query.exec())
2745 {
2746 MythDB::DBError("CaptureCard::fillSelections", query);
2747 return;
2748 }
2749
2751
2752 while (query.next())
2753 {
2754 uint cardid = query.value(0).toUInt();
2755 QString videodevice = query.value(1).toString();
2756 QString cardtype = query.value(2).toString();
2757 QString displayname = query.value(3).toString();
2758
2759 QString label = QString("%1 (%2)")
2760 .arg(CardUtil::GetDeviceLabel(cardtype, videodevice), displayname);
2761
2762 auto *card = new CaptureCard();
2763 card->loadByID(cardid);
2764 card->setLabel(label);
2765 setting->addChild(card);
2766 }
2767}
2768
2770{
2771 m_id->setValue(cardid);
2772 Load();
2773}
2774
2776{
2777 return true;
2778}
2779
2781{
2783}
2784
2785
2787{
2788 uint init_cardid = getCardID();
2789 QString init_dev = CardUtil::GetVideoDevice(init_cardid);
2790
2792
2794
2796
2797 uint cardid = getCardID();
2798 QString type = CardUtil::GetRawInputType(cardid);
2799 QString dev = CardUtil::GetVideoDevice(cardid);
2800
2801 if (dev != init_dev)
2802 {
2803 if (!init_dev.isEmpty())
2804 {
2805 uint init_groupid = CardUtil::GetDeviceInputGroup(init_cardid);
2806 CardUtil::UnlinkInputGroup(init_cardid, init_groupid);
2807 }
2808 if (!dev.isEmpty())
2809 {
2810 uint groupid =
2812 gCoreContext->GetHostName(), dev);
2813 CardUtil::LinkInputGroup(cardid, groupid);
2814 CardUtil::UnlinkInputGroup(0, groupid);
2815 }
2816 }
2817
2818 // Handle any cloning we may need to do
2820 {
2821 std::vector<uint> clones = CardUtil::GetChildInputIDs(cardid);
2822 for (uint clone : clones)
2823 CardUtil::CloneCard(cardid, clone);
2824 }
2825}
2826
2828{
2829 if (getCardID() == 0)
2830 {
2831 Save();
2832 Load();
2833 }
2834}
2835
2837 CaptureCardComboBoxSetting(parent, false, "cardtype")
2838{
2839 setLabel(QObject::tr("Card type"));
2840 setHelpText(QObject::tr("Change the cardtype to the appropriate type for "
2841 "the capture card you are configuring."));
2842 fillSelections(this);
2843}
2844
2846{
2847#if CONFIG_DVB
2848 setting->addSelection(
2849 QObject::tr("DVB-T/S/C, ATSC or ISDB-T tuner card"), "DVB");
2850#endif // CONFIG_DVB
2851
2852#if CONFIG_V4L2
2853 setting->addSelection(
2854 QObject::tr("V4L2 encoder"), "V4L2ENC");
2855 setting->addSelection(
2856 QObject::tr("HD-PVR H.264 encoder"), "HDPVR");
2857#endif // CONFIG_V4L2
2858
2859#if CONFIG_HDHOMERUN
2860 setting->addSelection(
2861 QObject::tr("HDHomeRun networked tuner"), "HDHOMERUN");
2862#endif // CONFIG_HDHOMERUN
2863
2864#if CONFIG_SATIP
2865 setting->addSelection(
2866 QObject::tr("Sat>IP networked tuner"), "SATIP");
2867#endif // CONFIG_SATIP
2868
2869#if CONFIG_VBOX
2870 setting->addSelection(
2871 QObject::tr("V@Box TV Gateway networked tuner"), "VBOX");
2872#endif // CONFIG_VBOX
2873
2874#if CONFIG_FIREWIRE
2875 setting->addSelection(
2876 QObject::tr("FireWire cable box"), "FIREWIRE");
2877#endif // CONFIG_FIREWIRE
2878
2879#if CONFIG_CETON
2880 setting->addSelection(
2881 QObject::tr("Ceton Cablecard tuner"), "CETON");
2882#endif // CONFIG_CETON
2883
2884#if CONFIG_IPTV
2885 setting->addSelection(QObject::tr("IPTV recorder"), "FREEBOX");
2886#endif // CONFIG_IPTV
2887
2888#if CONFIG_V4L2
2889 setting->addSelection(
2890 QObject::tr("Analog to MPEG-2 encoder card (PVR-150/250/350, etc)"), "MPEG");
2891 setting->addSelection(
2892 QObject::tr("Analog to MJPEG encoder card (Matrox G200, DC10, etc)"), "MJPEG");
2893 setting->addSelection(
2894 QObject::tr("Analog to MPEG-4 encoder (Plextor ConvertX USB, etc)"),
2895 "GO7007");
2896 setting->addSelection(
2897 QObject::tr("Analog capture card"), "V4L");
2898#endif // CONFIG_V4L2
2899
2900#if CONFIG_ASI
2901 setting->addSelection(QObject::tr("DVEO ASI recorder"), "ASI");
2902#endif
2903
2904 setting->addSelection(QObject::tr("Import test recorder"), "IMPORT");
2905 setting->addSelection(QObject::tr("Demo test recorder"), "DEMO");
2906#ifndef _WIN32
2907 setting->addSelection(QObject::tr("External (black box) recorder"),
2908 "EXTERNAL");
2909#endif
2910}
2911
2913 StandardSetting(new CaptureCardDBStorage(this, parent, "hostname"))
2914{
2915 setVisible(false);
2917}
2918
2920{
2921 delete GetStorage();
2922}
2923
2925{
2926 public:
2927 explicit InputName(const CardInput &parent) :
2928 MythUIComboBoxSetting(new CardInputDBStorage(this, parent, "inputname"))
2929 {
2930 setLabel(QObject::tr("Input name"));
2931 };
2932
2933 ~InputName() override
2934 {
2935 delete GetStorage();
2936 }
2937
2938 void Load(void) override // StandardSetting
2939 {
2942 };
2943
2945 clearSelections();
2946 addSelection(QObject::tr("(None)"), "None");
2947 auto *storage = dynamic_cast<CardInputDBStorage*>(GetStorage());
2948 if (storage == nullptr)
2949 return;
2950 uint cardid = storage->getInputID();
2951 QString type = CardUtil::GetRawInputType(cardid);
2952 QString device = CardUtil::GetVideoDevice(cardid);
2953 QStringList inputs;
2954 CardUtil::GetDeviceInputNames(device, type, inputs);
2955 while (!inputs.isEmpty())
2956 {
2957 addSelection(inputs.front());
2958 inputs.pop_front();
2959 }
2960 };
2961};
2962
2964{
2965 public:
2967 {
2968 setLabel(QObject::tr("Delivery system"));
2969 setHelpText(QObject::tr(
2970 "This shows the delivery system (modulation), for instance DVB-T2, "
2971 "that you have selected when you configured the capture card. "
2972 "This must be the same as the modulation used by the video source. "));
2973 };
2974};
2975
2977{
2979
2980 public:
2981 explicit InputDisplayName(const CardInput &parent) :
2982 MythUITextEditSetting(new CardInputDBStorage(this, parent, "displayname")), m_parent(parent)
2983 {
2984 setLabel(QObject::tr("Display name"));
2985 setHelpText(QObject::tr(
2986 "This name is displayed on screen when Live TV begins "
2987 "and in various other places. Make sure the last two "
2988 "characters are unique for each input or use a "
2989 "slash ('/') to designate the unique portion."));
2990 };
2991
2993 {
2994 delete GetStorage();
2995 }
2996 void Load(void) override {
2998 if (getValue().isEmpty())
2999 setValue(tr("Input %1").arg(m_parent.getInputID()));
3000 }
3001 private:
3003};
3004
3006{
3007 public:
3008 CardInputComboBoxSetting(const CardInput &parent, const QString &setting) :
3009 MythUIComboBoxSetting(new CardInputDBStorage(this, parent, setting))
3010 {
3011 }
3012
3014 {
3015 delete GetStorage();
3016 }
3017};
3018
3020{
3021 public:
3022 explicit SourceID(const CardInput &parent) :
3023 CardInputComboBoxSetting(parent, "sourceid")
3024 {
3025 setLabel(QObject::tr("Video source"));
3026 addSelection(QObject::tr("(None)"), "0");
3027 };
3028
3029 void Load(void) override // StandardSetting
3030 {
3033 };
3034
3036 clearSelections();
3037 addSelection(QObject::tr("(None)"), "0");
3039 };
3040};
3041
3043{
3044 public:
3045 InputGroup(const CardInput &parent, uint group_num) :
3046 m_cardInput(parent),
3047 m_groupNum(group_num)
3048 {
3049 setLabel(QObject::tr("Input group") +
3050 QString(" %1").arg(m_groupNum + 1));
3051 setHelpText(QObject::tr(
3052 "Leave as 'Generic' unless this input is shared with "
3053 "another device. Only one of the inputs in an input "
3054 "group will be allowed to record at any given time."));
3055 }
3056
3057 void Load(void) override; // StandardSetting
3058
3059 void Save(void) override // StandardSetting
3060 {
3061 uint inputid = m_cardInput.getInputID();
3062 uint new_groupid = getValue().toUInt();
3063
3064 if (m_groupId && (m_groupId != new_groupid))
3065 CardUtil::UnlinkInputGroup(inputid, m_groupId);
3066
3067 if (new_groupid)
3068 CardUtil::LinkInputGroup(inputid, new_groupid);
3069 }
3070
3071 virtual void Save(const QString& /*destination*/) { Save(); }
3072
3073 private:
3076 uint m_groupId {0};
3077};
3078
3080{
3081#if 0
3082 LOG(VB_GENERAL, LOG_DEBUG, QString("InputGroup::Load() %1 %2")
3083 .arg(m_groupNum).arg(m_cardInput.getInputID()));
3084#endif
3085
3086 uint inputid = m_cardInput.getInputID();
3087 QMap<uint, uint> grpcnt;
3088 std::vector<QString> names;
3089 std::vector<uint> grpid;
3090 std::vector<uint> selected_groupids;
3091
3092 names.push_back(QObject::tr("Generic"));
3093 grpid.push_back(0);
3094 grpcnt[0]++;
3095
3097 query.prepare(
3098 "SELECT cardinputid, inputgroupid, inputgroupname "
3099 "FROM inputgroup "
3100 "WHERE inputgroupname LIKE 'user:%' "
3101 "ORDER BY inputgroupid, cardinputid, inputgroupname");
3102
3103 if (!query.exec())
3104 {
3105 MythDB::DBError("InputGroup::Load()", query);
3106 }
3107 else
3108 {
3109 while (query.next())
3110 {
3111 uint groupid = query.value(1).toUInt();
3112 if ((inputid != 0U) && (query.value(0).toUInt() == inputid))
3113 selected_groupids.push_back(groupid);
3114
3115 grpcnt[groupid]++;
3116
3117 if (grpcnt[groupid] == 1)
3118 {
3119 names.push_back(query.value(2).toString().mid(5, -1));
3120 grpid.push_back(groupid);
3121 }
3122 }
3123 }
3124
3125 // makes sure we select something
3126 m_groupId = 0;
3127 if (m_groupNum < selected_groupids.size())
3128 m_groupId = selected_groupids[m_groupNum];
3129
3130#if 0
3131 LOG(VB_GENERAL, LOG_DEBUG, QString("Group num: %1 id: %2")
3132 .arg(m_groupNum).arg(m_groupId));
3133 {
3134 QString msg;
3135 for (uint i = 0; i < selected_groupids.size(); i++)
3136 msg += QString("%1 ").arg(selected_groupids[i]);
3137 LOG(VB_GENERAL, LOG_DEBUG, msg);
3138 }
3139#endif
3140
3141 // add selections to combobox
3142 clearSelections();
3143 uint index = 0;
3144 for (size_t i = 0; i < names.size(); i++)
3145 {
3146 bool sel = (m_groupId == grpid[i]);
3147 index = (sel) ? i : index;
3148
3149#if 0
3150 LOG(VB_GENERAL, LOG_DEBUG, QString("grpid %1, name '%2', i %3, s %4")
3151 .arg(grpid[i]).arg(names[i]) .arg(index).arg(sel ? "T" : "F"));
3152#endif
3153
3154 addSelection(names[i], QString::number(grpid[i]), sel);
3155 }
3156
3157#if 0
3158 LOG(VB_GENERAL, LOG_DEBUG, QString("Group index: %1").arg(index));
3159#endif
3160
3161 if (!names.empty())
3162 setValue(index);
3163
3165}
3166
3168{
3169 public:
3170 explicit QuickTune(const CardInput &parent) :
3171 CardInputComboBoxSetting(parent, "quicktune")
3172 {
3173 setLabel(QObject::tr("Use quick tuning"));
3174 addSelection(QObject::tr("Never"), "0", true);
3175 addSelection(QObject::tr("Live TV only"), "1", false);
3176 addSelection(QObject::tr("Always"), "2", false);
3177 setHelpText(QObject::tr(
3178 "If enabled, MythTV will tune using only the "
3179 "MPEG program number. The program numbers "
3180 "change more often than DVB or ATSC tuning "
3181 "parameters, so this is slightly less reliable. "
3182 "This will also inhibit EIT gathering during "
3183 "Live TV and recording."));
3184 };
3185};
3186
3188{
3189 public:
3190 explicit ExternalChannelCommand(const CardInput &parent) :
3191 MythUITextEditSetting(new CardInputDBStorage(this, parent, "externalcommand"))
3192 {
3193 setLabel(QObject::tr("External channel change command"));
3194 setValue("");
3195 setHelpText(QObject::tr("If specified, this command will be run to "
3196 "change the channel for inputs which have an external "
3197 "tuner device such as a cable box. The first argument "
3198 "will be the channel number."));
3199 };
3200
3202 {
3203 delete GetStorage();
3204 }
3205};
3206
3208{
3209 public:
3210 explicit PresetTuner(const CardInput &parent) :
3211 MythUITextEditSetting(new CardInputDBStorage(this, parent, "tunechan"))
3212 {
3213 setLabel(QObject::tr("Preset tuner to channel"));
3214 setValue("");
3215 setHelpText(QObject::tr("Leave this blank unless you have an external "
3216 "tuner that is connected to the tuner input of your card. "
3217 "If so, you will need to specify the preset channel for "
3218 "the signal (normally 3 or 4)."));
3219 };
3220
3221 ~PresetTuner() override
3222 {
3223 delete GetStorage();
3224 }
3225};
3226
3227void StartingChannel::SetSourceID(const QString &sourceid)
3228{
3229 clearSelections();
3230 if (sourceid.isEmpty() || !sourceid.toUInt())
3231 return;
3232
3233 // Get the existing starting channel
3234 auto *storage = dynamic_cast<CardInputDBStorage*>(GetStorage());
3235 if (storage == nullptr)
3236 return;
3237 int inputId = storage->getInputID();
3238 QString startChan = CardUtil::GetStartChannel(inputId);
3239
3240 ChannelInfoList channels = ChannelUtil::GetAllChannels(sourceid.toUInt());
3241
3242 if (channels.empty())
3243 {
3244 addSelection(tr("Please add channels to this source"),
3245 startChan.isEmpty() ? "0" : startChan);
3246 return;
3247 }
3248
3249 // If there are channels sort them, then add theme
3250 // (selecting the old start channel if it is there).
3251 QString order = gCoreContext->GetSetting("ChannelOrdering", "channum");
3252 ChannelUtil::SortChannels(channels, order);
3253 bool has_visible = false;
3254 for (size_t i = 0; i < channels.size() && !has_visible; i++)
3255 has_visible |= channels[i].m_visible;
3256
3257 for (auto & channel : channels)
3258 {
3259 const QString channum = channel.m_chanNum;
3260 bool sel = channum == startChan;
3261 if (!has_visible || channel.m_visible || sel)
3262 {
3263 addSelection(channum, channum, sel);
3264 }
3265 }
3266}
3267
3269{
3270 public:
3271 explicit InputPriority(const CardInput &parent) :
3272 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "recpriority"),
3273 -99, 99, 1)
3274 {
3275 setLabel(QObject::tr("Input priority"));
3276 setValue(0);
3277 setHelpText(QObject::tr("If the input priority is not equal for "
3278 "all inputs, the scheduler may choose to record a show "
3279 "at a later time so that it can record on an input with "
3280 "a higher value."));
3281 };
3282
3284 {
3285 delete GetStorage();
3286 }
3287};
3288
3290{
3291 public:
3292 ScheduleOrder(const CardInput &parent, int _value) :
3293 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "schedorder"),
3294 0, 99, 1)
3295 {
3296 setLabel(QObject::tr("Schedule order"));
3297 setValue(_value);
3298 setHelpText(QObject::tr("If priorities and other factors are equal "
3299 "the scheduler will choose the available "
3300 "input with the lowest, non-zero value. "
3301 "Setting this value to zero will make the "
3302 "input unavailable to the scheduler."));
3303 };
3304
3306 {
3307 delete GetStorage();
3308 }
3309};
3310
3312{
3313 public:
3314 LiveTVOrder(const CardInput &parent, int _value) :
3315 MythUISpinBoxSetting(new CardInputDBStorage(this, parent, "livetvorder"),
3316 0, 99, 1)
3317 {
3318 setLabel(QObject::tr("Live TV order"));
3319 setValue(_value);
3320 setHelpText(QObject::tr("When entering Live TV, the available, local "
3321 "input with the lowest, non-zero value will "
3322 "be used. If no local inputs are available, "
3323 "the available, remote input with the lowest, "
3324 "non-zero value will be used. "
3325 "Setting this value to zero will make the "
3326 "input unavailable to live TV."));
3327 };
3328
3329 ~LiveTVOrder() override
3330 {
3331 delete GetStorage();
3332 }
3333};
3334
3336{
3337 public:
3338 explicit DishNetEIT(const CardInput &parent) :
3340 "dishnet_eit"))
3341 {
3342 setLabel(QObject::tr("Use DishNet long-term EIT data"));
3343 setValue(false);
3345 QObject::tr(
3346 "If you point your satellite dish toward DishNet's birds, "
3347 "you may wish to enable this feature. For best results, "
3348 "enable general EIT collection as well."));
3349 };
3350
3351 ~DishNetEIT() override
3352 {
3353 delete GetStorage();
3354 }
3355};
3356
3357CardInput::CardInput(const QString & cardtype, const QString & device,
3358 int _cardid) :
3359 m_id(new ID()),
3360 m_inputName(new InputName(*this)),
3361 m_sourceId(new SourceID(*this)),
3362 m_startChan(new StartingChannel(*this)),
3363 m_scan(new ButtonStandardSetting(tr("Scan for channels"))),
3364 m_srcFetch(new ButtonStandardSetting(tr("Fetch channels from listings source"))),
3365 m_externalInputSettings(new DiSEqCDevSettings()),
3366 m_inputGrp0(new InputGroup(*this, 0)),
3367 m_inputGrp1(new InputGroup(*this, 1))
3368{
3369 addChild(m_id);
3370
3372 {
3374 _cardid, true));
3375 }
3376
3377 // Delivery system for DVB, input name for other,
3378 // same field capturecard/inputname for both
3379 if ("DVB" == cardtype)
3380 {
3381 auto *ds = new DeliverySystem();
3382 ds->setValue(CardUtil::GetDeliverySystemFromDB(_cardid));
3383 addChild(ds);
3384 }
3385 else if (CardUtil::IsV4L(cardtype))
3386 {
3388 }
3389 addChild(new InputDisplayName(*this));
3391
3392 if (CardUtil::IsEncoder(cardtype) || CardUtil::IsUnscanable(cardtype))
3393 {
3394 addChild(new ExternalChannelCommand(*this));
3395 if (CardUtil::HasTuner(cardtype, device))
3396 addChild(new PresetTuner(*this));
3397 }
3398 else
3399 {
3400 addChild(new QuickTune(*this));
3401 if ("DVB" == cardtype)
3402 addChild(new DishNetEIT(*this));
3403 }
3404
3406 tr("Use channel scanner to find channels for this input."));
3407
3409 tr("This uses the listings data source to "
3410 "provide the channels for this input.") + " " +
3411 tr("This can take a long time to run."));
3412
3415
3417
3418 auto *interact = new GroupSetting();
3419
3420 interact->setLabel(QObject::tr("Interactions between inputs"));
3421 if (CardUtil::IsTunerSharingCapable(cardtype))
3422 {
3423 m_instanceCount = new InstanceCount(*this);
3424 interact->addChild(m_instanceCount);
3425 m_schedGroup = new SchedGroup(*this);
3426 interact->addChild(m_schedGroup);
3427 }
3428 interact->addChild(new InputPriority(*this));
3429 interact->addChild(new ScheduleOrder(*this, _cardid));
3430 interact->addChild(new LiveTVOrder(*this, _cardid));
3431
3432 auto *ingrpbtn =
3433 new ButtonStandardSetting(QObject::tr("Create a New Input Group"));
3434 ingrpbtn->setHelpText(
3435 QObject::tr("Input groups are only needed when two or more cards "
3436 "share the same resource such as a FireWire card and "
3437 "an analog card input controlling the same set top box."));
3438 interact->addChild(ingrpbtn);
3439 interact->addChild(m_inputGrp0);
3440 interact->addChild(m_inputGrp1);
3441
3442 addChild(interact);
3443
3444 setObjectName("CardInput");
3445 SetSourceID("-1");
3446
3449 connect(m_sourceId, qOverload<const QString&>(&StandardSetting::valueChanged),
3451 connect(m_sourceId, qOverload<const QString&>(&StandardSetting::valueChanged),
3452 this, &CardInput::SetSourceID);
3453 connect(ingrpbtn, &ButtonStandardSetting::clicked,
3455}
3456
3458{
3460 {
3462 m_externalInputSettings = nullptr;
3463 }
3464}
3465
3466void CardInput::SetSourceID(const QString &sourceid)
3467{
3468 uint cid = m_id->getValue().toUInt();
3469 QString raw_card_type = CardUtil::GetRawInputType(cid);
3470 bool enable = (sourceid.toInt() > 0);
3471 m_scan->setEnabled(enable && !raw_card_type.isEmpty() &&
3472 !CardUtil::IsUnscanable(raw_card_type));
3473 m_srcFetch->setEnabled(enable);
3474}
3475
3476QString CardInput::getSourceName(void) const
3477{
3478 return m_sourceId->getValueLabel();
3479}
3480
3482{
3483 m_inputGrp0->Save();
3484 m_inputGrp1->Save();
3485
3486 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
3487 auto *settingdialog =
3488 new MythTextInputDialog(popupStack, tr("Enter new group name"));
3489
3490 if (settingdialog->Create())
3491 {
3492 connect(settingdialog, &MythTextInputDialog::haveResult,
3494 popupStack->AddScreen(settingdialog);
3495 }
3496 else
3497 {
3498 delete settingdialog;
3499 }
3500}
3501
3502void CardInput::CreateNewInputGroupSlot(const QString& name)
3503{
3504 if (name.isEmpty())
3505 {
3506 ShowOkPopup(tr("Sorry, this Input Group name cannot be blank."));
3507 return;
3508 }
3509
3510 QString new_name = QString("user:") + name;
3511
3513 query.prepare("SELECT inputgroupname "
3514 "FROM inputgroup "
3515 "WHERE inputgroupname = :GROUPNAME");
3516 query.bindValue(":GROUPNAME", new_name);
3517
3518 if (!query.exec())
3519 {
3520 MythDB::DBError("CreateNewInputGroup 1", query);
3521 return;
3522 }
3523
3524 if (query.next())
3525 {
3526 ShowOkPopup(tr("Sorry, this Input Group name is already in use."));
3527 return;
3528 }
3529
3530 uint inputgroupid = CardUtil::CreateInputGroup(new_name);
3531
3532 m_inputGrp0->Load();
3533 m_inputGrp1->Load();
3534
3535 if (m_inputGrp0->getValue().toUInt() == 0U)
3536 {
3538 m_inputGrp0->getValueIndex(QString::number(inputgroupid)));
3539 }
3540 else
3541 {
3543 m_inputGrp1->getValueIndex(QString::number(inputgroupid)));
3544 }
3545}
3546
3548{
3549 uint srcid = m_sourceId->getValue().toUInt();
3550 uint crdid = m_id->getValue().toUInt();
3551 QString in = m_inputName->getValue();
3552
3553#if CONFIG_BACKEND
3554 uint num_channels_before = SourceUtil::GetChannelCount(srcid);
3555
3556 Save(); // save info for scanner.
3557
3558 QString cardtype = CardUtil::GetRawInputType(crdid);
3559 if (CardUtil::IsUnscanable(cardtype))
3560 {
3561 LOG(VB_GENERAL, LOG_ERR,
3562 QString("Sorry, %1 cards do not yet support scanning.")
3563 .arg(cardtype));
3564 return;
3565 }
3566
3568 auto *ssd = new StandardSettingDialog(mainStack, "generalsettings",
3569 new ScanWizard(srcid, crdid, in));
3570
3571 if (ssd->Create())
3572 {
3573 connect(ssd, &StandardSettingDialog::Exiting, this,
3574 [srcid, this, num_channels_before]()
3575 {
3576 if (SourceUtil::GetChannelCount(srcid))
3577 m_startChan->SetSourceID(QString::number(srcid));
3578 if (num_channels_before)
3579 {
3580 m_startChan->Load();
3581 m_startChan->Save();
3582 }
3583 });
3584 mainStack->AddScreen(ssd);
3585 }
3586 else
3587 {
3588 delete ssd;
3589 }
3590
3591#else
3592 LOG(VB_GENERAL, LOG_ERR, "You must compile the backend "
3593 "to be able to scan for channels");
3594#endif
3595}
3596
3598{
3599 uint srcid = m_sourceId->getValue().toUInt();
3600 uint crdid = m_id->getValue().toUInt();
3601
3602 uint num_channels_before = SourceUtil::GetChannelCount(srcid);
3603
3604 if (crdid && srcid)
3605 {
3606 Save(); // save info for fetch..
3607
3608 QString cardtype = CardUtil::GetRawInputType(crdid);
3609
3610 if (!CardUtil::IsCableCardPresent(crdid, cardtype) &&
3611 !CardUtil::IsUnscanable(cardtype) &&
3612 !CardUtil::IsEncoder(cardtype) &&
3613 cardtype != "HDHOMERUN" &&
3614 !num_channels_before)
3615 {
3616 LOG(VB_GENERAL, LOG_ERR, "Skipping channel fetch, you need to "
3617 "scan for channels first.");
3618 return;
3619 }
3620
3622 }
3623
3624 if (SourceUtil::GetChannelCount(srcid))
3625 m_startChan->SetSourceID(QString::number(srcid));
3626 if (num_channels_before)
3627 {
3628 m_startChan->Load();
3629 m_startChan->Save();
3630 }
3631}
3632
3634{
3635 QString cardinputidTag(":WHERECARDID");
3636
3637 QString query("cardid = " + cardinputidTag);
3638
3639 bindings.insert(cardinputidTag, m_parent.getInputID());
3640
3641 return query;
3642}
3643
3645{
3646 QString cardinputidTag(":SETCARDID");
3647 QString colTag(":SET" + GetColumnName().toUpper());
3648
3649 QString query("cardid = " + cardinputidTag + ", " +
3650 GetColumnName() + " = " + colTag);
3651
3652 bindings.insert(cardinputidTag, m_parent.getInputID());
3653 bindings.insert(colTag, m_user->GetDBValue());
3654
3655 return query;
3656}
3657
3658void CardInput::loadByID(int inputid)
3659{
3660 m_id->setValue(inputid);
3661 m_externalInputSettings->Load(inputid);
3663}
3664
3665void CardInput::loadByInput(int _cardid, const QString& _inputname)
3666{
3668 query.prepare("SELECT cardid FROM capturecard "
3669 "WHERE cardid = :CARDID AND inputname = :INPUTNAME");
3670 query.bindValue(":CARDID", _cardid);
3671 query.bindValue(":INPUTNAME", _inputname);
3672
3673 if (query.exec() && query.isActive() && query.next())
3674 {
3675 loadByID(query.value(0).toInt());
3676 }
3677}
3678
3680{
3681 uint cardid = m_id->getValue().toUInt();
3684
3685 uint icount = 1;
3686 if (m_instanceCount)
3687 icount = m_instanceCount->getValue().toUInt();
3688
3689 CardUtil::InputSetMaxRecordings(cardid, icount);
3690}
3691
3693{
3694 return m_parent.getInputID();
3695}
3696
3698{
3699 return m_parent.getCardID();
3700}
3701
3703{
3704 emit Clicked(m_value);
3705}
3706
3707void CaptureCardEditor::AddSelection(const QString &label, const CCESlot slot)
3708{
3709 auto *button = new ButtonStandardSetting(label);
3710 connect(button, &ButtonStandardSetting::clicked, this, slot);
3711 addChild(button);
3712}
3713
3714void CaptureCardEditor::AddSelection(const QString &label, const CCESlotConst slot)
3715{
3716 auto *button = new ButtonStandardSetting(label);
3717 connect(button, &ButtonStandardSetting::clicked, this, slot);
3718 addChild(button);
3719}
3720
3722{
3724 tr("Are you sure you want to delete "
3725 "ALL capture cards on %1?").arg(gCoreContext->GetHostName()),
3727 true);
3728}
3729
3731{
3733 tr("Are you sure you want to delete "
3734 "ALL capture cards?"),
3736 true);
3737}
3738
3740{
3741 auto *card = new CaptureCard();
3742 card->setLabel(tr("New capture card"));
3743 card->Load();
3744 addChild(card);
3745 emit settingsChanged(this);
3746}
3747
3749{
3750 if (!doDelete)
3751 return;
3752
3754 Load();
3755 emit settingsChanged(this);
3756}
3757
3759{
3760 if (!doDelete)
3761 return;
3762
3764
3765 cards.prepare(
3766 "SELECT cardid "
3767 "FROM capturecard "
3768 "WHERE hostname = :HOSTNAME");
3769 cards.bindValue(":HOSTNAME", gCoreContext->GetHostName());
3770
3771 if (!cards.exec() || !cards.isActive())
3772 {
3774 tr("Error getting list of cards for this host. "
3775 "Unable to delete capturecards for %1")
3776 .arg(gCoreContext->GetHostName()));
3777
3778 MythDB::DBError("Selecting cardids for deletion", cards);
3779 return;
3780 }
3781
3782 while (cards.next())
3783 CardUtil::DeleteInput(cards.value(0).toUInt());
3784
3785 Load();
3786 emit settingsChanged(this);
3787}
3788
3790{
3791 setLabel(tr("Capture cards"));
3792}
3793
3795{
3796 clearSettings();
3797 AddSelection(QObject::tr("(New capture card)"), &CaptureCardEditor::AddNewCard);
3798 AddSelection(QObject::tr("(Delete all capture cards on %1)")
3799 .arg(gCoreContext->GetHostName()),
3801 AddSelection(QObject::tr("(Delete all capture cards)"),
3804}
3805
3807{
3808 setLabel(tr("Video sources"));
3809}
3810
3812{
3813 clearSettings();
3814 AddSelection(QObject::tr("(New video source)"), &VideoSourceEditor::NewSource);
3815 AddSelection(QObject::tr("(Delete all video sources)"),
3819}
3820
3821void VideoSourceEditor::AddSelection(const QString &label, const VSESlot slot)
3822{
3823 auto *button = new ButtonStandardSetting(label);
3824 connect(button, &ButtonStandardSetting::clicked, this, slot);
3825 addChild(button);
3826}
3827
3828void VideoSourceEditor::AddSelection(const QString &label, const VSESlotConst slot)
3829{
3830 auto *button = new ButtonStandardSetting(label);
3831 connect(button, &ButtonStandardSetting::clicked, this, slot);
3832 addChild(button);
3833}
3834
3836{
3838 tr("Are you sure you want to delete "
3839 "ALL video sources?"),
3841 true);
3842}
3843
3845{
3846 if (!doDelete)
3847 return;
3848
3850 Load();
3851 emit settingsChanged(this);
3852}
3853
3855{
3856 auto *source = new VideoSource();
3857 source->setLabel(tr("New video source"));
3858 source->Load();
3859 addChild(source);
3860 emit settingsChanged(this);
3861}
3862
3864{
3865 setLabel(tr("Input connections"));
3866}
3867
3869{
3870 m_cardInputs.clear();
3871 clearSettings();
3872
3873 // We do this manually because we want custom labels. If
3874 // SelectSetting provided a facility to edit the labels, we
3875 // could use CaptureCard::fillSelections
3876
3878 query.prepare(
3879 "SELECT cardid, videodevice, cardtype, displayname "
3880 "FROM capturecard "
3881 "WHERE hostname = :HOSTNAME "
3882 " AND parentid = 0 "
3883 "ORDER BY cardid");
3884 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
3885
3886 if (!query.exec())
3887 {
3888 MythDB::DBError("CardInputEditor::load", query);
3889 return;
3890 }
3891
3892 while (query.next())
3893 {
3894 uint cardid = query.value(0).toUInt();
3895 QString videodevice = query.value(1).toString();
3896 QString cardtype = query.value(2).toString();
3897 QString displayname = query.value(3).toString();
3898
3899 auto *cardinput = new CardInput(cardtype, videodevice, cardid);
3900 cardinput->loadByID(cardid);
3901 QString inputlabel = QString("%1 (%2) -> %3")
3902 .arg(CardUtil::GetDeviceLabel(cardtype, videodevice),
3903 displayname, cardinput->getSourceName());
3904 m_cardInputs.push_back(cardinput);
3905 cardinput->setLabel(inputlabel);
3906 addChild(cardinput);
3907 }
3908
3910}
3911
3912#if CONFIG_DVB
3913static QString remove_chaff(const QString &name)
3914{
3915 // Trim off some of the chaff.
3916 QString short_name = name;
3917 if (short_name.startsWith("LG Electronics"))
3918 short_name = short_name.right(short_name.length() - 15);
3919 if (short_name.startsWith("Oren"))
3920 short_name = short_name.right(short_name.length() - 5);
3921 if (short_name.startsWith("Nextwave"))
3922 short_name = short_name.right(short_name.length() - 9);
3923 if (short_name.startsWith("frontend", Qt::CaseInsensitive))
3924 short_name = short_name.left(short_name.length() - 9);
3925 if (short_name.endsWith("VSB/QAM"))
3926 short_name = short_name.left(short_name.length() - 8);
3927 if (short_name.endsWith("VSB"))
3928 short_name = short_name.left(short_name.length() - 4);
3929 if (short_name.endsWith("DVB-T"))
3930 short_name = short_name.left(short_name.length() - 6);
3931
3932 // It would be infinitely better if DVB allowed us to query
3933 // the vendor ID. But instead we have to guess based on the
3934 // demodulator name. This means cards like the Air2PC HD5000
3935 // and DViCO Fusion HDTV cards are not identified correctly.
3936 short_name = short_name.simplified();
3937 if (short_name.startsWith("or51211", Qt::CaseInsensitive))
3938 short_name = "pcHDTV HD-2000";
3939 else if (short_name.startsWith("or51132", Qt::CaseInsensitive))
3940 short_name = "pcHDTV HD-3000";
3941 else if (short_name.startsWith("bcm3510", Qt::CaseInsensitive))
3942 short_name = "Air2PC v1";
3943 else if (short_name.startsWith("nxt2002", Qt::CaseInsensitive) ||
3944 short_name.startsWith("nxt200x", Qt::CaseInsensitive))
3945 short_name = "Air2PC v2";
3946 else if (short_name.startsWith("lgdt3302", Qt::CaseInsensitive))
3947 short_name = "DViCO HDTV3";
3948 else if (short_name.startsWith("lgdt3303", Qt::CaseInsensitive))
3949 short_name = "DViCO v2 or Air2PC v3 or pcHDTV HD-5500";
3950
3951 return short_name;
3952}
3953#endif // CONFIG_DVB
3954
3955void DVBConfigurationGroup::reloadDiseqcTree(const QString &videodevice)
3956{
3957 if (m_diseqcTree)
3958 m_diseqcTree->Load(videodevice);
3959
3960 if (m_cardType->getValue() == "DVB-S" ||
3961 m_cardType->getValue() == "DVB-S2" )
3962 {
3963 m_diseqcBtn->setVisible(true);
3964 }
3965 else
3966 {
3967 m_diseqcBtn->setVisible(false);
3968 }
3969 emit getParent()->settingsChanged(this);
3970}
3971
3972void DVBConfigurationGroup::probeCard(const QString &videodevice)
3973{
3974 if (videodevice.isEmpty())
3975 {
3976 m_cardName->setValue("");
3977 m_cardType->setValue("");
3978 return;
3979 }
3980
3981 if ((m_parent.getCardID() != 0) && m_parent.GetRawCardType() != "DVB")
3982 {
3983 m_cardName->setValue("");
3984 m_cardType->setValue("");
3985 return;
3986 }
3987
3988#if CONFIG_DVB
3989 QString frontend_name = CardUtil::ProbeDVBFrontendName(videodevice);
3990 QString subtype = CardUtil::ProbeDVBType(videodevice);
3991
3992 QString err_open = tr("Could not open card %1").arg(videodevice);
3993 QString err_other = tr("Could not get card info for card %1").arg(videodevice);
3994
3995 switch (CardUtil::toInputType(subtype))
3996 {
3998 m_cardName->setValue(err_open);
3999 m_cardType->setValue(strerror(errno));
4000 break;
4002 m_cardName->setValue(err_other);
4003 m_cardType->setValue("Unknown error");
4004 break;
4006 m_cardName->setValue(err_other);
4007 m_cardType->setValue(strerror(errno));
4008 break;
4010 m_cardType->setValue("DVB-S");
4011 m_cardName->setValue(frontend_name);
4014 break;
4016 m_cardType->setValue("DVB-S2");
4017 m_cardName->setValue(frontend_name);
4020 break;
4022 m_cardType->setValue("DVB-C");
4023 m_cardName->setValue(frontend_name);
4026 break;
4028 m_cardType->setValue("DVB-T2");
4029 m_cardName->setValue(frontend_name);
4032 break;
4034 {
4035 m_cardType->setValue("DVB-T");
4036 m_cardName->setValue(frontend_name);
4039 if (frontend_name.toLower().indexOf("usb") >= 0)
4040 {
4043 }
4044
4045 // slow down tuning for buggy drivers
4046 if ((frontend_name == "DiBcom 3000P/M-C DVB-T") ||
4047 (frontend_name ==
4048 "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"))
4049 {
4050 m_tuningDelay->setValueMs(200ms);
4051 }
4052
4053#if 0 // frontends on hybrid DVB-T/Analog cards
4054 QString short_name = remove_chaff(frontend_name);
4055 m_buttonAnalog->setVisible(
4056 short_name.startsWith("zarlink zl10353",
4057 Qt::CaseInsensitive) ||
4058 short_name.startsWith("wintv hvr 900 m/r: 65008/a1c0",
4059 Qt::CaseInsensitive) ||
4060 short_name.startsWith("philips tda10046h",
4061 Qt::CaseInsensitive));
4062#endif
4063 }
4064 break;
4066 {
4067 QString short_name = remove_chaff(frontend_name);
4068 m_cardType->setValue("ATSC");
4069 m_cardName->setValue(short_name);
4072
4073 // According to #1779 and #1935 the AverMedia 180 needs
4074 // a 3000 ms signal timeout, at least for QAM tuning.
4075 if (frontend_name == "Nextwave NXT200X VSB/QAM frontend")
4076 {
4079 }
4080
4081#if 0 // frontends on hybrid DVB-T/Analog cards
4082 if (frontend_name.toLower().indexOf("usb") < 0)
4083 {
4084 m_buttonAnalog->setVisible(
4085 short_name.startsWith("pchdtv", Qt::CaseInsensitive) ||
4086 short_name.startsWith("dvico", Qt::CaseInsensitive) ||
4087 short_name.startsWith("nextwave", Qt::CaseInsensitive));
4088 }
4089#endif
4090 }
4091 break;
4092 default:
4093 break;
4094 }
4095
4096 // Create selection list of all delivery systems of this card
4097 {
4099 QStringList delsyslist = CardUtil::ProbeDeliverySystems(videodevice);
4100 for (const auto & item : std::as_const(delsyslist))
4101 {
4102 LOG(VB_GENERAL, LOG_DEBUG, QString("DVBCardType: add deliverysystem:%1")
4103 .arg(item));
4104
4105 m_cardType->addSelection(item, item);
4106 }
4107
4108 // Default value, used if not already defined in capturecard/inputname
4109 QString delsys = CardUtil::ProbeDefaultDeliverySystem(videodevice);
4110 if (!delsys.isEmpty())
4111 {
4112 m_cardType->setValue(delsys);
4113 }
4114 }
4115#
4116#else
4117 m_cardType->setValue(QString("Recompile with DVB-Support!"));
4118#endif
4119}
4120
4122 QString dev, QString type) :
4123 CaptureCardComboBoxSetting(parent, false, "audiodevice"),
4124 m_lastDevice(std::move(dev)), m_lastCardType(std::move(type))
4125{
4126 setLabel(QObject::tr("Audio input"));
4127 setHelpText(QObject::tr("If there is more than one audio input, "
4128 "select which one to use."));
4129 int cardid = parent.getCardID();
4130 if (cardid <= 0)
4131 return;
4132
4135}
4136
4137int TunerCardAudioInput::fillSelections(const QString &device)
4138{
4140
4141 if (device.isEmpty())
4142 return 0;
4143
4144 m_lastDevice = device;
4145 QStringList inputs =
4147
4148 for (uint i = 0; i < (uint)inputs.size(); i++)
4149 {
4150 addSelection(inputs[i], QString::number(i),
4151 m_lastDevice == QString::number(i));
4152 }
4153 return inputs.size();
4154}
4155
4157 CardType& cardType) :
4158 m_parent(a_parent),
4159 m_cardNum(new DVBCardNum(a_parent)),
4160 m_cardName(new DVBCardName()),
4161 m_cardType(new DVBCardType(a_parent)),
4162 m_signalTimeout(new SignalTimeout(a_parent, 0.5s, 0.25s)),
4163 m_tuningDelay(new DVBTuningDelay(a_parent)),
4164 m_diseqcTree(new DiSEqCDevTree()),
4165 m_diseqcBtn(new DeviceTree(*m_diseqcTree))
4166{
4167 setVisible(false);
4168
4169 m_channelTimeout = new ChannelTimeout(m_parent, 3s, 1.75s);
4170
4171 cardType.addTargetedChild("DVB", m_cardNum);
4172
4173 cardType.addTargetedChild("DVB", m_cardName);
4174 cardType.addTargetedChild("DVB", m_cardType);
4175
4176 cardType.addTargetedChild("DVB", m_signalTimeout);
4177 cardType.addTargetedChild("DVB", m_channelTimeout);
4178
4179 cardType.addTargetedChild("DVB", new EmptyAudioDevice(m_parent));
4180 cardType.addTargetedChild("DVB", new EmptyVBIDevice(m_parent));
4181
4182 cardType.addTargetedChild("DVB", new DVBNoSeqStart(m_parent));
4183 cardType.addTargetedChild("DVB", new DVBOnDemand(m_parent));
4184 cardType.addTargetedChild("DVB", new DVBEITScan(m_parent));
4185
4186 m_diseqcBtn->setLabel(tr("DiSEqC (Switch, LNB and Rotor Configuration)"));
4187 m_diseqcBtn->setHelpText(tr("Input and satellite settings."));
4188
4189 cardType.addTargetedChild("DVB", m_tuningDelay);
4190 cardType.addTargetedChild("DVB", m_diseqcBtn);
4191 m_tuningDelay->setVisible(false);
4192
4193 connect(m_cardNum, qOverload<const QString&>(&StandardSetting::valueChanged),
4195 connect(m_cardNum, qOverload<const QString&>(&StandardSetting::valueChanged),
4197}
4198
4200{
4201 if (m_diseqcTree)
4202 {
4203 delete m_diseqcTree;
4204 m_diseqcTree = nullptr;
4205 }
4206}
4207
4209{
4211 m_diseqcBtn->Load();
4213 if (m_cardType->getValue() == "DVB-S" ||
4214 m_cardType->getValue() == "DVB-S2" ||
4216 {
4217 m_diseqcBtn->setVisible(true);
4218 }
4219}
4220
4222{
4226}
4227
4228// -----------------------
4229// SAT>IP configuration
4230// -----------------------
4231#if CONFIG_SATIP
4232
4233class DiSEqCPosition : public MythUISpinBoxSetting
4234{
4235 public:
4236 explicit DiSEqCPosition(const CaptureCard &parent, int value, int min_val) :
4237 MythUISpinBoxSetting(new CaptureCardDBStorage(this, parent, "dvb_diseqc_type"),
4238 min_val, 0xff, 1)
4239 {
4240 setLabel(QObject::tr("DiSEqC position"));
4241 setHelpText(QObject::tr("Position of the LNB on the DiSEqC switch. "
4242 "Leave at 1 if there is no DiSEqC switch "
4243 "and the LNB is directly connected to the SatIP server. "
4244 "This value is used as signal source (attribute src) in "
4245 "the SatIP tune command."));
4246 setValue(value);
4247 };
4248
4249 ~DiSEqCPosition() override
4250 {
4251 delete GetStorage();
4252 }
4253};
4254
4255SatIPConfigurationGroup::SatIPConfigurationGroup
4256 (CaptureCard& a_parent, CardType &a_cardtype) :
4257 m_parent(a_parent),
4258 m_deviceId(new SatIPDeviceID(a_parent))
4259{
4260 setVisible(false);
4261
4262 FillDeviceList();
4263
4264 m_friendlyName = new SatIPDeviceAttribute(tr("Friendly name"), tr("Friendly name of the Sat>IP server"));
4265 m_tunerType = new SatIPDeviceAttribute(tr("Tuner type"), tr("Type of the selected tuner"));
4266 m_tunerIndex = new SatIPDeviceAttribute(tr("Tuner index"), tr("Index of the tuner on the Sat>IP server"));
4267
4268 m_deviceIdList = new SatIPDeviceIDList(
4269 m_deviceId, m_friendlyName, m_tunerType, m_tunerIndex, &m_deviceList, m_parent);
4270
4271 a_cardtype.addTargetedChild("SATIP", m_deviceIdList);
4272 a_cardtype.addTargetedChild("SATIP", m_friendlyName);
4273 a_cardtype.addTargetedChild("SATIP", m_tunerType);
4274 a_cardtype.addTargetedChild("SATIP", m_tunerIndex);
4275 a_cardtype.addTargetedChild("SATIP", m_deviceId);
4276 a_cardtype.addTargetedChild("SATIP", new SignalTimeout(m_parent, 7s, 1s));
4277 a_cardtype.addTargetedChild("SATIP", new ChannelTimeout(m_parent, 10s, 2s));
4278 a_cardtype.addTargetedChild("SATIP", new DVBEITScan(m_parent));
4279 a_cardtype.addTargetedChild("SATIP", new DiSEqCPosition(m_parent, 1, 1));
4280
4281 connect(m_deviceIdList, &SatIPDeviceIDList::NewTuner,
4282 m_deviceId, &SatIPDeviceID::SetTuner);
4283};
4284
4285void SatIPConfigurationGroup::FillDeviceList(void)
4286{
4287 m_deviceList.clear();
4288
4289 // Find devices on the network
4290 // Returns each devices as "deviceid friendlyname ip tunerno tunertype"
4291 QStringList devs = CardUtil::ProbeVideoDevices("SATIP");
4292
4293 for (const auto & dev : std::as_const(devs))
4294 {
4295 QStringList devparts = dev.split(" ");
4296 const QString& id = devparts.value(0);
4297 const QString& name = devparts.value(1);
4298 const QString& ip = devparts.value(2);
4299 const QString& tunerno = devparts.value(3);
4300 const QString& tunertype = devparts.value(4);
4301
4302 SatIPDevice device;
4303 device.m_deviceId = id;
4304 device.m_cardIP = ip;
4305 device.m_inUse = false;
4306 device.m_friendlyName = name;
4307 device.m_tunerNo = tunerno;
4308 device.m_tunerType = tunertype;
4309 device.m_mythDeviceId = QString("%1:%2:%3").arg(id, tunertype, tunerno);
4310
4311 QString friendlyIdentifier = QString("%1, %2, Tuner #%3").arg(name, tunertype, tunerno);
4312
4313 m_deviceList[device.m_mythDeviceId] = device;
4314
4315 LOG(VB_CHANNEL, LOG_DEBUG, QString("SatIP: Add %1 '%2' '%3'")
4316 .arg(device.m_mythDeviceId, device.m_friendlyName, friendlyIdentifier));
4317 }
4318
4319 // Now find configured devices
4320 // Returns each devices as "deviceid friendlyname ip tunerno tunertype"
4321 QStringList db = CardUtil::GetVideoDevices("SATIP");
4322 for (const auto& dev : std::as_const(db))
4323 {
4324 auto dit = m_deviceList.find(dev);
4325 if (dit != m_deviceList.end())
4326 {
4327 (*dit).m_inUse = true;
4328 }
4329 }
4330};
4331
4332SatIPDeviceIDList::SatIPDeviceIDList(
4333 SatIPDeviceID *deviceId,
4334 SatIPDeviceAttribute *friendlyName,
4335 SatIPDeviceAttribute *tunerType,
4336 SatIPDeviceAttribute *tunerIndex,
4337 SatIPDeviceList *deviceList,
4338 const CaptureCard &parent) :
4339 m_deviceId(deviceId),
4340 m_friendlyName(friendlyName),
4341 m_tunerType(tunerType),
4342 m_tunerIndex(tunerIndex),
4343 m_deviceList(deviceList),
4344 m_parent(parent)
4345{
4346 setLabel(tr("Available devices"));
4347 setHelpText(tr("Device IP or ID, tuner number and tuner type of available Sat>IP device"));
4348
4349 connect(this, qOverload<const QString&>(&StandardSetting::valueChanged),
4350 this, &SatIPDeviceIDList::UpdateDevices);
4351};
4352
4353void SatIPDeviceIDList::Load(void)
4354{
4355 clearSelections();
4356
4357 int cardid = m_parent.getCardID();
4358 QString device = CardUtil::GetVideoDevice(cardid);
4359
4360 fillSelections(device);
4361};
4362
4363void SatIPDeviceIDList::UpdateDevices(const QString &v)
4364{
4365 SatIPDevice dev = (*m_deviceList)[v];
4366 m_deviceId->setValue(dev.m_mythDeviceId);
4367 m_friendlyName->setValue(dev.m_friendlyName);
4368 m_tunerType->setValue(dev.m_tunerType);
4369 m_tunerIndex->setValue(dev.m_tunerNo);
4370};
4371
4372void SatIPDeviceIDList::fillSelections(const QString &cur)
4373{
4374 clearSelections();
4375
4376 std::vector<QString> names;
4377 std::vector<QString> devs;
4378 QMap<QString, bool> in_use;
4379
4380 const QString& current = cur;
4381 QString sel;
4382
4383 SatIPDeviceList::iterator it = m_deviceList->begin();
4384 for(; it != m_deviceList->end(); ++it)
4385 {
4386 QString friendlyIdentifier = QString("%1, %2, Tuner #%3")
4387 .arg((*it).m_friendlyName, (*it).m_tunerType, (*it).m_tunerNo);
4388 names.push_back(friendlyIdentifier);
4389
4390 devs.push_back(it.key());
4391 in_use[it.key()] = (*it).m_inUse;
4392 }
4393
4394 for (const auto& it2s : devs)
4395 {
4396 sel = (current == it2s) ? it2s : sel;
4397 }
4398
4399 QString usestr = QString(" -- ");
4400 usestr += tr("Warning: already in use");
4401
4402 for (uint i = 0; i < devs.size(); ++i)
4403 {
4404 const QString& dev = devs[i];
4405 const QString& name = names[i];
4406 bool dev_in_use = (dev == sel) ? false : in_use[devs[i]];
4407 QString desc = name + (dev_in_use ? usestr : "");
4408 addSelection(desc, dev, dev == sel);
4409 }
4410};
4411
4412SatIPDeviceID::SatIPDeviceID(const CaptureCard &parent) :
4413 MythUITextEditSetting(new CaptureCardDBStorage(this, parent, "videodevice")),
4414 m_parent(parent)
4415{
4416 setLabel(tr("Device ID"));
4417 setHelpText(tr("Device ID of the Sat>IP tuner"));
4418 setEnabled(true);
4419 setReadOnly(true);
4420};
4421
4422SatIPDeviceID::~SatIPDeviceID()
4423{
4424 delete GetStorage();
4425}
4426
4427void SatIPDeviceID::Load(void)
4428{
4430};
4431
4432void SatIPDeviceID::SetTuner(const QString &tuner)
4433{
4434 setValue(tuner);
4435};
4436
4437SatIPDeviceAttribute::SatIPDeviceAttribute(const QString& label, const QString& helptext)
4438{
4439 setLabel(label);
4440 setHelpText(helptext);
4441};
4442#endif // CONFIG_SATIP
@ DVB_DEV_FRONTEND
Definition: cardutil.h:30
std::vector< ChannelInfo > ChannelInfoList
Definition: channelinfo.h:130
TransTextEditSetting * m_cardInfo
Definition: videosource.h:628
void probeCard(const QString &device)
ASIDevice * m_device
Definition: videosource.h:627
ASIConfigurationGroup(CaptureCard &parent, CardType &cardType)
CaptureCard & m_parent
Definition: videosource.h:626
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:251
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:849
void Load(void) override
void(CaptureCardEditor::*)(void) CCESlot
Definition: videosource.h:848
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:759
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:815
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:893
void SetSourceID(const QString &sourceid)
SourceID * m_sourceId
Definition: videosource.h:953
void CreateNewInputGroup()
void sourceFetch()
void Save(void) override
InputName * m_inputName
Definition: videosource.h:952
CardInput(const QString &cardtype, const QString &device, int cardid)
int getInputID(void) const
Definition: videosource.h:927
MythUISpinBoxSetting * m_instanceCount
Definition: videosource.h:960
QString getSourceName(void) const
void loadByID(int id)
MythUICheckBoxSetting * m_schedGroup
Definition: videosource.h:961
void loadByInput(int cardid, const QString &inputname)
~CardInput() override
StartingChannel * m_startChan
Definition: videosource.h:954
DiSEqCDevSettings * m_externalInputSettings
Definition: videosource.h:957
ButtonStandardSetting * m_srcFetch
Definition: videosource.h:956
ButtonStandardSetting * m_scan
Definition: videosource.h:955
InputGroup * m_inputGrp0
Definition: videosource.h:958
void channelScanner()
void CreateNewInputGroupSlot(const QString &name)
InputGroup * m_inputGrp1
Definition: videosource.h:959
static void fillSelections(MythUIComboBoxSetting *setting)
CardType(const CaptureCard &parent)
static int GetASIDeviceNumber(const QString &device, QString *error=nullptr)
Definition: cardutil.cpp:3254
static bool IsTunerSharingCapable(const QString &rawtype)
Definition: cardutil.h:177
static INPUT_TYPES toInputType(const QString &name)
Definition: cardutil.h:78
static void GetDeviceInputNames(const QString &device, const QString &inputtype, QStringList &inputs)
Definition: cardutil.cpp:2660
static bool IsUnscanable(const QString &rawtype)
Definition: cardutil.h:158
static uint CreateInputGroup(const QString &name)
Definition: cardutil.cpp:2027
static QString GetRawInputType(uint inputid)
Definition: cardutil.h:292
static QString GetAudioDevice(uint inputid)
Definition: cardutil.h:296
static QString ProbeDVBFrontendName(const QString &device)
Returns the input type from the video device.
Definition: cardutil.cpp:746
static QString ProbeDVBType(const QString &device)
Definition: cardutil.cpp:725
static bool InputSetMaxRecordings(uint parentid, uint max_recordings)
Definition: cardutil.cpp:1561
static QStringList ProbeDeliverySystems(const QString &device)
Definition: cardutil.cpp:634
static uint CreateDeviceInputGroup(uint inputid, const QString &type, const QString &host, const QString &device)
Definition: cardutil.cpp:2069
static QString GetStartChannel(uint inputid)
Definition: cardutil.cpp:1792
static QString GetDeviceLabel(const QString &inputtype, const QString &videodevice)
Definition: cardutil.cpp:2638
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:3185
static QStringList GetVideoDevices(const QString &rawtype, QString hostname=QString())
Returns the videodevices of the matching inputs, duplicates removed.
Definition: cardutil.cpp:398
static bool HasTuner(const QString &rawtype, const QString &device)
Definition: cardutil.cpp:217
static QString GetDeliverySystemFromDB(uint inputid)
Definition: cardutil.h:300
static bool UnlinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:2157
static bool LinkInputGroup(uint inputid, uint inputgroupid)
Definition: cardutil.cpp:2106
static QString GetVideoDevice(uint inputid)
Definition: cardutil.h:294
static bool IsInNeedOfExternalInputConf(uint inputid)
Definition: cardutil.cpp:2311
static void ClearVideoDeviceCache()
Definition: cardutil.cpp:443
static uint CloneCard(uint src_inputid, uint dst_inputid)
Definition: cardutil.cpp:1550
static bool IsV4L(const QString &rawtype)
Definition: cardutil.h:145
static std::vector< uint > GetChildInputIDs(uint inputid)
Definition: cardutil.cpp:1373
static QString GetDeviceName(dvb_dev_type_t type, const QString &device)
Definition: cardutil.cpp:2967
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:135
static bool DeleteInput(uint inputid)
Definition: cardutil.cpp:2823
static QString ProbeDefaultDeliverySystem(const QString &device)
Definition: cardutil.cpp:709
static QStringList ProbeVideoDevices(const QString &rawtype)
Definition: cardutil.cpp:449
static bool IsCableCardPresent(uint inputid, const QString &inputType)
Definition: cardutil.cpp:109
static bool GetV4LInfo(int videofd, QString &input, QString &driver, uint32_t &version, uint32_t &capabilities)
Definition: cardutil.cpp:2355
static bool DeleteAllInputs(void)
Definition: cardutil.cpp:2893
static QStringList ProbeAudioInputs(const QString &device, const QString &inputtype=QString())
Definition: cardutil.cpp:2542
static uint GetDeviceInputGroup(uint inputid)
Definition: cardutil.cpp:2082
ChannelTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::duration< T > min_secs)
ChannelTimeout(const CaptureCard &parent, std::chrono::duration< T > value, std::chrono::duration< T > min_secs)
ChannelTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::milliseconds min_val)
static void SortChannels(ChannelInfoList &list, const QString &order, bool eliminate_duplicates=false)
static ChannelInfoList GetAllChannels(uint sourceid)
Returns channels that are not connected to an input and channels that are not marked as visible.
Definition: channelutil.h:264
~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:712
DVBCardType * m_cardType
Definition: videosource.h:708
DVBCardName * m_cardName
Definition: videosource.h:707
CaptureCard & m_parent
Definition: videosource.h:704
DVBConfigurationGroup(CaptureCard &a_parent, CardType &cardType)
~DVBConfigurationGroup() override
SignalTimeout * m_signalTimeout
Definition: videosource.h:709
ChannelTimeout * m_channelTimeout
Definition: videosource.h:710
DeviceTree * m_diseqcBtn
Definition: videosource.h:713
void Load(void) override
void Save(void) override
void probeCard(const QString &videodevice)
DVBCardNum * m_cardNum
Definition: videosource.h:706
DVBTuningDelay * m_tuningDelay
Definition: videosource.h:711
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:659
GroupSetting * m_size
Definition: videosource.h:660
DemoConfigurationGroup(CaptureCard &parent, CardType &cardtype)
void probeCard(const QString &device)
CaptureCard & m_parent
Definition: videosource.h:658
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:162
bool Load(uint card_input_id)
Loads configuration chain from DB for specified card input id.
Definition: diseqc.cpp:128
DVB-S device tree class.
Definition: diseqc.h:75
bool Load(const QString &device)
Loads the device tree from the database.
Definition: diseqc.cpp:311
bool Store(uint cardid, const QString &device="")
Stores the device tree to the database.
Definition: diseqc.cpp:422
static bool Exists(int cardid)
Check if a Diseqc device tree exists.
Definition: diseqc.cpp:393
static void InvalidateTrees(void)
Invalidate cached trees.
Definition: diseqc.cpp:245
~DishNetEIT() override
DishNetEIT(const CardInput &parent)
UseEIT * m_useEit
Definition: videosource.h:148
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:743
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:728
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:589
GroupSetting * m_cardInfo
Definition: videosource.h:590
TunerCardAudioInput * m_audioInput
Definition: videosource.h:591
HDPVRConfigurationGroup(CaptureCard &parent, CardType &cardtype)
static QString TestMimeType(const QString &sFileName)
IPTVHost(const CaptureCard &parent)
void setValue(int value) override
Definition: videosource.h:172
GroupSetting * m_info
Definition: videosource.h:643
CaptureCard & m_parent
Definition: videosource.h:642
ImportConfigurationGroup(CaptureCard &parent, CardType &cardtype)
void probeCard(const QString &device)
GroupSetting * m_size
Definition: videosource.h:644
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:574
void probeCard(const QString &device)
MPEGConfigurationGroup(CaptureCard &parent, CardType &cardtype)
GroupSetting * m_cardInfo
Definition: videosource.h:575
CaptureCard & m_parent
Definition: videosource.h:572
VideoDevice * m_device
Definition: videosource.h:573
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
QString GetHostName(void)
QString GetSetting(const QString &key, const QString &defaultval="")
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
MythScreenStack * GetMainStack()
MythScreenStack * GetStack(const QString &Stackname)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Screen in which all other widgets are contained and rendered.
virtual bool Create(void)
uint Wait(std::chrono::seconds timeout=0s)
void Run(std::chrono::seconds timeout=0s)
Runs a command inside the /bin/sh shell. Returns immediately.
QByteArray & ReadAll()
Dialog prompting the user to enter a text string.
void haveResult(QString)
void setValue(const QString &newValue) override
void addSelection(const QString &label, QString value=QString(), bool select=false)
QString getValueLabel(void) const
void fillSelectionsFromDir(const QDir &dir, bool absPath=true)
void setValue(int value) override
int getValueIndex(const QString &value) const
UseEIT * m_useEit
Definition: videosource.h:160
void Save(void) override
NoGrabber_config(const VideoSource &_parent)
~PresetTuner() override
PresetTuner(const CardInput &parent)
QuickTune(const CardInput &parent)
~RegionID() override
RegionID(const VideoSource &parent, signed int value, signed int min_val)
~ScanFrequencyStart() override
ScanFrequencyStart(const VideoSource &parent)
SchedGroupFalse(const CaptureCard &parent)
~SchedGroupFalse() override
SchedGroup(const CardInput &parent)
~SchedGroup() override
~ScheduleOrder() override
ScheduleOrder(const CardInput &parent, int _value)
SignalTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::milliseconds min_val)
SignalTimeout(const CaptureCard &parent, std::chrono::duration< T > value, std::chrono::duration< T > min_secs)
SignalTimeout(const CaptureCard &parent, std::chrono::milliseconds value, std::chrono::duration< T > min_secs)
SkipBtAudio(const CaptureCard &parent)
~SkipBtAudio() override
void fillSelections()
void Load(void) override
SourceID(const CardInput &parent)
static bool GetListingsLoginData(uint sourceid, QString &grabber, QString &userid, QString &passwd, QString &lineupid)
Definition: sourceutil.cpp:172
static bool DeleteSource(uint sourceid)
Definition: sourceutil.cpp:530
static bool DeleteAllSources(void)
Definition: sourceutil.cpp:586
static uint GetChannelCount(uint sourceid)
Definition: sourceutil.cpp:136
static bool UpdateChannelsFromListings(uint sourceid, const QString &inputtype=QString(), bool wait=false)
Definition: sourceutil.cpp:396
virtual void addChild(StandardSetting *child)
virtual void Save(void)
StandardSetting * m_parent
virtual void Load(void)
bool isEnabled() const
virtual void setReadOnly(bool readonly)
void addTargetedChild(const QString &value, StandardSetting *setting)
virtual void clearSettings()
void settingsChanged(StandardSetting *selectedSetting=nullptr)
virtual void setHelpText(const QString &str)
StandardSetting * getParent() const
void setVisible(bool visible)
virtual QList< StandardSetting * > * getSubSettings()
virtual void setValue(const QString &newValue)
Storage * GetStorage(void) const
void valueChanged(const QString &newValue)
virtual QString getValue(void) const
virtual void setEnabled(bool enabled)
virtual void setLabel(QString str)
void SetSourceID(const QString &sourceid)
virtual QString GetDBValue(void) const =0
virtual void Load(void)=0
void SetSourceID(uint sourceid)
void Save(void) override
void Load(void) override
TransFreqTableSelector(uint sourceid)
int fillSelections(const QString &device)
TunerCardAudioInput(const CaptureCard &parent, QString dev=QString(), QString type=QString())
UseEIT(const VideoSource &parent)
~UseEIT() override
VideoDevice * m_device
Definition: videosource.h:607
CaptureCard & m_parent
Definition: videosource.h:605
GroupSetting * m_cardInfo
Definition: videosource.h:606
void probeCard(const QString &device)
QString m_driverName
Definition: videosource.h:609
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:555
GroupSetting * m_cardInfo
Definition: videosource.h:554
CaptureCard & m_parent
Definition: videosource.h:553
VBIDevice(const CaptureCard &parent)
uint fillSelectionsFromDir(const QDir &dir, const QString &card, const QString &driver)
uint setFilter(const QString &card, const QString &driver)
void fillSelectionsFromDir(const QDir &dir, bool absPath=true)
VBoxDeviceList m_deviceList
Definition: videosource.h:539
StandardSetting * m_desc
Definition: videosource.h:534
VBoxDeviceID * m_deviceId
Definition: videosource.h:536
VBoxDeviceIDList * m_deviceIdList
Definition: videosource.h:535
VBoxTunerIndex * m_cardTuner
Definition: videosource.h:538
VBoxConfigurationGroup(CaptureCard &parent, CardType &cardtype)
CaptureCard & m_parent
Definition: videosource.h:533
void fillSelections(const QString &current)
Adds all available device-tuner combinations to list.
VBoxDeviceID * m_deviceId
Definition: videosource.h:1030
const CaptureCard & m_parent
Definition: videosource.h:1035
VBoxDeviceList * m_deviceList
Definition: videosource.h:1034
StandardSetting * m_desc
Definition: videosource.h:1031
VBoxTunerIndex * m_cardTuner
Definition: videosource.h:1033
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:1057
QString m_ip
Definition: videosource.h:1056
QString m_overrideDeviceId
Definition: videosource.h:1058
bool m_inUse
Definition: videosource.h:512
QString m_cardIp
Definition: videosource.h:509
QString m_deviceId
Definition: videosource.h:507
QString m_mythDeviceId
Definition: videosource.h:506
QString m_tunerNo
Definition: videosource.h:510
QString m_tunerType
Definition: videosource.h:511
QString m_desc
Definition: videosource.h:508
bool m_discovered
Definition: videosource.h:513
void NewIP(const QString &)
void SetOldValue(const QString &s)
Definition: videosource.h:976
void setEnabled(bool e) override
void UpdateDevices(const QString &v)
QString m_oldValue
Definition: videosource.h:986
QString m_oldValue
Definition: videosource.h:1007
void UpdateDevices(const QString &v)
void setEnabled(bool e) override
void NewTuner(const QString &)
void SetOldValue(const QString &s)
Definition: videosource.h:997
void fillSelectionsFromDir(const QDir &dir, bool absPath=true)
QString Card(void) const
QString m_driverName
VideoDevice(const CaptureCard &parent, uint minor_min=0, uint minor_max=UINT_MAX, const QString &card=QString(), const QRegularExpression &driver=QRegularExpression())
QString Driver(void) const
QString m_cardName
uint fillSelectionsFromDir(const QDir &dir, uint minor_min, uint minor_max, const QString &card, const QRegularExpression &driver, bool allow_duplicates)
QMap< uint, uint > m_minorList
QString GetWhereClause(MSqlBindings &bindings) const override
const VideoSource & m_parent
Definition: videosource.h:54
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:872
void ShowDeleteAllSourcesDialog(void) const
void(VideoSourceEditor::*)(void) const VSESlotConst
Definition: videosource.h:873
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:64
void Load(void) override
Definition: videosource.cpp:80
void Load(void) override
VideoSourceShow(uint _initial_sourceid)
uint m_initialSourceId
Definition: videosource.h:88
static void fillSelections(GroupSetting *setting)
int getSourceID(void) const
Definition: videosource.h:183
Name * m_name
Definition: videosource.h:232
QString getSourceName(void) const
Definition: videosource.h:191
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:136
const VideoSource & m_parent
Definition: videosource.h:134
void Save(void) override
XMLTV_generic_config(const VideoSource &_parent, const QString &_grabber, StandardSetting *_setting)
unsigned int uint
Definition: compat.h:68
#define close
Definition: compat.h:30
#define lstat
Definition: compat.h:111
#define minor(X)
Definition: compat.h:65
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
static guint32 * tmp
Definition: goom_core.cpp:26
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QMap< QString, QVariant > MSqlBindings
typedef for a map of string -> string bindings for generic queries.
Definition: mythdbcon.h:100
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
QString GetConfDir(void)
Definition: mythdirs.cpp:263
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
@ kMSStdOut
allow access to stdout
Definition: mythsystem.h:41
@ kMSRunShell
run process through shell
Definition: mythsystem.h:43
@ kMSDontDisableDrawing
avoid disabling UI drawing
Definition: mythsystem.h:37
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
dictionary info
Definition: azlyrics.py:7
def error(message)
Definition: smolt.py:409
STL namespace.
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:95
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:89
static void IPTVConfigurationGroup(CaptureCard &parent, CardType &cardType)
QMap< QString, VBoxDevice > VBoxDeviceList
Definition: videosource.h:516
static bool is_grabber_external(const QString &grabber)
Definition: videosource.h:29