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