MythTV master
recordingprofile.cpp
Go to the documentation of this file.
1
2#include "recordingprofile.h"
3
4#include "cardutil.h"
5#include "libmythbase/mythconfig.h"
9#include "v4l2util.h"
10#include <algorithm>
11#include <utility>
12
14{
15 QString idTag(":WHEREID");
16 QString query("id = " + idTag);
17
18 bindings.insert(idTag, m_parent.getProfileNum());
19
20 return query;
21}
22
24{
25 protected:
27 const RecordingProfile &parentProfile,
28 const QString& name) :
29 SimpleDBStorage(_setting, "codecparams", "value"),
30 m_parent(parentProfile), m_codecName(name)
31 {
32 _setting->setName(name);
33 }
34
35 QString GetSetClause(MSqlBindings &bindings) const override; // SimpleDBStorage
36 QString GetWhereClause(MSqlBindings &bindings) const override; // SimpleDBStorage
37
39 QString m_codecName;
40};
41
43{
44 QString profileTag(":SETPROFILE");
45 QString nameTag(":SETNAME");
46 QString valueTag(":SETVALUE");
47
48 QString query("profile = " + profileTag + ", name = " + nameTag
49 + ", value = " + valueTag);
50
51 bindings.insert(profileTag, m_parent.getProfileNum());
52 bindings.insert(nameTag, m_codecName);
53 bindings.insert(valueTag, m_user->GetDBValue());
54
55 return query;
56}
57
59{
60 QString profileTag(":WHEREPROFILE");
61 QString nameTag(":WHERENAME");
62
63 QString query("profile = " + profileTag + " AND name = " + nameTag);
64
65 bindings.insert(profileTag, m_parent.getProfileNum());
66 bindings.insert(nameTag, m_codecName);
67
68 return query;
69}
70
72{
73 public:
74 explicit AudioCodecName(const RecordingProfile &parent) :
76 new RecordingProfileStorage(this, parent, "audiocodec"))
77 {
78 setLabel(QObject::tr("Codec"));
79 setName("audiocodec");
80 }
81};
82
84{
85 public:
86 explicit MP3Quality(const RecordingProfile &parent) :
87 MythUISpinBoxSetting(this, 1, 9, 1),
88 CodecParamStorage(this, parent, "mp3quality")
89 {
90 setLabel(QObject::tr("MP3 quality"));
91 setValue(7);
92 setHelpText(QObject::tr("The higher the slider number, the lower the "
93 "quality of the audio. Better quality audio (lower "
94 "numbers) requires more CPU."));
95 };
96};
97
99{
100 public:
101 explicit BTTVVolume(const RecordingProfile& parent) :
102 MythUISpinBoxSetting(this, 0, 100, 1),
103 CodecParamStorage(this, parent, "volume")
104 {
105 setLabel(QObject::tr("Volume (%)"));
106 setValue(90);
107 setHelpText(QObject::tr("Recording volume of the capture card."));
108 };
109};
110
112{
113 public:
114 explicit SampleRate(const RecordingProfile &parent, bool analog = true) :
115 MythUIComboBoxSetting(this), CodecParamStorage(this, parent, "samplerate")
116 {
117 setLabel(QObject::tr("Sampling rate"));
118 setHelpText(QObject::tr("Sets the audio sampling rate for your DSP. "
119 "Ensure that you choose a sampling rate appropriate "
120 "for your device. btaudio may only allow 32000."));
121
122 m_rates.push_back(32000);
123 m_rates.push_back(44100);
124 m_rates.push_back(48000);
125
126 m_allowedRate[48000] = true;
127 for (uint i = 0; analog && (i < m_rates.size()); i++)
128 m_allowedRate[m_rates[i]] = true;
129
130 };
131
132 void Load(void) override // StandardSetting
133 {
135 QString val = getValue();
136
138 for (uint rate : m_rates)
139 {
140 if (m_allowedRate[rate])
141 addSelection(QString::number(rate));
142 }
143
144 int which = getValueIndex(val);
145 setValue(std::max(which,0));
146
147 if (m_allowedRate.size() <= 1)
148 setEnabled(false);
149 }
150
151 void addSelection(const QString &label,
152 const QString value = QString(), // clazy:exclude=function-args-by-ref
153 bool select = false) override
154 {
155 QString val = value.isEmpty() ? label : value;
156 uint rate = val.toUInt();
157 if (m_allowedRate[rate])
158 {
159 MythUIComboBoxSetting::addSelection(label, value, select);
160 }
161 else
162 {
163 LOG(VB_GENERAL, LOG_ERR, QString("SampleRate: ") +
164 QString("Attempted to add a rate %1 Hz, which is "
165 "not in the list of allowed rates.").arg(rate));
166 }
167 }
168
169 std::vector<uint> m_rates;
170 QMap<uint,bool> m_allowedRate;
171};
172
174{
175 public:
177 bool layer1, bool layer2, bool layer3) :
178 MythUIComboBoxSetting(this), CodecParamStorage(this, parent, "mpeg2audtype"),
179 m_allowLayer1(layer1), m_allowLayer2(layer2), m_allowLayer3(layer3)
180 {
181 setLabel(QObject::tr("Type"));
182
183 if (m_allowLayer1)
184 addSelection("Layer I");
185 if (m_allowLayer2)
186 addSelection("Layer II");
187 if (m_allowLayer3)
188 addSelection("Layer III");
189
190 uint allowed_cnt = 0;
191 allowed_cnt += ((m_allowLayer1) ? 1 : 0);
192 allowed_cnt += ((m_allowLayer2) ? 1 : 0);
193 allowed_cnt += ((m_allowLayer3) ? 1 : 0);
194
195 if (1 == allowed_cnt)
196 setEnabled(false);
197
198 setHelpText(QObject::tr("Sets the audio type"));
199 }
200
201 void Load(void) override // StandardSetting
202 {
204 QString val = getValue();
205
206 if ((val == "Layer I") && !m_allowLayer1)
207 {
208 if (m_allowLayer2)
209 val = "Layer II";
210 else if (m_allowLayer3)
211 val = "Layer III";
212 }
213
214 if ((val == "Layer II") && !m_allowLayer2)
215 {
216 if (m_allowLayer3)
217 val = "Layer III";
218 else if (m_allowLayer1)
219 val = "Layer I";
220 }
221
222 if ((val == "Layer III") && !m_allowLayer3)
223 {
224 if (m_allowLayer2)
225 val = "Layer II";
226 else if (m_allowLayer1)
227 val = "Layer I";
228 }
229
230 if (getValue() != val)
231 {
232 int which = getValueIndex(val);
233 if (which >= 0)
234 setValue(which);
235 }
236 }
237
238 private:
242};
243
245{
246 public:
247 explicit MPEG2audBitrateL1(const RecordingProfile &parent) :
249 CodecParamStorage(this, parent, "mpeg2audbitratel1")
250 {
251 setLabel(QObject::tr("Bitrate"));
252
253 addSelection("32 kbps", "32");
254 addSelection("64 kbps", "64");
255 addSelection("96 kbps", "96");
256 addSelection("128 kbps", "128");
257 addSelection("160 kbps", "160");
258 addSelection("192 kbps", "192");
259 addSelection("224 kbps", "224");
260 addSelection("256 kbps", "256");
261 addSelection("288 kbps", "288");
262 addSelection("320 kbps", "320");
263 addSelection("352 kbps", "352");
264 addSelection("384 kbps", "384");
265 addSelection("416 kbps", "416");
266 addSelection("448 kbps", "448");
267 setValue(13);
268 setHelpText(QObject::tr("Sets the audio bitrate"));
269 };
270};
271
273{
274 public:
275 explicit MPEG2audBitrateL2(const RecordingProfile &parent) :
277 CodecParamStorage(this, parent, "mpeg2audbitratel2")
278 {
279 setLabel(QObject::tr("Bitrate"));
280
281 addSelection("32 kbps", "32");
282 addSelection("48 kbps", "48");
283 addSelection("56 kbps", "56");
284 addSelection("64 kbps", "64");
285 addSelection("80 kbps", "80");
286 addSelection("96 kbps", "96");
287 addSelection("112 kbps", "112");
288 addSelection("128 kbps", "128");
289 addSelection("160 kbps", "160");
290 addSelection("192 kbps", "192");
291 addSelection("224 kbps", "224");
292 addSelection("256 kbps", "256");
293 addSelection("320 kbps", "320");
294 addSelection("384 kbps", "384");
295 setValue(13);
296 setHelpText(QObject::tr("Sets the audio bitrate"));
297 };
298};
299
301{
302 public:
303 explicit MPEG2audBitrateL3(const RecordingProfile &parent) :
305 CodecParamStorage(this, parent, "mpeg2audbitratel3")
306 {
307 setLabel(QObject::tr("Bitrate"));
308
309 addSelection("32 kbps", "32");
310 addSelection("40 kbps", "40");
311 addSelection("48 kbps", "48");
312 addSelection("56 kbps", "56");
313 addSelection("64 kbps", "64");
314 addSelection("80 kbps", "80");
315 addSelection("96 kbps", "96");
316 addSelection("112 kbps", "112");
317 addSelection("128 kbps", "128");
318 addSelection("160 kbps", "160");
319 addSelection("192 kbps", "192");
320 addSelection("224 kbps", "224");
321 addSelection("256 kbps", "256");
322 addSelection("320 kbps", "320");
323 setValue(10);
324 setHelpText(QObject::tr("Sets the audio bitrate"));
325 };
326};
327
329{
330 public:
331 explicit MPEG2audVolume(const RecordingProfile &parent) :
332 MythUISpinBoxSetting(this, 0, 100, 1),
333 CodecParamStorage(this, parent, "mpeg2audvolume")
334 {
335
336 setLabel(QObject::tr("Volume (%)"));
337 setValue(90);
338 setHelpText(QObject::tr("Volume of the recording "));
339 };
340};
341
343{
344 public:
346 bool layer1, bool layer2, bool layer3,
347 uint default_layer)
348 {
349 const std::array<const QString,3> layers { "Layer I", "Layer II", "Layer III", };
350
351 setLabel(QObject::tr("Bitrate Settings"));
352
353 auto *audType = new MPEG2audType(parent, layer1, layer2, layer3);
354
355 addChild(audType);
356
357 addTargetedChild(layers[0], new MPEG2audBitrateL1(parent));
358 addTargetedChild(layers[1], new MPEG2audBitrateL2(parent));
359 addTargetedChild(layers[2], new MPEG2audBitrateL3(parent));
360
361 uint desired_layer = std::clamp(default_layer, 1U, 3U) - 1;
362 int which = audType->getValueIndex(layers[desired_layer]);
363 if (which >= 0)
364 audType->setValue(which);
365 };
366};
367
369{
370 public:
371 explicit MPEG2Language(const RecordingProfile &parent) :
372 MythUIComboBoxSetting(this), CodecParamStorage(this, parent, "mpeg2language")
373 {
374 setLabel(QObject::tr("SAP/Bilingual"));
375
376 addSelection(QObject::tr("Main Language"), "0");
377 addSelection(QObject::tr("SAP Language"), "1");
378 addSelection(QObject::tr("Dual"), "2");
379
380 setValue(0);
381 setHelpText(QObject::tr(
382 "Chooses the language(s) to record when "
383 "two languages are broadcast. Only Layer II "
384 "supports the recording of two languages (Dual)."
385 "Requires ivtv 0.4.0 or later."));
386 };
387};
388
390{
391 public:
392 explicit BitrateMode(const RecordingProfile& parent,
393 const QString& setting = "mpeg2bitratemode") :
395 CodecParamStorage(this, parent, setting)
396 {
397 setLabel(QObject::tr("Bitrate Mode"));
398
399 addSelection("Variable Bitrate", "0");
400 addSelection("Constant Bitrate", "1");
401 setValue(0);
402 setHelpText(QObject::tr("Bitrate mode"));
403 }
404};
405
407{
408 public:
410 [[maybe_unused]] V4L2util* v4l2) :
411 m_parent(parentProfile),
413 {
414 setName(QObject::tr("Audio Quality"));
415
417
418 QString label("MP3");
422
423 label = "MPEG-2 Hardware Encoder";
424 m_codecName->addTargetedChild(label, new SampleRate(m_parent, false));
427 (m_parent, false, true, false, 2));
430
431 label = "Uncompressed";
434
435 m_codecName->addTargetedChild("AC3 Hardware Encoder",
436 new GroupSetting());
437
438 m_codecName->addTargetedChild("AAC Hardware Encoder",
439 new GroupSetting());
440
441#if CONFIG_V4L2
442 if (v4l2)
443 {
444 // Dynamically create user options based on the
445 // capabilties the driver reports.
446
448
449 if (v4l2->GetOptions(options))
450 {
451 /* StandardSetting cannot handle multiple 'targets' pointing
452 * to the same setting configuration, so we need to do
453 * this in two passes. */
454
455 for (const auto & option : std::as_const(options))
456 {
457 if (option.m_category == DriverOption::AUDIO_ENCODING)
458 {
459 for (const auto & Imenu : std::as_const(option.m_menu))
460 {
461 if (!Imenu.isEmpty())
462 m_v4l2codecs << "V4L2:" + Imenu;
463 }
464 }
465 }
466
467 for (auto Icodec = m_v4l2codecs.begin(); Icodec < m_v4l2codecs.end(); ++Icodec)
468 {
469 for (const auto & option : std::as_const(options))
470 {
471 if (option.m_category == DriverOption::AUDIO_BITRATE_MODE)
472 {
474 new BitrateMode(m_parent, "audbitratemode"));
475 }
476 else if (option.m_category ==
478 {
480 new SampleRate(m_parent, false));
481 }
482 else if (option.m_category ==
484 {
487 }
488 else if (option.m_category == DriverOption::AUDIO_BITRATE)
489 {
490 bool layer1 = false;
491 bool layer2 = false;
492 bool layer3 = false;
493
494 for (const auto & Imenu : std::as_const(option.m_menu))
495 {
496 if (Imenu.indexOf("Layer III") >= 0)
497 layer3 = true;
498 else if (Imenu.indexOf("Layer II") >= 0)
499 layer2 = true;
500 else if (Imenu.indexOf("Layer I") >= 0)
501 layer1 = true;
502 }
503
504 if (layer1 || layer2 || layer3)
505 {
508 layer1,
509 layer2,
510 layer3, 2));
511 }
512 }
513 else if (option.m_category == DriverOption::VOLUME)
514 {
517 }
518 }
519 }
520 }
521 }
522#endif // CONFIG_V4L2
523 }
524
525 void selectCodecs(const QString & groupType)
526 {
527 if (!groupType.isNull())
528 {
529 if (groupType == "MPEG")
530 {
531 m_codecName->addSelection("MPEG-2 Hardware Encoder");
532 }
533 else if (groupType == "HDPVR")
534 {
535 m_codecName->addSelection("AC3 Hardware Encoder");
536 m_codecName->addSelection("AAC Hardware Encoder");
537 }
538 else if (groupType.startsWith("V4L2:"))
539 {
540 for (const auto & codec : std::as_const(m_v4l2codecs))
542 }
543 else
544 {
545 // V4L, TRANSCODE (and any undefined types)
547 m_codecName->addSelection("Uncompressed");
548 }
549 }
550 else
551 {
553 m_codecName->addSelection("Uncompressed");
554 m_codecName->addSelection("MPEG-2 Hardware Encoder");
555 }
556 }
557private:
560 QStringList m_v4l2codecs;
561};
562
564{
565 public:
566 explicit VideoCodecName(const RecordingProfile &parent) :
568 new RecordingProfileStorage(this, parent, "videocodec"))
569 {
570 setLabel(QObject::tr("Codec"));
571 setName("videocodec");
572 }
573};
574
576{
577 public:
578 explicit RTjpegQuality(const RecordingProfile &parent) :
579 MythUISpinBoxSetting(this, 1, 255, 1),
580 CodecParamStorage(this, parent, "rtjpegquality")
581 {
582 setLabel(QObject::tr("RTjpeg Quality"));
583 setValue(170);
584 setHelpText(QObject::tr("Higher is better quality."));
585 };
586};
587
589{
590 public:
591 explicit RTjpegLumaFilter(const RecordingProfile &parent) :
592 MythUISpinBoxSetting(this, 0, 31, 1),
593 CodecParamStorage(this, parent, "rtjpeglumafilter")
594 {
595 setLabel(QObject::tr("Luma filter"));
596 setValue(0);
597 setHelpText(QObject::tr("Lower is better."));
598 };
599};
600
602{
603 public:
604 explicit RTjpegChromaFilter(const RecordingProfile &parent) :
605 MythUISpinBoxSetting(this, 0, 31, 1),
606 CodecParamStorage(this, parent, "rtjpegchromafilter")
607 {
608 setLabel(QObject::tr("Chroma filter"));
609 setValue(0);
610 setHelpText(QObject::tr("Lower is better."));
611 };
612};
613
615{
616 public:
617 explicit MPEG4bitrate(const RecordingProfile &parent) :
618 MythUISpinBoxSetting(this, 100, 8000, 100),
619 CodecParamStorage(this, parent, "mpeg4bitrate")
620 {
621 setLabel(QObject::tr("Bitrate (kb/s)"));
622 setValue(2200);
623 setHelpText(QObject::tr("Bitrate in kilobits/second. As a guide, "
624 "2200 kb/s is approximately 1 GB/hr."));
625 };
626};
627
629{
630 public:
631 explicit ScaleBitrate(const RecordingProfile &parent) :
633 CodecParamStorage(this, parent, "scalebitrate")
634 {
635 setLabel(QObject::tr("Scale bitrate for frame size"));
636 setValue(true);
637 setHelpText(QObject::tr("If set, the bitrate specified will be used "
638 "for 640x480. If other resolutions are used, the "
639 "bitrate will be scaled appropriately."));
640 };
641};
642
644{
645 public:
646 explicit MPEG4MinQuality(const RecordingProfile &parent) :
647 MythUISpinBoxSetting(this, 1, 31, 1),
648 CodecParamStorage(this, parent, "mpeg4minquality")
649 {
650 setLabel(QObject::tr("Minimum quality"));
651 setValue(15);
652 setHelpText(QObject::tr("Modifying the default may have severe "
653 "consequences."));
654 };
655};
656
658{
659 public:
660 explicit MPEG4MaxQuality(const RecordingProfile &parent) :
661 MythUISpinBoxSetting(this, 1, 31, 1),
662 CodecParamStorage(this, parent, "mpeg4maxquality")
663 {
664 setLabel(QObject::tr("Maximum quality"));
665 setValue(2);
666 setHelpText(QObject::tr("Modifying the default may have severe "
667 "consequences."));
668 };
669};
670
672{
673 public:
674 explicit MPEG4QualDiff(const RecordingProfile &parent) :
675 MythUISpinBoxSetting(this, 1, 31, 1),
676 CodecParamStorage(this, parent, "mpeg4qualdiff")
677 {
678
679 setLabel(QObject::tr("Max quality difference between frames"));
680 setValue(3);
681 setHelpText(QObject::tr("Modifying the default may have severe "
682 "consequences."));
683 };
684};
685
687{
688 public:
689 explicit MPEG4OptionIDCT(const RecordingProfile &parent) :
691 CodecParamStorage(this, parent, "mpeg4optionidct")
692 {
693 setLabel(QObject::tr("Enable interlaced DCT encoding"));
694 setValue(false);
695 setHelpText(QObject::tr("If set, the MPEG4 encoder will use "
696 "interlaced DCT encoding. You may want this when encoding "
697 "interlaced video; however, this is experimental and may "
698 "cause damaged video."));
699 };
700};
701
703{
704 public:
705 explicit MPEG4OptionIME(const RecordingProfile &parent) :
707 CodecParamStorage(this, parent, "mpeg4optionime")
708 {
709 setLabel(QObject::tr("Enable interlaced motion estimation"));
710 setValue(false);
711 setHelpText(QObject::tr("If set, the MPEG4 encoder will use "
712 "interlaced motion estimation. You may want this when "
713 "encoding interlaced video; however, this is experimental "
714 "and may cause damaged video."));
715 };
716};
717
719{
720 public:
721 explicit MPEG4OptionVHQ(const RecordingProfile &parent) :
723 CodecParamStorage(this, parent, "mpeg4optionvhq")
724 {
725 setLabel(QObject::tr("Enable high-quality encoding"));
726 setValue(false);
727 setHelpText(QObject::tr("If set, the MPEG4 encoder will use "
728 "'high-quality' encoding options. This requires much "
729 "more processing, but can result in better video."));
730 };
731};
732
734{
735 public:
736 explicit MPEG4Option4MV(const RecordingProfile &parent) :
738 CodecParamStorage(this, parent, "mpeg4option4mv")
739 {
740 setLabel(QObject::tr("Enable 4MV encoding"));
741 setValue(false);
742 setHelpText(QObject::tr("If set, the MPEG4 encoder will use '4MV' "
743 "motion-vector encoding. This requires "
744 "much more processing, but can result in better "
745 "video. It is highly recommended that the HQ option is "
746 "enabled if 4MV is enabled."));
747 };
748};
749
751{
752 public:
753 explicit EncodingThreadCount(const RecordingProfile &parent) :
754 MythUISpinBoxSetting(this, 1, 8, 1),
755 CodecParamStorage(this, parent, "encodingthreadcount")
756 {
757
758 setLabel(QObject::tr("Number of threads"));
759 setValue(1);
761 QObject::tr("Threads to use for software encoding.") + " " +
762 QObject::tr("Set to a value less than or equal to the "
763 "number of processors on the backend that "
764 "will be doing the encoding."));
765 };
766};
767
769{
770 public:
771 explicit AverageBitrate(const RecordingProfile &parent,
772 const QString& setting = "mpeg2bitrate",
773 uint min_br = 1000, uint max_br = 16000,
774 uint default_br = 4500, uint increment = 100,
775 QString label = QString()) :
776 MythUISpinBoxSetting(this, min_br, max_br, increment),
777 CodecParamStorage(this, parent, setting)
778 {
779 if (label.isEmpty())
780 label = QObject::tr("Avg. Bitrate (kb/s)");
781 setLabel(label);
782 setValue(default_br);
783 setHelpText(QObject::tr(
784 "Average bitrate in kilobits/second. As a guide, "
785 "2200 kb/s is approximately 1 GB/hour."));
786 };
787};
788
790{
791 public:
792 explicit PeakBitrate(const RecordingProfile &parent,
793 const QString& setting = "mpeg2maxbitrate",
794 uint min_br = 1000, uint max_br = 16000,
795 uint default_br = 6000, uint increment = 100,
796 QString label = QString()) :
797 MythUISpinBoxSetting(this, min_br, max_br, increment),
798 CodecParamStorage(this, parent, setting)
799 {
800 if (label.isEmpty())
801 label = QObject::tr("Max. Bitrate (kb/s)");
802 setLabel(label);
803 setValue(default_br);
804 setHelpText(QObject::tr("Maximum bitrate in kilobits/second. "
805 "As a guide, 2200 kb/s is approximately 1 GB/hour."));
806 };
807};
808
810{
811 public:
812 explicit MPEG2streamType(const RecordingProfile &parent,
813 uint minopt = 0, uint maxopt = 8, uint defopt = 0) :
815 CodecParamStorage(this, parent, "mpeg2streamtype")
816 {
817 maxopt = std::min<uint>(maxopt, 8);
818
819 setLabel(QObject::tr("Stream Type"));
820
821 const std::array<const QString,9> options { "MPEG-2 PS", "MPEG-2 TS",
822 "MPEG-1 VCD", "PES AV",
823 "PES V", "PES A",
824 "DVD", "DVD-Special 1", "DVD-Special 2" };
825
826 for (uint idx = minopt; idx <= maxopt; ++idx)
827 addSelection(options[idx]);
828
829 setValue(defopt - minopt);
830 setHelpText(QObject::tr("Sets the type of stream generated by "
831 "your PVR."));
832 };
833};
834
836{
837 public:
838 explicit MPEG2aspectRatio(const RecordingProfile &parent,
839 uint minopt = 0, uint maxopt = 8, uint defopt = 0) :
841 CodecParamStorage(this, parent, "mpeg2aspectratio")
842 {
843 maxopt = std::min<uint>(maxopt, 3);
844
845 setLabel(QObject::tr("Aspect Ratio"));
846
847 const std::array<const QString,4> options { QObject::tr("Square"), "4:3",
848 "16:9", "2.21:1" };
849
850 for (uint idx = minopt; idx <= maxopt; ++idx)
851 addSelection(options[idx]);
852
853 setValue(defopt);
854 setHelpText(QObject::tr("Sets the aspect ratio of stream generated "
855 "by your PVR."));
856 };
857};
858
860{
861 public:
862 explicit HardwareMJPEGQuality(const RecordingProfile &parent) :
863 MythUISpinBoxSetting(this, 0, 100, 1),
864 CodecParamStorage(this, parent, "hardwaremjpegquality")
865 {
866 setLabel(QObject::tr("Quality"));
867 setValue("100");
868 };
869};
870
872 public CodecParamStorage
873{
874 public:
877 CodecParamStorage(this, parent, "hardwaremjpeghdecimation")
878 {
879 setLabel(QObject::tr("Horizontal Decimation"));
880 addSelection("1");
881 addSelection("2");
882 addSelection("4");
883 setValue(2);
884 };
885};
886
888 public CodecParamStorage
889{
890 public:
893 CodecParamStorage(this, parent, "hardwaremjpegvdecimation") {
894 setLabel(QObject::tr("Vertical Decimation"));
895 addSelection("1");
896 addSelection("2");
897 addSelection("4");
898 setValue(2);
899 };
900};
901
903{
904 public:
906 [[maybe_unused]] V4L2util* v4l2) :
907 m_parent(parentProfile),
909 {
910 setName(QObject::tr("Video Compression"));
911
913
914 QString label("RTjpeg");
918
919 label = "MPEG-4";
925
928
932
933 label = "MPEG-2";
936 //m_codecName->addTargetedChild(label, new MPEG4MaxQuality(m_parent));
937 //m_codecName->addTargetedChild(label, new MPEG4MinQuality(m_parent));
938 //m_codecName->addTargetedChild(label, new MPEG4QualDiff(m_parent));
939 //m_codecName->addTargetedChild(label, new MPEG4OptionVHQ(m_parent));
940 //m_codecName->addTargetedChild(label, new MPEG4Option4MV(m_parent));
942
943 label = "Hardware MJPEG";
947
948 label = "MPEG-2 Hardware Encoder";
953
954 label = "MPEG-4 AVC Hardware Encoder";
955 auto *h0 = new GroupSetting();
956 h0->setLabel(QObject::tr("Low Resolution"));
957 h0->addChild(new AverageBitrate(m_parent, "low_mpeg4avgbitrate",
958 1000, 13500, 4500, 500));
959 h0->addChild(new PeakBitrate(m_parent, "low_mpeg4peakbitrate",
960 1100, 20200, 6000, 500));
961 m_codecName->addTargetedChild(label, h0);
962
963 auto *h1 = new GroupSetting();
964 h1->setLabel(QObject::tr("Medium Resolution"));
965 h1->addChild(new AverageBitrate(m_parent, "medium_mpeg4avgbitrate",
966 1000, 13500, 9000, 500));
967 h1->addChild(new PeakBitrate(m_parent, "medium_mpeg4peakbitrate",
968 1100, 20200, 11000, 500));
969 m_codecName->addTargetedChild(label, h1);
970
971 auto *h2 = new GroupSetting();
972 h2->setLabel(QObject::tr("High Resolution"));
973 h2->addChild(new AverageBitrate(m_parent, "high_mpeg4avgbitrate",
974 1000, 13500, 13500, 500));
975 h2->addChild(new PeakBitrate(m_parent, "high_mpeg4peakbitrate",
976 1100, 20200, 20200, 500));
977 m_codecName->addTargetedChild(label, h2);
978
979#if CONFIG_V4L2
980 if (v4l2)
981 {
983
984 if (v4l2->GetOptions(options))
985 {
986 /* StandardSetting cannot handle multiple 'targets' pointing
987 * to the same setting configuration, so we need to do
988 * this in two passes. */
989
990 for (const auto & option : std::as_const(options))
991 {
992 if (option.m_category == DriverOption::VIDEO_ENCODING)
993 {
994 for (const auto & Imenu : std::as_const(option.m_menu))
995 {
996 if (!Imenu.isEmpty())
997 m_v4l2codecs << "V4L2:" + Imenu;
998 }
999 }
1000 }
1001
1002 for (auto Icodec = m_v4l2codecs.begin(); Icodec < m_v4l2codecs.end(); ++Icodec)
1003 {
1004 auto* bit_low = new GroupSetting();
1005 auto* bit_medium = new GroupSetting();
1006 auto* bit_high = new GroupSetting();
1007 bool dynamic_res = !v4l2->UserAdjustableResolution();
1008
1009 for (auto & option : options)
1010 {
1011 if (option.m_category == DriverOption::STREAM_TYPE)
1012 {
1015 option.m_minimum,
1016 option.m_maximum,
1017 option.m_defaultValue));
1018 }
1019 else if (option.m_category == DriverOption::VIDEO_ASPECT)
1020 {
1023 option.m_minimum,
1024 option.m_maximum,
1025 option.m_defaultValue));
1026 }
1027 else if (option.m_category ==
1029 {
1030 if (dynamic_res)
1031 {
1032 bit_low->addChild(new BitrateMode(m_parent,
1033 "low_mpegbitratemode"));
1034 bit_medium->addChild(new BitrateMode(m_parent,
1035 "medium_mpegbitratemode"));
1036 bit_high->addChild(new BitrateMode(m_parent,
1037 "medium_mpegbitratemode"));
1038 }
1039 else
1040 {
1041 bit_low->addChild(new BitrateMode(m_parent));
1042 }
1043 }
1044 else if (option.m_category == DriverOption::VIDEO_BITRATE)
1045 {
1046 if (dynamic_res)
1047 {
1048 bit_low->setLabel(QObject::tr("Low Resolution"));
1049 bit_low->addChild(new AverageBitrate(m_parent,
1050 "low_mpegavgbitrate",
1051 option.m_minimum / 1000,
1052 option.m_maximum / 1000,
1053 option.m_defaultValue / 1000,
1054 option.m_step / 1000));
1055
1056 bit_medium->setLabel(QObject::
1057 tr("Medium Resolution"));
1058 bit_medium->addChild(new AverageBitrate(m_parent,
1059 "medium_mpegavgbitrate",
1060 option.m_minimum / 1000,
1061 option.m_maximum / 1000,
1062 option.m_defaultValue / 1000,
1063 option.m_step / 1000));
1064
1065 bit_high->setLabel(QObject::
1066 tr("High Resolution"));
1067 bit_high->addChild(new AverageBitrate(m_parent,
1068 "high_mpegavgbitrate",
1069 option.m_minimum / 1000,
1070 option.m_maximum / 1000,
1071 option.m_defaultValue / 1000,
1072 option.m_step / 1000));
1073 }
1074 else
1075 {
1076 bit_low->setLabel(QObject::tr("Bitrate"));
1077 bit_low->addChild(new AverageBitrate(m_parent,
1078 "mpeg2bitrate",
1079 option.m_minimum / 1000,
1080 option.m_maximum / 1000,
1081 option.m_defaultValue / 1000,
1082 option.m_step / 1000));
1083 }
1084 }
1085 else if (option.m_category ==
1087 {
1088 if (dynamic_res)
1089 {
1090 bit_low->addChild(new PeakBitrate(m_parent,
1091 "low_mpegpeakbitrate",
1092 option.m_minimum / 1000,
1093 option.m_maximum / 1000,
1094 option.m_defaultValue / 1000,
1095 option.m_step / 1000));
1096 bit_medium->addChild(new PeakBitrate(m_parent,
1097 "medium_mpegpeakbitrate",
1098 option.m_minimum / 1000,
1099 option.m_maximum / 1000,
1100 option.m_defaultValue / 1000,
1101 option.m_step / 1000));
1102 bit_high->addChild(new PeakBitrate(m_parent,
1103 "high_mpegpeakbitrate",
1104 option.m_minimum / 1000,
1105 option.m_maximum / 1000,
1106 option.m_defaultValue / 1000,
1107 option.m_step / 1000));
1108 }
1109 else
1110 {
1111 bit_low->addChild(new PeakBitrate(m_parent,
1112 "mpeg2maxbitrate",
1113 option.m_minimum / 1000,
1114 option.m_maximum / 1000,
1115 option.m_defaultValue / 1000,
1116 option.m_step / 1000));
1117 }
1118 }
1119 }
1120
1121 m_codecName->addTargetedChild(*Icodec, bit_low);
1122 if (dynamic_res)
1123 {
1124 m_codecName->addTargetedChild(*Icodec, bit_medium);
1125 m_codecName->addTargetedChild(*Icodec, bit_high);
1126 }
1127 else
1128 {
1129 // These are only referenced when dynamic_res is true.
1130 delete bit_medium;
1131 delete bit_high;
1132 }
1133 }
1134 }
1135 }
1136#endif // CONFIG_V4L2
1137 }
1138
1139 void selectCodecs(const QString& groupType)
1140 {
1141 if (!groupType.isNull())
1142 {
1143 if (groupType == "HDPVR")
1144 {
1145 m_codecName->addSelection("MPEG-4 AVC Hardware Encoder");
1146 }
1147 else if (groupType.startsWith("V4L2:"))
1148 {
1149 for (const auto & codec : std::as_const(m_v4l2codecs))
1150 m_codecName->addSelection(codec);
1151 }
1152 else if (groupType == "MPEG")
1153 {
1154 m_codecName->addSelection("MPEG-2 Hardware Encoder");
1155 }
1156 else if (groupType == "MJPEG")
1157 {
1158 m_codecName->addSelection("Hardware MJPEG");
1159 }
1160 else if (groupType == "GO7007")
1161 {
1162 m_codecName->addSelection("MPEG-4");
1163 m_codecName->addSelection("MPEG-2");
1164 }
1165 else
1166 {
1167 // V4L, TRANSCODE (and any undefined types)
1168 m_codecName->addSelection("RTjpeg");
1169 m_codecName->addSelection("MPEG-4");
1170 }
1171 }
1172 else
1173 {
1174 m_codecName->addSelection("RTjpeg");
1175 m_codecName->addSelection("MPEG-4");
1176 m_codecName->addSelection("Hardware MJPEG");
1177 m_codecName->addSelection("MPEG-2 Hardware Encoder");
1178 }
1179 }
1180
1181private:
1184 QStringList m_v4l2codecs;
1185};
1186
1188{
1189 public:
1190 explicit AutoTranscode(const RecordingProfile &parent) :
1192 CodecParamStorage(this, parent, "autotranscode")
1193 {
1194 setLabel(QObject::tr("Enable auto-transcode after recording"));
1195 setValue(false);
1196 setHelpText(QObject::tr("Automatically transcode when a recording is "
1197 "made using this profile and the recording's "
1198 "schedule is configured to allow transcoding."));
1199 };
1200};
1201
1203{
1204 public:
1205 explicit TranscodeResize(const RecordingProfile &parent) :
1207 CodecParamStorage(this, parent, "transcoderesize")
1208 {
1209 setLabel(QObject::tr("Resize video while transcoding"));
1210 setValue(false);
1211 setHelpText(QObject::tr("Allows the transcoder to "
1212 "resize the video during transcoding."));
1213 };
1214};
1215
1217{
1218 public:
1219 explicit TranscodeLossless(const RecordingProfile &parent) :
1221 CodecParamStorage(this, parent, "transcodelossless")
1222 {
1223 setLabel(QObject::tr("Lossless transcoding"));
1224 setValue(false);
1225 setHelpText(QObject::tr("Only reencode where absolutely needed "
1226 "(normally only around cutpoints). Otherwise "
1227 "keep audio and video formats identical to "
1228 "the source. This should result in the "
1229 "highest quality, but won't save as much "
1230 "space."));
1231 };
1232};
1233
1235{
1236 public:
1237 explicit RecordingTypeStream(const RecordingProfile &parent) :
1238 MythUIComboBoxSetting(this), CodecParamStorage(this, parent, "recordingtype")
1239 {
1240 setLabel(QObject::tr("Recording Type"));
1241
1242 QString msg = QObject::tr(
1243 "This option allows you to filter out unwanted streams. "
1244 "'Normal' will record all relevant streams including "
1245 "interactive television data. 'TV Only' will record only "
1246 "audio, video and subtitle streams. ");
1247 setHelpText(msg);
1248
1249 addSelection(QObject::tr("Normal"), "all");
1250 addSelection(QObject::tr("TV Only"), "tv");
1251 addSelection(QObject::tr("Audio Only"), "audio");
1252 setValue(0);
1253 };
1254};
1255
1257{
1258 public:
1259 explicit RecordFullTSStream(const RecordingProfile &parent) :
1260 MythUIComboBoxSetting(this), CodecParamStorage(this, parent, "recordmpts")
1261 {
1262 setLabel(QObject::tr("Record Full TS?"));
1263
1264 QString msg = QObject::tr(
1265 "If set, extra files will be created for each recording with "
1266 "the name of the recording followed by '.ts.raw'. "
1267 "These extra files represent the full contents of the transport "
1268 "stream used to generate the recording. (For debugging purposes)");
1269 setHelpText(msg);
1270
1271 addSelection(QObject::tr("Yes"), "1");
1272 addSelection(QObject::tr("No"), "0");
1273 setValue(1);
1274 };
1275};
1276
1278{
1279 public:
1280 explicit TranscodeFilters(const RecordingProfile &parent) :
1282 CodecParamStorage(this, parent, "transcodefilters")
1283 {
1284 setLabel(QObject::tr("Custom filters"));
1285 setHelpText(QObject::tr("Filters used when transcoding with this "
1286 "profile. This value must be blank to perform "
1287 "lossless transcoding. Format: "
1288 "[[<filter>=<options>,]...]"
1289 ));
1290 };
1291};
1292
1294{
1295 public:
1297 {
1298 public:
1300 uint defaultwidth, uint maxwidth,
1301 bool transcoding = false) :
1302 MythUISpinBoxSetting(this, transcoding ? 0 : 160,
1303 maxwidth, 16, 0,
1304 transcoding ? QObject::tr("Auto") : QString()),
1305 CodecParamStorage(this, parent, "width")
1306 {
1307 setLabel(QObject::tr("Width"));
1308 setValue(defaultwidth);
1309
1310 QString help = transcoding ?
1311 QObject::tr("If the width is set to 'Auto', the width "
1312 "will be calculated based on the height and "
1313 "the recording's physical aspect ratio.") :
1314 QObject::tr("Width to use for encoding. "
1315 "Note: PVR-x50 cards may produce ghosting if "
1316 "this is not set to 720 or 768 for NTSC and "
1317 "PAL, respectively.");
1318
1320 };
1321 };
1322
1324 {
1325 public:
1327 uint defaultheight, uint maxheight,
1328 bool transcoding = false):
1329 MythUISpinBoxSetting(this, transcoding ? 0 : 160,
1330 maxheight, 16, 0,
1331 transcoding ? QObject::tr("Auto") : QString()),
1332 CodecParamStorage(this, parent, "height")
1333 {
1334 setLabel(QObject::tr("Height"));
1335 setValue(defaultheight);
1336
1337 QString help = transcoding ?
1338 QObject::tr("If the height is set to 'Auto', the height "
1339 "will be calculated based on the width and "
1340 "the recording's physical aspect ratio.") :
1341 QObject::tr("Height to use for encoding. "
1342 "Note: PVR-x50 cards may produce ghosting if "
1343 "this is not set to 480 or 576 for NTSC and "
1344 "PAL, respectively.");
1345
1347 };
1348 };
1349
1351 const QString& tvFormat, const QString& profName)
1352 {
1353 setLabel(QObject::tr("Image size"));
1354
1355 QSize defaultsize(768, 576);
1356 QSize maxsize(768, 576);
1357 bool transcoding = profName.startsWith("Transcoders");
1358 bool ivtv = profName.startsWith("IVTV MPEG-2 Encoders");
1359
1360 if (transcoding)
1361 {
1362 maxsize = QSize(1920, 1088);
1363 if (tvFormat.toLower() == "ntsc" || tvFormat.toLower() == "atsc")
1364 defaultsize = QSize(480, 480);
1365 else
1366 defaultsize = QSize(480, 576);
1367 }
1368 else if (tvFormat.startsWith("ntsc", Qt::CaseInsensitive))
1369 {
1370 maxsize = QSize(720, 480);
1371 defaultsize = ivtv ? QSize(720, 480) : QSize(480, 480);
1372 }
1373 else if (tvFormat.toLower() == "atsc")
1374 {
1375 maxsize = QSize(1920, 1088);
1376 defaultsize = QSize(1920, 1088);
1377 }
1378 else
1379 {
1380 maxsize = QSize(768, 576);
1381 defaultsize = ivtv ? QSize(720, 576) : QSize(480, 576);
1382 }
1383
1384 addChild(new Width(parent, defaultsize.width(),
1385 maxsize.width(), transcoding));
1386 addChild(new Height(parent, defaultsize.height(),
1387 maxsize.height(), transcoding));
1388 };
1389};
1390
1391// id and name will be deleted by ConfigurationGroup's destructor
1393 : m_id(new ID()),
1394 m_name(new Name(*this)),
1395 m_profileName(profName)
1396{
1397 // This must be first because it is needed to load/save the other settings
1398 addChild(m_id);
1399
1400 setLabel(profName);
1402
1403 if (!profName.isEmpty())
1404 {
1405 if (profName.startsWith("Transcoders"))
1406 {
1407 m_trFilters = new TranscodeFilters(*this);
1408 m_trLossless = new TranscodeLossless(*this);
1409 m_trResize = new TranscodeResize(*this);
1413 }
1414 else
1415 {
1416 addChild(new AutoTranscode(*this));
1417 }
1418 }
1419 else
1420 {
1421 m_trFilters = new TranscodeFilters(*this);
1422 m_trLossless = new TranscodeLossless(*this);
1423 m_trResize = new TranscodeResize(*this);
1427 addChild(new AutoTranscode(*this));
1428 }
1429};
1430
1431// NOLINTNEXTLINE(modernize-use-equals-default)
1433{
1434#if CONFIG_V4L2
1435 delete m_v4l2util;
1436 m_v4l2util = nullptr;
1437#endif
1438}
1439
1440void RecordingProfile::ResizeTranscode(const QString & /*val*/)
1441{
1442 if (m_imageSize)
1444}
1445
1446void RecordingProfile::SetLosslessTranscode(const QString & /*val*/)
1447{
1448 bool lossless = m_trLossless->boolValue();
1449 bool show_size = lossless ? false : m_trResize->boolValue();
1450 if (m_imageSize)
1451 m_imageSize->setEnabled(show_size);
1452 m_videoSettings->setEnabled(! lossless);
1453 m_audioSettings->setEnabled(! lossless);
1454 m_trResize->setEnabled(! lossless);
1455 m_trFilters->setEnabled(! lossless);
1456}
1457
1459{
1460 MSqlQuery result(MSqlQuery::InitCon());
1461 result.prepare(
1462 "SELECT cardtype, profilegroups.name "
1463 "FROM profilegroups, recordingprofiles "
1464 "WHERE profilegroups.id = recordingprofiles.profilegroup AND "
1465 " recordingprofiles.id = :PROFILEID");
1466 result.bindValue(":PROFILEID", profileId);
1467
1468 QString type;
1469 QString name;
1470 if (!result.exec())
1471 {
1472 MythDB::DBError("RecordingProfile::loadByID -- cardtype", result);
1473 }
1474 else if (result.next())
1475 {
1476 type = result.value(0).toString();
1477 name = result.value(1).toString();
1478 }
1479
1480 CompleteLoad(profileId, type, name);
1481}
1482
1483void RecordingProfile::FiltersChanged(const QString &val)
1484{
1485 // If there are filters, we cannot do lossless transcoding
1486 if (!val.trimmed().isEmpty())
1487 {
1488 m_trLossless->setValue(false);
1489 m_trLossless->setEnabled(false);
1490 }
1491 else
1492 {
1493 m_trLossless->setEnabled(true);
1494 }
1495}
1496
1497bool RecordingProfile::loadByType(const QString &name, const QString &card,
1498 [[maybe_unused]] const QString &videodev)
1499{
1500 QString hostname = gCoreContext->GetHostName().toLower();
1501 uint profileId = 0;
1502
1503#if CONFIG_V4L2
1504 QString cardtype = card;
1505 if (cardtype == "V4L2ENC")
1506 {
1507 m_v4l2util = new V4L2util(videodev);
1508 if (m_v4l2util->IsOpen())
1509 cardtype = m_v4l2util->ProfileName();
1510 }
1511#else
1512 const QString& cardtype = card;
1513#endif
1514
1515 MSqlQuery result(MSqlQuery::InitCon());
1516 result.prepare(
1517 "SELECT recordingprofiles.id, profilegroups.hostname, "
1518 " profilegroups.is_default "
1519 "FROM recordingprofiles, profilegroups "
1520 "WHERE profilegroups.id = recordingprofiles.profilegroup AND "
1521 " profilegroups.cardtype = :CARDTYPE AND "
1522 " recordingprofiles.name = :NAME");
1523 result.bindValue(":CARDTYPE", cardtype);
1524 result.bindValue(":NAME", name);
1525
1526 if (!result.exec())
1527 {
1528 MythDB::DBError("RecordingProfile::loadByType()", result);
1529 return false;
1530 }
1531
1532 while (result.next())
1533 {
1534 if (result.value(1).toString().toLower() == hostname)
1535 {
1536 profileId = result.value(0).toUInt();
1537 }
1538 else if (result.value(2).toInt() == 1)
1539 {
1540 profileId = result.value(0).toUInt();
1541 break;
1542 }
1543 }
1544
1545 if (profileId)
1546 {
1547 CompleteLoad(profileId, cardtype, name);
1548 return true;
1549 }
1550
1551 return false;
1552}
1553
1554bool RecordingProfile::loadByGroup(const QString &name, const QString &group)
1555{
1556 MSqlQuery result(MSqlQuery::InitCon());
1557 result.prepare(
1558 "SELECT recordingprofiles.id, cardtype "
1559 "FROM recordingprofiles, profilegroups "
1560 "WHERE recordingprofiles.profilegroup = profilegroups.id AND "
1561 " profilegroups.name = :GROUPNAME AND "
1562 " recordingprofiles.name = :NAME");
1563 result.bindValue(":GROUPNAME", group);
1564 result.bindValue(":NAME", name);
1565
1566 if (!result.exec())
1567 {
1568 MythDB::DBError("RecordingProfile::loadByGroup()", result);
1569 return false;
1570 }
1571
1572 if (result.next())
1573 {
1574 uint profileId = result.value(0).toUInt();
1575 QString type = result.value(1).toString();
1576
1577 CompleteLoad(profileId, type, name);
1578 return true;
1579 }
1580
1581 return false;
1582}
1583
1584void RecordingProfile::CompleteLoad(int profileId, const QString &type,
1585 const QString &name)
1586{
1587 if (m_profileName.isEmpty())
1588 m_profileName = name;
1589
1591
1592 if (m_isEncoder)
1593 {
1594#if CONFIG_V4L2
1595 if (type.startsWith("V4L2:"))
1596 {
1597 QStringList devices = CardUtil::GetVideoDevices("V4L2ENC");
1598 if (!devices.isEmpty())
1599 {
1600 for (const auto & device : std::as_const(devices))
1601 {
1602 delete m_v4l2util;
1603 m_v4l2util = new V4L2util(device);
1604 if (m_v4l2util->IsOpen() &&
1605 m_v4l2util->DriverName() == type.mid(5))
1606 break;
1607 delete m_v4l2util;
1608 m_v4l2util = nullptr;
1609 }
1610 }
1611 }
1612#endif
1613
1614 QString tvFormat = gCoreContext->GetSetting("TVFormat");
1615 // TODO: When mpegrecorder is removed, don't check for "HDPVR' anymore...
1616 if (type != "HDPVR" &&
1617 (!m_v4l2util
1618#if CONFIG_V4L2
1620#endif
1621 ))
1622 {
1623 addChild(new ImageSize(*this, tvFormat, m_profileName));
1624 }
1626 m_v4l2util);
1628
1630 m_v4l2util);
1632
1633 if (!m_profileName.isEmpty() && m_profileName.startsWith("Transcoders"))
1634 {
1635 connect(m_trResize, qOverload<const QString&>(&StandardSetting::valueChanged),
1637 connect(m_trLossless, qOverload<const QString&>(&StandardSetting::valueChanged),
1639 connect(m_trFilters, qOverload<const QString&>(&StandardSetting::valueChanged),
1641 }
1642 }
1643
1644 // Cards that can receive additional streams such as EIT and MHEG
1646 {
1647 addChild(new RecordingTypeStream(*this));
1648 }
1649
1651 {
1652 addChild(new RecordFullTSStream(*this));
1653 }
1654
1655 m_id->setValue(profileId);
1656 Load();
1657}
1658
1660{
1661 if (m_videoSettings)
1663 if (m_audioSettings)
1665}
1666
1668 m_group(id), m_labelName(std::move(profName))
1669{
1670 if (!m_labelName.isEmpty())
1672}
1673
1675{
1676 clearSettings();
1677 auto *newProfile = new ButtonStandardSetting(tr("(Create new profile)"));
1679 addChild(newProfile);
1682}
1683
1685{
1686 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1687 auto *settingdialog = new MythTextInputDialog(popupStack,
1688 tr("Enter the name of the new profile"));
1689
1690 if (settingdialog->Create())
1691 {
1692 connect(settingdialog, &MythTextInputDialog::haveResult,
1694 popupStack->AddScreen(settingdialog);
1695 }
1696 else
1697 {
1698 delete settingdialog;
1699 }
1700}
1701
1702void RecordingProfileEditor::CreateNewProfile(const QString& profName)
1703{
1705 query.prepare(
1706 "INSERT INTO recordingprofiles "
1707 "(name, videocodec, audiocodec, profilegroup) "
1708 "VALUES "
1709 "(:NAME, :VIDEOCODEC, :AUDIOCODEC, :PROFILEGROUP);");
1710 query.bindValue(":NAME", profName);
1711 query.bindValue(":VIDEOCODEC", "MPEG-4");
1712 query.bindValue(":AUDIOCODEC", "MP3");
1713 query.bindValue(":PROFILEGROUP", m_group);
1714 if (!query.exec())
1715 {
1716 MythDB::DBError("RecordingProfileEditor::open", query);
1717 }
1718 else
1719 {
1720 query.prepare(
1721 "SELECT id "
1722 "FROM recordingprofiles "
1723 "WHERE name = :NAME AND profilegroup = :PROFILEGROUP;");
1724 query.bindValue(":NAME", profName);
1725 query.bindValue(":PROFILEGROUP", m_group);
1726 if (!query.exec())
1727 {
1728 MythDB::DBError("RecordingProfileEditor::open", query);
1729 }
1730 else
1731 {
1732 if (query.next())
1733 {
1734 auto* profile = new RecordingProfile(profName);
1735
1736 profile->loadByID(query.value(0).toInt());
1737 profile->setCodecTypes();
1739 emit settingsChanged(this);
1740 }
1741 }
1742 }
1743}
1744
1746 bool foldautodetect)
1747{
1748 if (!group)
1749 {
1750 for (const auto & name : kAvailProfiles)
1751 {
1752 auto *profile = new GroupSetting();
1753 profile->setLabel(name);
1754 setting->addChild(profile);
1755 }
1756 return;
1757 }
1758
1759 MSqlQuery result(MSqlQuery::InitCon());
1760 result.prepare(
1761 "SELECT name, id "
1762 "FROM recordingprofiles "
1763 "WHERE profilegroup = :GROUP "
1764 "ORDER BY id");
1765 result.bindValue(":GROUP", group);
1766
1767 if (!result.exec())
1768 {
1769 MythDB::DBError("RecordingProfile::fillSelections 1", result);
1770 return;
1771 }
1772
1773 while (result.next())
1774 {
1775 if ((result.at() == 0) && foldautodetect &&
1777 {
1778 auto *autodetect = new GroupSetting();
1779 autodetect->setLabel(QObject::tr("Autodetect"));
1780 setting->addChild(autodetect);
1781 }
1782
1783 QString name = result.value(0).toString();
1784 QString id = result.value(1).toString();
1785
1786 if ((group == RecordingProfile::TranscoderGroup) &&
1787 (name == "RTjpeg/MPEG4" || name == "MPEG2") &&
1788 !foldautodetect) {
1789 name = QObject::tr("Autodetect from %1").arg(name);
1790 }
1791
1792 auto *profile = new RecordingProfile(name);
1793 profile->loadByID(id.toInt());
1794 profile->setCodecTypes();
1795 setting->addChild(profile);
1796 }
1797}
1798
1800{
1801 QMap<int, QString> profiles;
1802
1803 if (!group)
1804 {
1805 for (uint i = 0; !kAvailProfiles[i].isEmpty(); i++)
1806 profiles[i] = kAvailProfiles[i];
1807 return profiles;
1808 }
1809
1811 query.prepare(
1812 "SELECT name, id "
1813 "FROM recordingprofiles "
1814 "WHERE profilegroup = :GROUPID "
1815 "ORDER BY id");
1816 query.bindValue(":GROUPID", group);
1817
1818 if (!query.exec())
1819 {
1820 MythDB::DBError("RecordingProfile::GetProfileMap()", query);
1821 return profiles;
1822 }
1823
1824 while (query.next())
1825 {
1826 if ((query.at() == 0) && (group == RecordingProfile::TranscoderGroup))
1827 {
1829 profiles[id] = QObject::tr("Transcode using Autodetect");
1830 }
1831
1832 QString name = query.value(0).toString();
1833 int id = query.value(1).toInt();
1834
1836 {
1837 /* RTjpeg/MPEG4 and MPEG2 are used by "Autodetect". */
1838 if (name != "RTjpeg/MPEG4" && name != "MPEG2")
1839 {
1840 QString lbl = QObject::tr("Transcode using \"%1\"").arg(name);
1841 profiles[id] = lbl;
1842 }
1843 continue;
1844 }
1845
1846 QString lbl = QObject::tr("Record using the \"%1\" profile").arg(name);
1847 profiles[id] = lbl;
1848 }
1849
1850 if (query.at() == QSql::BeforeFirstRow)
1851 {
1852 LOG(VB_GENERAL, LOG_WARNING,
1853 "RecordingProfile::fillselections, Warning: "
1854 "Failed to locate recording id for recording group.");
1855 }
1856
1857 return profiles;
1858}
1859
1861{
1863}
1864
1866{
1867 MSqlQuery result(MSqlQuery::InitCon());
1868 result.prepare(
1869 "SELECT profilegroups.cardtype "
1870 "FROM profilegroups, recordingprofiles "
1871 "WHERE profilegroups.id = recordingprofiles.profilegroup AND "
1872 " recordingprofiles.id = :ID");
1873 result.bindValue(":ID", getProfileNum());
1874
1875 if (!result.exec())
1876 MythDB::DBError("RecordingProfile::groupType", result);
1877 else if (result.next())
1878 return result.value(0).toString();
1879
1880 return {};
1881}
1882
1884{
1885 MSqlQuery result(MSqlQuery::InitCon());
1886 result.prepare(
1887 "SELECT name "
1888 "FROM recordingprofiles "
1889 "WHERE id = :ID");
1890
1891 result.bindValue(":ID", id);
1892
1893 if (!result.exec())
1894 MythDB::DBError("RecordingProfile::getName", result);
1895 else if (result.next())
1896 return result.value(0).toString();
1897
1898 return {};
1899}
1900
1902{
1903 return true;
1904}
1905
1907{
1908 MSqlQuery result(MSqlQuery::InitCon());
1909 result.prepare(
1910 "DELETE "
1911 "FROM recordingprofiles "
1912 "WHERE id = :ID");
1913
1914 result.bindValue(":ID", m_id->getValue());
1915
1916 if (!result.exec())
1917 MythDB::DBError("RecordingProfile::deleteEntry", result);
1918
1919}
1920
1921
1922/* vim: set expandtab tabstop=4 shiftwidth=4: */
AudioCodecName(const RecordingProfile &parent)
const RecordingProfile & m_parent
void selectCodecs(const QString &groupType)
AudioCompressionSettings(const RecordingProfile &parentProfile, V4L2util *v4l2)
AutoTranscode(const RecordingProfile &parent)
AverageBitrate(const RecordingProfile &parent, const QString &setting="mpeg2bitrate", uint min_br=1000, uint max_br=16000, uint default_br=4500, uint increment=100, QString label=QString())
BTTVVolume(const RecordingProfile &parent)
BitrateMode(const RecordingProfile &parent, const QString &setting="mpeg2bitratemode")
static bool IsTunerSharingCapable(const QString &rawtype)
Definition: cardutil.h:179
static QStringList GetVideoDevices(const QString &rawtype, QString hostname=QString())
Returns the videodevices of the matching inputs, duplicates removed.
Definition: cardutil.cpp:402
static bool IsEITCapable(const QString &rawtype)
Definition: cardutil.h:172
static bool IsEncoder(const QString &rawtype)
Definition: cardutil.h:137
const RecordingProfile & m_parent
QString GetSetClause(MSqlBindings &bindings) const override
QString GetWhereClause(MSqlBindings &bindings) const override
CodecParamStorage(StandardSetting *_setting, const RecordingProfile &parentProfile, const QString &name)
StorageUser * m_user
Definition: mythstorage.h:50
EncodingThreadCount(const RecordingProfile &parent)
GroupSetting()=default
HardwareMJPEGHDecimation(const RecordingProfile &parent)
HardwareMJPEGQuality(const RecordingProfile &parent)
HardwareMJPEGVDecimation(const RecordingProfile &parent)
Height(const RecordingProfile &parent, uint defaultheight, uint maxheight, bool transcoding=false)
Width(const RecordingProfile &parent, uint defaultwidth, uint maxwidth, bool transcoding=false)
ImageSize(const RecordingProfile &parent, const QString &tvFormat, const QString &profName)
MP3Quality(const RecordingProfile &parent)
MPEG2AudioBitrateSettings(const RecordingProfile &parent, bool layer1, bool layer2, bool layer3, uint default_layer)
MPEG2Language(const RecordingProfile &parent)
MPEG2aspectRatio(const RecordingProfile &parent, uint minopt=0, uint maxopt=8, uint defopt=0)
MPEG2audBitrateL1(const RecordingProfile &parent)
MPEG2audBitrateL2(const RecordingProfile &parent)
MPEG2audBitrateL3(const RecordingProfile &parent)
MPEG2audType(const RecordingProfile &parent, bool layer1, bool layer2, bool layer3)
void Load(void) override
MPEG2audVolume(const RecordingProfile &parent)
MPEG2streamType(const RecordingProfile &parent, uint minopt=0, uint maxopt=8, uint defopt=0)
MPEG4MaxQuality(const RecordingProfile &parent)
MPEG4MinQuality(const RecordingProfile &parent)
MPEG4Option4MV(const RecordingProfile &parent)
MPEG4OptionIDCT(const RecordingProfile &parent)
MPEG4OptionIME(const RecordingProfile &parent)
MPEG4OptionVHQ(const RecordingProfile &parent)
MPEG4QualDiff(const RecordingProfile &parent)
MPEG4bitrate(const RecordingProfile &parent)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
int at(void) const
Definition: mythdbcon.h:221
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
QString GetHostName(void)
QString GetSetting(const QString &key, const QString &defaultval="")
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
MythScreenStack * GetStack(const QString &Stackname)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Dialog prompting the user to enter a text string.
void haveResult(QString)
void setValue(const QString &newValue) override
virtual void addSelection(const QString &label, QString value=QString(), bool select=false)
void setValue(int value) override
int getValueIndex(const QString &value) const
PeakBitrate(const RecordingProfile &parent, const QString &setting="mpeg2maxbitrate", uint min_br=1000, uint max_br=16000, uint default_br=6000, uint increment=100, QString label=QString())
RTjpegChromaFilter(const RecordingProfile &parent)
RTjpegLumaFilter(const RecordingProfile &parent)
RTjpegQuality(const RecordingProfile &parent)
RecordFullTSStream(const RecordingProfile &parent)
void CreateNewProfile(const QString &profName)
void Load(void) override
RecordingProfileEditor(int id, QString profName)
QString GetWhereClause(MSqlBindings &bindings) const override
const RecordingProfile & m_parent
~RecordingProfile(void) override
int getProfileNum(void) const
static const uint kTranscoderAutodetect
sentinel value
void FiltersChanged(const QString &val)
QString getName(void) const
RecordingProfile(const QString &profName=QString())
TranscodeResize * m_trResize
bool canDelete(void) override
void SetLosslessTranscode(const QString &val)
virtual void CompleteLoad(int profileId, const QString &type, const QString &name)
static QMap< int, QString > GetTranscodingProfiles()
ImageSize * m_imageSize
virtual bool loadByType(const QString &name, const QString &cardtype, const QString &videodev)
TranscodeFilters * m_trFilters
static QMap< int, QString > GetProfiles(RecProfileGroup group=AllGroups)
static void fillSelections(GroupSetting *setting, int group, bool foldautodetect=false)
void ResizeTranscode(const QString &val)
TranscodeLossless * m_trLossless
VideoCompressionSettings * m_videoSettings
virtual void loadByID(int id)
QString groupType(void) const
AudioCompressionSettings * m_audioSettings
virtual bool loadByGroup(const QString &name, const QString &group)
void deleteEntry(void) override
RecordingTypeStream(const RecordingProfile &parent)
QMap< uint, bool > m_allowedRate
SampleRate(const RecordingProfile &parent, bool analog=true)
void addSelection(const QString &label, const QString value=QString(), bool select=false) override
void Load(void) override
std::vector< uint > m_rates
ScaleBitrate(const RecordingProfile &parent)
void Load(void) override
Definition: mythstorage.cpp:9
virtual void addChild(StandardSetting *child)
virtual void Load(void)
virtual void setName(const QString &name)
void addTargetedChild(const QString &value, StandardSetting *setting)
virtual void clearSettings()
void settingsChanged(StandardSetting *selectedSetting=nullptr)
virtual void setHelpText(const QString &str)
virtual void setValue(const QString &newValue)
void valueChanged(const QString &newValue)
virtual QString getValue(void) const
virtual void setEnabled(bool enabled)
virtual void setLabel(QString str)
virtual QString GetDBValue(void) const =0
TranscodeFilters(const RecordingProfile &parent)
TranscodeLossless(const RecordingProfile &parent)
TranscodeResize(const RecordingProfile &parent)
bool UserAdjustableResolution(void) const
Definition: v4l2util.cpp:711
QString DriverName(void) const
Definition: v4l2util.h:50
bool IsOpen(void) const
Definition: v4l2util.h:31
QString ProfileName(void) const
Definition: v4l2util.h:52
VideoCodecName(const RecordingProfile &parent)
VideoCompressionSettings(const RecordingProfile &parentProfile, V4L2util *v4l2)
void selectCodecs(const QString &groupType)
const RecordingProfile & m_parent
unsigned int uint
Definition: compat.h:60
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
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
string hostname
Definition: caa.py:17
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:204
const std::array< QString, 4 > kAvailProfiles
QMap< category_t, DriverOption > Options
Definition: driveroption.h:22
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:80