MythTV  master
v4l2encstreamhandler.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 
3 #include <iostream>
4 
5 // POSIX headers
6 #include <fcntl.h>
7 #include <unistd.h>
8 #ifndef USING_MINGW
9 #include <poll.h>
10 //#include <sys/select.h>
11 #include <sys/ioctl.h>
12 #endif
13 #include <chrono> // for milliseconds
14 #include <thread> // for sleep_for
15 
16 // Qt headers
17 #include <QString>
18 #include <QFile>
19 
20 // MythTV headers
21 #include "v4l2encstreamhandler.h"
22 #include "v4lchannel.h"
23 #include "dtvsignalmonitor.h"
24 #include "streamlisteners.h"
25 #include "mpegstreamdata.h"
26 #include "cardutil.h"
27 #include "exitcodes.h"
28 
30 {
31  "MPEG-2 PS", "MPEG-2 TS", "MPEG-1 VCD", "PES AV",
32  "", "PES V", "", "PES A",
33  "", "", "DVD", "VCD",
34  "SVCD", "DVD-Special 1", "DVD-Special 2", nullptr
35 };
36 
38 {
39  32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0
40 };
41 
43 {
44  32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0
45 };
46 
48 {
49  32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0
50 };
51 
52 #define LOC QString("V4L2SH[%1](%2): ").arg(m_inputId).arg(m_device)
53 
54 QMap<QString,V4L2encStreamHandler*> V4L2encStreamHandler::s_handlers;
57 
59  int audioinput, int inputid)
60 {
61  QMutexLocker locker(&s_handlers_lock);
62 
63  const QString& devkey = devname;
64 
65  QMap<QString,V4L2encStreamHandler*>::iterator it = s_handlers.find(devkey);
66 
67  if (it == s_handlers.end())
68  {
69  auto *newhandler = new V4L2encStreamHandler(devname, audioinput, inputid);
70 
71  s_handlers[devkey] = newhandler;
72  s_handlers_refcnt[devkey] = 1;
73 
74  LOG(VB_RECORD, LOG_INFO,
75  QString("V4L2SH[%1]: Creating new stream handler for %2")
76  .arg(inputid).arg(devname));
77  }
78  else
79  {
80  s_handlers_refcnt[devkey]++;
81  uint rcount = s_handlers_refcnt[devkey];
82  LOG(VB_RECORD, LOG_INFO,
83  QString("V4L2SH[%1]: Using existing stream handler for %2")
84  .arg(inputid).arg(devkey) + QString(" (%1 in use)").arg(rcount));
85  }
86 
87  return s_handlers[devkey];
88 }
89 
91 {
92  QMutexLocker locker(&s_handlers_lock);
93 
94  QString devname = ref->m_device;
95 
96  QMap<QString,uint>::iterator rit = s_handlers_refcnt.find(devname);
97  if (rit == s_handlers_refcnt.end())
98  return;
99 
100  LOG(VB_RECORD, LOG_INFO, QString("V4L2SH[%1]: Return '%2' in use %3")
101  .arg(inputid).arg(devname).arg(*rit));
102 
103  if (*rit > 1)
104  {
105  ref = nullptr;
106  --(*rit);
107  return;
108  }
109 
110  QMap<QString, V4L2encStreamHandler*>::iterator it =
111  s_handlers.find(devname);
112  if ((it != s_handlers.end()) && (*it == ref))
113  {
114  LOG(VB_RECORD, LOG_INFO, QString("V4L2SH[%1]: Closing handler for %2")
115  .arg(inputid).arg(devname));
116  delete *it;
117  s_handlers.erase(it);
118  }
119  else
120  {
121  LOG(VB_GENERAL, LOG_ERR,
122  QString("V4L2SH[%1]: Error: Couldn't find handler for %2")
123  .arg(inputid).arg(devname));
124  }
125 
126  s_handlers_refcnt.erase(rit);
127  ref = nullptr;
128 }
129 
130 /*
131  V4L2encStreamHandler
132 */
133 bool V4L2encStreamHandler::Status(bool &failed, bool &failing)
134 {
135  failed = !IsRunning();
136  failing = m_failing;
137  return !failed && !failing;
138 }
139 
141  int audio_input, int inputid)
142  : StreamHandler(device, inputid)
143  , m_audioInput(audio_input)
144 {
145  setObjectName("V4L2encSH");
146 
147  if (!Open())
148  {
149  LOG(VB_GENERAL, LOG_ERR, LOC + QString("-- Failed to open %1: ")
150  .arg(m_device) + ENO);
151  m_bError = true;
152  return;
153  }
154  LOG(VB_RECORD, LOG_INFO, LOC + QString("'%1' open").arg(m_device));
155 }
156 
158 {
159  StopEncoding();
160  Close();
161 }
162 
164 {
165  RunProlog();
166 
167  LOG(VB_RECORD, LOG_INFO, LOC + "run() -- begin");
168 
169  if (!IsOpen())
170  {
171  LOG(VB_GENERAL, LOG_WARNING, LOC +
172  "Starting stream handler, but v4l2 is not open!");
173  if (!Open())
174  {
175  LOG(VB_GENERAL, LOG_ERR, LOC +
176  QString("run() -- Failed to open %1: ")
177  .arg(m_device) + ENO);
178  m_bError = true;
179  return;
180  }
181  }
182 
183 #if 0
184  // VBI
185  if (m_vbi_fd >= 0)
186  m_vbi_thread = new VBIThread(this);
187 #endif
188 
189  bool good_data = false;
190  bool gap = false;
191  QDateTime gap_start;
192 
193  QByteArray buffer;
194  char* pkt_buf = new char[PACKET_SIZE + 1];
195 
196  SetRunning(true, true, false);
197 
198  while (m_runningDesired && !m_bError)
199  {
200  // Get V4L2 data
201  if (m_streamingCnt.load() == 0)
202  {
203  LOG(VB_RECORD, LOG_INFO, LOC + "Waiting for stream start.");
204  QMutexLocker locker(&m_startStopLock);
206  continue;
207  }
208 
209  // Check for errors
210 
211  if (!m_drb)
212  break;
213 
214  int len = m_drb->Read(reinterpret_cast<unsigned char *>(pkt_buf),
215  PACKET_SIZE);
216  if (m_drb->IsErrored())
217  {
218  LOG(VB_GENERAL, LOG_ERR, LOC + "run() -- Device error detected");
219 
220  if (good_data)
221  {
222  if (gap)
223  {
224  /* Already processing a gap, which means
225  * restarting the encoding didn't work! */
226  m_failing = true;
227  }
228  else
229  gap = true;
230  }
231 
232  RestartEncoding();
233  }
234  else if (m_drb->IsEOF())
235  {
236  LOG(VB_GENERAL, LOG_ERR, LOC + "run() -- Device EOF detected");
237  m_bError = true;
238  }
239  else
240  {
241 #if 0 // For this to work, the data needs to be propagated back up to
242  // the 'recorder', but there could be multiple rcorders...
243 
244  // If we have seen good data, but now have a gap, note it
245  if (good_data)
246  {
247  if (gap)
248  {
249  QMutexLocker locker(&statisticsLock);
250  QDateTime gap_end(MythDate::current());
251 
252  for (Irec = m_rec_gaps.begin();
253  Irec != m_rec_caps.end(); ++Irec)
254  {
255  (*Irec).push_back(RecordingGap
256  (gap_start, gap_end));
257  }
258  LOG(VB_RECORD, LOG_DEBUG,
259  LOC + QString("Inserted gap %1 dur %2")
260  .arg(recordingGaps.back().toString())
261  .arg(gap_start.secsTo(gap_end)));
262  gap = false;
263  }
264  else
265  gap_start = MythDate::current();
266  }
267  else
268  good_data = true;
269 #else
270  good_data = true;
271 #endif
272  }
273 
274  if (len < 0)
275  {
276  if (errno != EAGAIN)
277  {
278  LOG(VB_GENERAL, LOG_ERR, LOC +
279  QString("run() -- error reading from: %1")
280  .arg(m_device) + ENO);
281  }
282  continue;
283  }
284 
285  buffer.append(pkt_buf, len);
286  len = buffer.size();
287 
288  if (len < static_cast<int>(TSPacket::kSize))
289  continue;
290 
291  if (!m_listenerLock.tryLock())
292  continue;
293 
294  if (m_streamDataList.empty())
295  {
296  LOG(VB_GENERAL, LOG_ERR, LOC +
297  QString("run() -- _stream_data_list is empty, %1 buffered")
298  .arg(buffer.size()));
299  m_listenerLock.unlock();
300  continue;
301  }
302 
303  int remainder = 0;
304  for (auto sit = m_streamDataList.cbegin(); sit != m_streamDataList.cend(); ++sit)
305  {
306  remainder = sit.key()->ProcessData
307  (reinterpret_cast<const uint8_t *>
308  (buffer.constData()), len);
309  }
310 
311  m_listenerLock.unlock();
312 
313  if (remainder > 0 && (len > remainder)) // leftover bytes
314  buffer.remove(0, len - remainder);
315  else
316  buffer.clear();
317  }
318 
319  QString tmp(m_error);
320  LOG(VB_GENERAL, LOG_WARNING, LOC +
321  QString("_running_desired(%1) _error(%2)")
322  .arg(m_runningDesired).arg(tmp));
323 
324  LOG(VB_RECORD, LOG_INFO, LOC + "run() -- finishing up");
325  StopEncoding();
326 
327  delete[] pkt_buf;
328 
329  SetRunning(false, true, false);
330  RunEpilog();
331 
332  LOG(VB_RECORD, LOG_INFO, LOC + "run() -- end");
333 }
334 
335 
337 {
338  LOG(VB_RECORD, LOG_INFO, LOC + "open() -- begin");
339 
340  if (IsOpen())
341  {
342  LOG(VB_RECORD, LOG_WARNING, LOC + "run() -- Already open.");
343  return true;
344  }
345  Close();
346 
347  QMutexLocker lock(&m_streamLock);
349  if (!m_v4l2.IsOpen())
350  {
351  m_error = QString("Open of '%1' failed: ").arg(m_device) + ENO;
352  LOG(VB_GENERAL, LOG_ERR, LOC + "Open() -- " + m_error);
353  return false;
354  }
355 
356  if (!m_v4l2.IsEncoder())
357  {
358  m_error = "V4L version 2 required";
359  LOG(VB_GENERAL, LOG_ERR, LOC + "Open() -- " + m_error);
360  m_v4l2.Close();
361  return false;
362  }
363 
364  if (m_drb)
365  {
366  if (m_drb->IsRunning())
367  m_drb->Stop();
368  delete m_drb;
369  m_drb = nullptr;
370  }
371 
372 
373  m_fd = open(m_device.toLatin1().constData(), O_RDWR | O_NONBLOCK);
374 
375  m_drb = new DeviceReadBuffer(this);
376  if (!m_drb)
377  {
378  m_error = "Failed to allocate DRB buffer";
379  LOG(VB_GENERAL, LOG_ERR, LOC + "Configure() -- " + m_error);
380  Close();
381  return false;
382  }
383 
384  m_drb->SetRequestPause(true);
385  if (!m_drb->Setup(m_device.toLatin1().constData(), m_fd))
386  {
387  m_error = "Failed to setup DRB buffer";
388  LOG(VB_GENERAL, LOG_ERR, LOC + "Configure() -- " + m_error);
389  Close();
390  return false;
391  }
392 
393  LOG(VB_RECORD, LOG_INFO, LOC + "open() -- done");
394  return true;
395 }
396 
398 {
399  if (m_streamingCnt.load() > 0)
400  {
401  LOG(VB_RECORD, LOG_INFO, LOC + "Configure() -- Already configured.");
402  return true;
403  }
404 
405  LOG(VB_RECORD, LOG_INFO, LOC + "Configure() -- begin");
406 
407  if (m_width > 0 && m_height > 0 &&
409  {
410  m_v4l2.Close();
411  LOG(VB_RECORD, LOG_ERR, LOC + "Configure() -- failed");
412  return false;
413  }
414 
415  if (m_v4l2.HasTuner())
416  SetLanguageMode(); // we don't care if this fails...
417 
418  if (m_v4l2.HasAudioSupport())
419  m_v4l2.SetVolume(m_audioVolume); // we don't care if this fails...
420 
421  if (m_desiredStreamType >= 0)
423 
424  LOG(VB_RECORD, LOG_INFO, LOC + QString("Options for %1")
426 
427  if (m_aspectRatio >= 0)
429 
430  if (m_bitrateMode < 0)
432  if (m_maxBitrate < 0 && m_highPeakBitrate > 0)
434  if (m_bitrate < 0 && m_highBitrate > 0)
436 
437  m_maxBitrate = std::max(m_maxBitrate, m_bitrate);
438 
439  if (m_bitrateMode >= 0)
441  if (m_bitrate > 0)
443  if (m_maxBitrate > 0)
445 
446  if (m_audioInput >= 0)
448  else
449  LOG(VB_CHANNEL, LOG_WARNING, "Audio input not set.");
450 
451  if (m_audioCodec >= 0)
453  if (m_audioSampleRate >= 0)
455  if (m_audioBitrateL2 >= 0)
457 
458  ConfigureVBI();
459 
460  LOG(VB_RECORD, LOG_INFO, LOC + "Configure() -- done");
461  return false;
462 }
463 
465 {
466 #if 0
467 // VBI
468  if (m_vbi_thread != nullptr)
469  {
470  m_vbi_thread->wait();
471  delete m_vbi_thread;
472  m_vbi_thread = nullptr;
473 
474  CloseVBIDevice();
475  }
476 #endif
477 
478  if (m_drb)
479  {
480  m_drb->Stop();
481  delete m_drb;
482  m_drb = nullptr;
483  }
484 
485  m_v4l2.Close();
486 
487  if (m_fd >= 0)
488  {
489  close(m_fd);
490  m_fd = -1;
491  LOG(VB_RECORD, LOG_INFO, LOC + "Closed.");
492  }
493 
494  m_streamingCnt.fetchAndStoreAcquire(0);
495 }
496 
498 {
499  LOG(VB_RECORD, LOG_INFO, LOC + "StartEncoding() -- begin");
500  int old_cnt = 0;
501 
502  QMutexLocker lock(&m_streamLock);
503 
504  if (!IsOpen())
505  {
506  LOG(VB_GENERAL, LOG_ERR, LOC + "V4L2 recorder not initialized.");
507  return false;
508  }
509 
510  if ((old_cnt = m_streamingCnt.load()) == 0)
511  {
512  // Start encoding
513 
514  LOG(VB_GENERAL, LOG_INFO, LOC +
515  QString("Streaming mode %1. Not using it.")
516  .arg(m_v4l2.HasStreaming() ? "available" : "not available"));
517 
519  {
520  for (int idx = 0; idx < 10; ++idx)
521  {
523  break;
524  }
525  }
526 
531 
532  // (at least) with the 3.10 kernel, the V4L2_ENC_CMD_START does
533  // not reliably start the data flow from a HD-PVR. A read() seems
534  // to work, though.
535 
536  int idx = 1;
537  for ( ; idx < 50; ++idx)
538  {
539  uint8_t dummy = 0;
540  int len = read(m_fd, &dummy, 0);
541  if (len >= 0)
542  {
543  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("StartEncoding read %1 bytes").arg(len));
544  break;
545  }
546  if (idx == 20)
547  {
548  LOG(VB_GENERAL, LOG_ERR, LOC +
549  "StartEncoding: read failing, re-opening device: " + ENO);
550  close(m_fd);
551  std::this_thread::sleep_for(std::chrono::milliseconds(2));
552  m_fd = open(m_device.toLatin1().constData(), O_RDWR | O_NONBLOCK);
553  if (m_fd < 0)
554  {
555  LOG(VB_GENERAL, LOG_ERR, LOC +
556  "StartEncoding: Can't open video device." + ENO);
557  m_error = "Failed to start recording";
558  return false;
559  }
560  }
561  else
562  {
563  LOG(VB_GENERAL, LOG_ERR, LOC +
564  QString("StartEncoding: read failed, retry in %1 msec:")
565  .arg(100 * idx) + ENO);
566  std::this_thread::sleep_for(std::chrono::microseconds(idx * 100));
567  }
568  }
569  if (idx == 50)
570  {
571  LOG(VB_GENERAL, LOG_ERR, LOC +
572  "StartEncoding: read from video device failed." + ENO);
573  m_error = "Failed to start recording";
574  close(m_fd);
575  m_fd = -1;
576  return false;
577  }
578  if (idx > 0)
579  {
580  LOG(VB_RECORD, LOG_WARNING, LOC +
581  QString("%1 read attempts required to start encoding").arg(idx));
582  }
583 
584  if (m_drb)
585  {
586  m_drb->SetRequestPause(false);
587  m_drb->Start();
588  }
589  }
590  else
591  LOG(VB_RECORD, LOG_INFO, LOC + "Already encoding");
592 
593  QMutexLocker listen_lock(&m_listenerLock);
594 
595  m_streamingCnt.ref();
596 
597  LOG(VB_RECORD, LOG_INFO, LOC +
598  QString("StartEncoding() -- %1->%2 listeners")
599  .arg(old_cnt).arg(m_streamingCnt.load()));
600 
601  LOG(VB_RECORD, LOG_INFO, LOC + "StartEncoding() -- end");
602 
603  return true;
604 }
605 
607 {
608  int old_cnt = m_streamingCnt.load();
609 
610  if (old_cnt == 0)
611  {
612  LOG(VB_RECORD, LOG_INFO, LOC + "StopEncoding: already stopped.");
613  return true;
614  }
615 
616  QMutexLocker lock(&m_streamLock);
617 
618  if (m_streamingCnt.deref())
619  {
620  LOG(VB_RECORD, LOG_INFO, LOC +
621  QString("StopEncoding() -- delayed, still have %1 listeners")
622  .arg(m_streamingCnt.load()));
623  return true;
624  }
625 
626  if (!IsOpen())
627  {
628  LOG(VB_GENERAL, LOG_ERR, LOC +
629  "StopEncoding() -- V4L2enc recorder not started.");
630  return false;
631  }
632 
633  // Stop encoding
634  if (m_drb)
635  m_drb->SetRequestPause(true);
636 
641 
642  // allow last bits of data through..
643  if (m_drb && m_drb->IsRunning())
644  std::this_thread::sleep_for(std::chrono::milliseconds(20));
645 #if 0
646  // close the fd so streamoff/streamon work in V4LChannel Close();
647  close(m_fd);
648 #endif
649 
650  LOG(VB_RECORD, LOG_INFO, LOC +
651  QString("StopEncoding() -- %1->%2 listeners")
652  .arg(old_cnt).arg(m_streamingCnt.load()));
653 
654  return true;
655 }
656 
658 {
659  LOG(VB_RECORD, LOG_INFO, LOC + "RestartEncoding()");
660 
661  StopEncoding();
662  StartEncoding();
663 }
664 
665 #if 0
667 {
668  // TODO report on buffer overruns, etc.
669 }
670 #endif
671 
674 {
675  if (m_langMode < 0)
676  return true;
677 
678  if (V4L2_TUNER_MODE_LANG1_LANG2 == m_langMode &&
679  V4L2_MPEG_AUDIO_ENCODING_LAYER_1 == m_audioLayer)
680  {
681  LOG(VB_GENERAL, LOG_WARNING, LOC +
682  "SetLanguageMode() -- Dual audio mode incompatible "
683  "with Layer I audio. Falling back to Main Language");
684  m_langMode = V4L2_TUNER_MODE_LANG1;
685  }
686 
688 }
689 
690 static int find_index(const int *audio_rate, int value)
691 {
692  for (uint i = 0; audio_rate[i] != 0; ++i)
693  {
694  if (audio_rate[i] == value)
695  return i;
696  }
697 
698  return -1;
699 }
700 
701 bool V4L2encStreamHandler::SetOption(const QString &opt, int value)
702 {
703  if (m_streamingCnt.load() > 0)
704  return true;
705 
706  if (opt == "width")
707  m_width = value;
708  else if (opt == "height")
709  m_height = value;
710  else if (opt == "mpeg2bitratemode")
711  {
712  m_bitrateMode = value ? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR :
713  V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
714  }
715  else if (opt == "mpeg2bitrate")
716  {
717  m_bitrate = value;
718  }
719  else if (opt == "mpeg2maxbitrate")
720  {
721  m_maxBitrate = value;
722  }
723  else if (opt == "samplerate")
724  {
725  switch (value)
726  {
727  case 32000:
728  m_audioSampleRate = V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000;
729  break;
730  case 44100:
731  m_audioSampleRate = V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100;
732  break;
733  case 48000:
734  default:
735  m_audioSampleRate = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
736  }
737  }
738  else if (opt == "mpeg2audbitratel1")
739  {
740  int index = find_index(kAudioRateL1, value);
741  if (index >= 0)
742  m_audioBitrateL1 = index;
743  else
744  {
745  LOG(VB_GENERAL, LOG_ERR, LOC + "Audiorate(L1): " +
746  QString("%1 is invalid").arg(value));
747  return true;
748  }
749  }
750  else if (opt == "mpeg2audbitratel2")
751  {
752  int index = find_index(kAudioRateL2, value);
753  if (index >= 0)
754  m_audioBitrateL2 = index;
755  else
756  {
757  LOG(VB_GENERAL, LOG_ERR, LOC + "Audiorate(L2): " +
758  QString("%1 is invalid").arg(value));
759  return true;
760  }
761  }
762  else if (opt == "mpeg2audbitratel3")
763  {
764  int index = find_index(kAudioRateL3, value);
765  if (index >= 0)
766  m_audioBitrateL3 = index;
767  else
768  {
769  LOG(VB_GENERAL, LOG_ERR, LOC + "Audiorate(L2): " +
770  QString("%1 is invalid").arg(value));
771  return true;
772  }
773  }
774  else if (opt == "mpeg2audvolume")
775  m_audioVolume = value;
776  else if (opt == "low_mpegbitratemode")
777  m_lowBitrateMode = value;
778  else if (opt == "medium_mpegbitratemode")
779  m_mediumBitrateMode = value;
780  else if (opt == "high_mpegbitratemode")
781  m_highBitrateMode = value;
782  else if (opt.endsWith("avgbitrate"))
783  {
784  if (opt.startsWith("low"))
785  m_lowBitrate = value;
786  else if (opt.startsWith("medium"))
787  m_mediumBitrate = value;
788  else if (opt.startsWith("high"))
789  m_highBitrate = value;
790  else
791  return false;
792  }
793  else if (opt.endsWith("peakbitrate"))
794  {
795  if (opt.startsWith("low"))
796  m_lowPeakBitrate = value;
797  else if (opt.startsWith("medium"))
798  m_mediumPeakBitrate = value;
799  else if (opt.startsWith("high"))
800  m_highPeakBitrate = value;
801  else
802  return false;
803  }
804  else
805  return false;
806 
807  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetOption('%1', %2) -- success")
808  .arg(opt).arg(value));
809  return true;
810 }
811 
813 {
814  if (m_streamType == -1)
816  return m_streamType;
817 }
818 
819 bool V4L2encStreamHandler::SetOption(const QString &opt, const QString &value)
820 {
821  if (m_streamingCnt.load() > 0)
822  return true;
823 
824  if (opt == "vbidevice")
825  m_vbiDevice = value;
826  else if (opt == "mpeg2streamtype")
827  {
828  for (size_t i = 0; i < sizeof(kStreamTypes) / sizeof(char*); ++i)
829  {
830  if (QString(kStreamTypes[i]) == value)
831  {
833  return true;
834  }
835  }
836  LOG(VB_GENERAL, LOG_ERR, LOC +
837  QString("MPEG2 stream type %1 is invalid ").arg(value));
838  return false;
839  }
840  else if (opt == "mpeg2language")
841  {
842  bool ok = false;
843  int lang_mode = value.toInt(&ok); // on failure language will be 0
844  if (!ok)
845  {
846  LOG(VB_GENERAL, LOG_ERR, LOC + "MPEG2 language (stereo) flag " +
847  QString("'%1' is invalid").arg(value));
848  return true;
849  }
850  switch (lang_mode)
851  {
852  case 1:
853  m_langMode = V4L2_TUNER_MODE_LANG2;
854  break;
855  case 2:
856  m_langMode = V4L2_TUNER_MODE_LANG1_LANG2;
857  break;
858  case 0:
859  default:
860  m_langMode = V4L2_TUNER_MODE_LANG1;
861  break;
862  }
863  }
864  else if (opt == "mpeg2aspectratio")
865  {
866  if (value == "Square")
867  m_aspectRatio = V4L2_MPEG_VIDEO_ASPECT_1x1;
868  else if (value == "4:3")
869  m_aspectRatio = V4L2_MPEG_VIDEO_ASPECT_4x3;
870  else if (value == "16:9")
871  m_aspectRatio = V4L2_MPEG_VIDEO_ASPECT_16x9; // NOLINT(bugprone-branch-clone)
872  else if (value == "2.21:1")
873  m_aspectRatio = V4L2_MPEG_VIDEO_ASPECT_221x100;
874  else
875  m_aspectRatio = V4L2_MPEG_VIDEO_ASPECT_16x9;
876  }
877  else if (opt == "mpeg2audtype")
878  {
879  if (value == "Layer I")
880  m_audioLayer = V4L2_MPEG_AUDIO_ENCODING_LAYER_1; // plus one?
881  else if (value == "Layer II")
882  m_audioLayer = V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
883  else if (value == "Layer III")
884  m_audioLayer = V4L2_MPEG_AUDIO_ENCODING_LAYER_3;
885  else
886  {
887  LOG(VB_GENERAL, LOG_ERR, LOC + "MPEG2 audio layer: " +
888  QString("%1 is invalid").arg(value));
889  }
890  }
891  else if (opt == "audiocodec")
892  {
893  if (value.startsWith("V4L2:"))
894  {
895  // Find the value in the options returns by the driver
897  (DriverOption::AUDIO_ENCODING, value.mid(5));
898  }
899  }
900  else
901  return false;
902 
903  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetOption('%1', '%2') -- success")
904  .arg(opt).arg(value));
905  return true;
906 }
907 
909 {
910  if (m_streamingCnt.load() > 0)
911  return true;
912 
913  return true;
914 }
915 
917 {
918  if (m_streamingCnt.load() > 0)
919  {
920  LOG(VB_RECORD, LOG_INFO, LOC + QString("GetSignalStrength() -- "
921  "returning cached value (%1)")
922  .arg(m_signalStrength));
923  return m_signalStrength;
924  }
925 
927  return m_signalStrength;
928 }
929 
930 void V4L2encStreamHandler::SetBitrate(int bitrate, int maxbitrate,
931  int bitratemode, const QString & reason)
932 {
933  if (maxbitrate == bitrate)
934  {
935  LOG(VB_RECORD, LOG_INFO, LOC +
936  QString("SetBitrate() -- %1 bitrate %2 kbps CBR")
937  .arg(reason).arg(bitrate));
938  }
939  else
940  {
941  LOG(VB_RECORD, LOG_INFO, LOC +
942  QString("SetBitrate() -- %1 bitrate %2/%3 kbps VBR")
943  .arg(reason).arg(bitrate).arg(maxbitrate));
944  }
945 
946  // Set VBR or CBR mode
947  if (bitratemode >= 0)
948  m_v4l2.SetVideoBitrateMode(bitratemode);
949 
950  // Set average bitrate
951  if (bitrate > 0)
952  m_v4l2.SetVideoBitrate(bitrate * 1000);
953 
954  // Set max bitrate
955  if (maxbitrate > 0)
956  m_v4l2.SetVideoBitratePeak(maxbitrate * 1000);
957 }
958 
960 {
961  m_width = m_height = -1;
962 
963  int width = 0;
964  int height = 0;
965  int idx = 0;
966  for ( ; idx < 10; ++idx)
967  {
968  if (m_v4l2.GetResolution(width, height))
969  break;
970  if (idx == 5)
971  {
972  m_v4l2.Close();
974  }
975  std::this_thread::sleep_for(std::chrono::microseconds(100));
976  }
977  if (idx == 5)
978  return false;
979 
980  m_width = width;
981  m_height = height;
982  int pix = width * height;
983 
984  int old_mode = m_bitrateMode;
985  int old_max = m_maxBitrate;
986  int old_avg = m_bitrate;
987 
988  if (m_lowBitrate > 0 && pix <= 768*1080)
989  {
993  }
994  else if (m_highBitrate > 0 && pix >= 1920*1080)
995  {
999  }
1000  else if (m_mediumBitrate > 0)
1001  {
1005  }
1006  m_maxBitrate = std::max(m_maxBitrate, m_bitrate);
1007 
1008  if ((old_max != m_maxBitrate) || (old_avg != m_bitrate) ||
1009  old_mode != m_bitrateMode)
1010  {
1011  if (old_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
1012  {
1013  LOG(VB_RECORD, LOG_INFO, LOC +
1014  QString("Old bitrate %1 CBR").arg(old_avg));
1015  }
1016  else
1017  {
1018  LOG(VB_RECORD, LOG_INFO,LOC +
1019  QString("Old bitrate %1/%2 VBR").arg(old_avg).arg(old_max));
1020  }
1021 
1023  }
1024 
1025  return true;
1026 }
1027 
1029 {
1030  LOG(VB_GENERAL, LOG_INFO, LOC + "ConfigureVBI() -- begin");
1031 
1032 
1033  LOG(VB_RECORD, LOG_INFO, LOC + "ConfigureVBI() -- end");
1034 
1035  return false;
1036 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
static const int kAudioRateL3[]
static QMap< QString, uint > s_handlers_refcnt
static QString StreamTypeDesc(int value)
bool IsRunning(void) const
bool Open(const QString &dev_name, const QString &vbi_dev_name="")
Definition: v4l2util.cpp:31
bool UserAdjustableResolution(void) const
bool SetVolume(int volume)
QWaitCondition m_runningStateChanged
static V4L2encStreamHandler * Get(const QString &devname, int audioinput, int inputid)
#define O_NONBLOCK
Definition: mythmedia.cpp:25
static const char * kStreamTypes[]
bool IsEOF(void) const
bool SetLanguageMode(int mode)
bool SetStreamType(int value)
bool GetResolution(int &width, int &height) const
bool SetAudioBitrateL2(int value)
static int find_index(const int *audio_rate, int value)
bool SetAudioInput(int value)
V4L2encStreamHandler(const QString &device, int audio_input, int inputid)
bool StopEncoding(void)
QString m_device
void PriorityEvent(int fd) override
Definition: streamhandler.h:96
static QMap< QString, V4L2encStreamHandler * > s_handlers
bool IsOpen(void) const
Definition: v4l2util.h:29
bool Status(bool &failed, bool &failing)
static const int kAudioRateL1[]
bool SetLanguageMode(void)
Set audio language mode.
static guint32 * tmp
Definition: goom_core.c:35
bool SetVideoAspect(int value)
void setObjectName(const QString &name)
Definition: mthread.cpp:249
int m_langMode
0 is Main Lang; 1 is SAP Lang; 2 is Dual
bool IsErrored(void) const
def read(device=None, features=[])
Definition: disc.py:35
QMutex m_startStopLock
bool SetAudioSamplingRate(int value)
uint Read(unsigned char *buf, uint count)
Try to Read count bytes from into buffer.
int GetStreamType(void) const
void SetRunning(bool running, bool using_buffering, bool using_section_reader)
int GetSignalStrength(void) const
#define close
Definition: compat.h:16
void Close(void)
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
bool SetResolution(uint32_t width, uint32_t height)
void SetRequestPause(bool request)
bool PauseEncoding(void)
QMutex m_listenerLock
static const int kAudioRateL2[]
unsigned int uint
Definition: compat.h:140
bool StartEncoding(void)
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
bool SetVideoBitratePeak(int value)
volatile bool m_runningDesired
void SetBitrate(int bitrate, int maxbitrate, int bitratemode, const QString &reason)
bool SetVideoBitrate(int value)
bool SetOption(const QString &opt, int value)
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
bool HasStreaming(void) const
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
#define LOC
~V4L2encStreamHandler(void) override
static const unsigned int kSize
Definition: tspacket.h:220
bool HasAudioSupport(void) const
bool Setup(const QString &streamName, int streamfd, uint readQuanta=sizeof(TSPacket), uint deviceBufferSize=0, uint deviceBufferCount=1)
bool HasTuner(void) const
bool SetVideoBitrateMode(int value)
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
volatile bool m_bError
Buffers reads from device files.
bool IsEncoder(void) const
static void Return(V4L2encStreamHandler *&ref, int inputid)
StreamDataList m_streamDataList
bool ResumeEncoding(void)
bool SetAudioCodec(int value)
int GetOptionValue(DriverOption::category_t cat, const QString &desc)
DeviceReadBuffer * m_drb
bool IsRunning(void) const