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  V4L2encStreamHandler *newhandler = new V4L2encStreamHandler(devname,
70  audioinput,
71  inputid);
72 
73  s_handlers[devkey] = newhandler;
74  s_handlers_refcnt[devkey] = 1;
75 
76  LOG(VB_RECORD, LOG_INFO,
77  QString("V4L2SH[%1]: Creating new stream handler for %2")
78  .arg(inputid).arg(devname));
79  }
80  else
81  {
82  s_handlers_refcnt[devkey]++;
83  uint rcount = s_handlers_refcnt[devkey];
84  LOG(VB_RECORD, LOG_INFO,
85  QString("V4L2SH[%1]: Using existing stream handler for %2")
86  .arg(inputid).arg(devkey) + QString(" (%1 in use)").arg(rcount));
87  }
88 
89  return s_handlers[devkey];
90 }
91 
93 {
94  QMutexLocker locker(&s_handlers_lock);
95 
96  QString devname = ref->m_device;
97 
98  QMap<QString,uint>::iterator rit = s_handlers_refcnt.find(devname);
99  if (rit == s_handlers_refcnt.end())
100  return;
101 
102  LOG(VB_RECORD, LOG_INFO, QString("V4L2SH[%1]: Return '%2' in use %3")
103  .arg(inputid).arg(devname).arg(*rit));
104 
105  if (*rit > 1)
106  {
107  ref = nullptr;
108  --(*rit);
109  return;
110  }
111 
112  QMap<QString, V4L2encStreamHandler*>::iterator it =
113  s_handlers.find(devname);
114  if ((it != s_handlers.end()) && (*it == ref))
115  {
116  LOG(VB_RECORD, LOG_INFO, QString("V4L2SH[%1]: Closing handler for %2")
117  .arg(inputid).arg(devname));
118  delete *it;
119  s_handlers.erase(it);
120  }
121  else
122  {
123  LOG(VB_GENERAL, LOG_ERR,
124  QString("V4L2SH[%1]: Error: Couldn't find handler for %2")
125  .arg(inputid).arg(devname));
126  }
127 
128  s_handlers_refcnt.erase(rit);
129  ref = nullptr;
130 }
131 
132 /*
133  V4L2encStreamHandler
134 */
135 bool V4L2encStreamHandler::Status(bool &failed, bool &failing)
136 {
137  failed = !IsRunning();
138  failing = m_failing;
139  return !failed && !failing;
140 }
141 
143  int audio_input, int inputid)
144  : StreamHandler(device, inputid)
145  , m_audio_input(audio_input)
146 {
147  setObjectName("V4L2encSH");
148 
149  if (!Open())
150  {
151  LOG(VB_GENERAL, LOG_ERR, LOC + QString("-- Failed to open %1: ")
152  .arg(m_device) + ENO);
153  m_bError = true;
154  return;
155  }
156  LOG(VB_RECORD, LOG_INFO, LOC + QString("'%1' open").arg(m_device));
157 }
158 
160 {
161  StopEncoding();
162  Close();
163 }
164 
166 {
167  RunProlog();
168 
169  LOG(VB_RECORD, LOG_INFO, LOC + "run() -- begin");
170 
171  if (!IsOpen())
172  {
173  LOG(VB_GENERAL, LOG_WARNING, LOC +
174  "Starting stream handler, but v4l2 is not open!");
175  if (!Open())
176  {
177  LOG(VB_GENERAL, LOG_ERR, LOC +
178  QString("run() -- Failed to open %1: ")
179  .arg(m_device) + ENO);
180  m_bError = true;
181  return;
182  }
183  }
184 
185 #if 0
186  // VBI
187  if (m_vbi_fd >= 0)
188  m_vbi_thread = new VBIThread(this);
189 #endif
190 
191  bool good_data = false;
192  bool gap = false;
193  QDateTime gap_start;
194 
195  int len, remainder = 0;
196 
197  QByteArray buffer;
198  char* pkt_buf = new char[PACKET_SIZE + 1];
199 
200  SetRunning(true, true, false);
201 
202  while (m_running_desired && !m_bError)
203  {
204  // Get V4L2 data
205  if (m_streaming_cnt.load() == 0)
206  {
207  LOG(VB_RECORD, LOG_INFO, LOC + "Waiting for stream start.");
208  QMutexLocker locker(&m_start_stop_lock);
210  continue;
211  }
212 
213  // Check for errors
214 
215  if (!m_drb)
216  break;
217 
218  len = m_drb->Read(reinterpret_cast<unsigned char *>(pkt_buf),
219  PACKET_SIZE);
220  if (m_drb->IsErrored())
221  {
222  LOG(VB_GENERAL, LOG_ERR, LOC + "run() -- Device error detected");
223 
224  if (good_data)
225  {
226  if (gap)
227  {
228  /* Already processing a gap, which means
229  * restarting the encoding didn't work! */
230  m_failing = true;
231  }
232  else
233  gap = true;
234  }
235 
236  RestartEncoding();
237  }
238  else if (m_drb->IsEOF())
239  {
240  LOG(VB_GENERAL, LOG_ERR, LOC + "run() -- Device EOF detected");
241  m_bError = true;
242  }
243  else
244  {
245 #if 0 // For this to work, the data needs to be propagated back up to
246  // the 'recorder', but there could be multiple rcorders...
247 
248  // If we have seen good data, but now have a gap, note it
249  if (good_data)
250  {
251  if (gap)
252  {
253  QMutexLocker locker(&statisticsLock);
254  QDateTime gap_end(MythDate::current());
255 
256  for (Irec = m_rec_gaps.begin();
257  Irec != m_rec_caps.end(); ++Irec)
258  {
259  (*Irec).push_back(RecordingGap
260  (gap_start, gap_end));
261  }
262  LOG(VB_RECORD, LOG_DEBUG,
263  LOC + QString("Inserted gap %1 dur %2")
264  .arg(recordingGaps.back().toString())
265  .arg(gap_start.secsTo(gap_end)));
266  gap = false;
267  }
268  else
269  gap_start = MythDate::current();
270  }
271  else
272  good_data = true;
273 #else
274  good_data = true;
275 #endif
276  }
277 
278  if (len < 0)
279  {
280  if (errno != EAGAIN)
281  LOG(VB_GENERAL, LOG_ERR, LOC +
282  QString("run() -- error reading from: %1")
283  .arg(m_device) + ENO);
284  continue;
285  }
286 
287  buffer.append(pkt_buf, len);
288  len = buffer.size();
289 
290  if (len < static_cast<int>(TSPacket::kSize))
291  continue;
292 
293  if (!m_listener_lock.tryLock())
294  continue;
295 
296  if (m_stream_data_list.empty())
297  {
298  LOG(VB_GENERAL, LOG_ERR, LOC +
299  QString("run() -- _stream_data_list is empty, %1 buffered")
300  .arg(buffer.size()));
301  m_listener_lock.unlock();
302  continue;
303  }
304 
305  StreamDataList::const_iterator sit = m_stream_data_list.begin();
306  for (; sit != m_stream_data_list.end(); ++sit)
307  remainder = sit.key()->ProcessData
308  (reinterpret_cast<const uint8_t *>
309  (buffer.constData()), len);
310 
311  m_listener_lock.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_running_desired).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_stream_lock);
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_streaming_cnt.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_audio_volume); // we don't care if this fails...
420 
421  if (m_desired_stream_type >= 0)
423 
424  LOG(VB_RECORD, LOG_INFO, LOC + QString("Options for %1")
426 
427  if (m_aspect_ratio >= 0)
429 
430  if (m_bitrate_mode < 0)
432  if (m_max_bitrate < 0 && m_high_peak_bitrate > 0)
434  if (m_bitrate < 0 && m_high_bitrate > 0)
436 
438 
439  if (m_bitrate_mode >= 0)
441  if (m_bitrate > 0)
443  if (m_max_bitrate > 0)
445 
446  if (m_audio_input >= 0)
448  else
449  LOG(VB_CHANNEL, LOG_WARNING, "Audio input not set.");
450 
451  if (m_audio_codec >= 0)
453  if (m_audio_samplerate >= 0)
455  if (m_audio_bitrateL2 >= 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_streaming_cnt.fetchAndStoreAcquire(0);
495 }
496 
498 {
499  LOG(VB_RECORD, LOG_INFO, LOC + "StartEncoding() -- begin");
500  int old_cnt;
501 
502  QMutexLocker lock(&m_stream_lock);
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_streaming_cnt.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;
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_listener_lock);
594 
595  m_streaming_cnt.ref();
596 
597  LOG(VB_RECORD, LOG_INFO, LOC +
598  QString("StartEncoding() -- %1->%2 listeners")
599  .arg(old_cnt).arg(m_streaming_cnt.load()));
600 
601  LOG(VB_RECORD, LOG_INFO, LOC + "StartEncoding() -- end");
602 
603  return true;
604 }
605 
607 {
608  int old_cnt = m_streaming_cnt.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_stream_lock);
617 
618  if (m_streaming_cnt.deref())
619  {
620  LOG(VB_RECORD, LOG_INFO, LOC +
621  QString("StopEncoding() -- delayed, still have %1 listeners")
622  .arg(m_streaming_cnt.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_streaming_cnt.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_lang_mode < 0)
676  return true;
677 
678  if (V4L2_TUNER_MODE_LANG1_LANG2 == m_lang_mode &&
679  V4L2_MPEG_AUDIO_ENCODING_LAYER_1 == m_audio_layer)
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_lang_mode = 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_streaming_cnt.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  m_bitrate_mode = value ? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR :
712  V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
713  else if (opt == "mpeg2bitrate")
714  m_bitrate = value;
715  else if (opt == "mpeg2maxbitrate")
716  m_max_bitrate = value;
717  else if (opt == "samplerate")
718  {
719  switch (value)
720  {
721  case 32000:
722  m_audio_samplerate = V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000;
723  break;
724  case 44100:
725  m_audio_samplerate = V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100;
726  break;
727  case 48000:
728  default:
729  m_audio_samplerate = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
730  }
731  }
732  else if (opt == "mpeg2audbitratel1")
733  {
734  int index = find_index(s_audio_rateL1, value);
735  if (index >= 0)
736  m_audio_bitrateL1 = index;
737  else
738  {
739  LOG(VB_GENERAL, LOG_ERR, LOC + "Audiorate(L1): " +
740  QString("%1 is invalid").arg(value));
741  return true;
742  }
743  }
744  else if (opt == "mpeg2audbitratel2")
745  {
746  int index = find_index(s_audio_rateL2, value);
747  if (index >= 0)
748  m_audio_bitrateL2 = index;
749  else
750  {
751  LOG(VB_GENERAL, LOG_ERR, LOC + "Audiorate(L2): " +
752  QString("%1 is invalid").arg(value));
753  return true;
754  }
755  }
756  else if (opt == "mpeg2audbitratel3")
757  {
758  int index = find_index(s_audio_rateL3, value);
759  if (index >= 0)
760  m_audio_bitrateL3 = index;
761  else
762  {
763  LOG(VB_GENERAL, LOG_ERR, LOC + "Audiorate(L2): " +
764  QString("%1 is invalid").arg(value));
765  return true;
766  }
767  }
768  else if (opt == "mpeg2audvolume")
769  m_audio_volume = value;
770  else if (opt == "low_mpegbitratemode")
771  m_low_bitrate_mode = value;
772  else if (opt == "medium_mpegbitratemode")
773  m_medium_bitrate_mode = value;
774  else if (opt == "high_mpegbitratemode")
775  m_high_bitrate_mode = value;
776  else if (opt.endsWith("avgbitrate"))
777  {
778  if (opt.startsWith("low"))
779  m_low_bitrate = value;
780  else if (opt.startsWith("medium"))
781  m_medium_bitrate = value;
782  else if (opt.startsWith("high"))
783  m_high_bitrate = value;
784  else
785  return false;
786  }
787  else if (opt.endsWith("peakbitrate"))
788  {
789  if (opt.startsWith("low"))
790  m_low_peak_bitrate = value;
791  else if (opt.startsWith("medium"))
792  m_medium_peak_bitrate = value;
793  else if (opt.startsWith("high"))
794  m_high_peak_bitrate = value;
795  else
796  return false;
797  }
798  else
799  return false;
800 
801  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetOption('%1', %2) -- success")
802  .arg(opt).arg(value));
803  return true;
804 }
805 
807 {
808  if (m_stream_type == -1)
810  return m_stream_type;
811 }
812 
813 bool V4L2encStreamHandler::SetOption(const QString &opt, const QString &value)
814 {
815  if (m_streaming_cnt.load() > 0)
816  return true;
817 
818  if (opt == "vbidevice")
819  m_vbi_device = value;
820  else if (opt == "mpeg2streamtype")
821  {
822  for (size_t i = 0; i < sizeof(s_stream_types) / sizeof(char*); ++i)
823  {
824  if (QString(s_stream_types[i]) == value)
825  {
827  return true;
828  }
829  }
830  LOG(VB_GENERAL, LOG_ERR, LOC +
831  QString("MPEG2 stream type %1 is invalid ").arg(value));
832  return false;
833  }
834  else if (opt == "mpeg2language")
835  {
836  bool ok = false;
837  int lang_mode = value.toInt(&ok); // on failure language will be 0
838  if (!ok)
839  {
840  LOG(VB_GENERAL, LOG_ERR, LOC + "MPEG2 language (stereo) flag " +
841  QString("'%1' is invalid").arg(value));
842  return true;
843  }
844  switch (lang_mode)
845  {
846  case 1:
847  m_lang_mode = V4L2_TUNER_MODE_LANG2;
848  break;
849  case 2:
850  m_lang_mode = V4L2_TUNER_MODE_LANG1_LANG2;
851  break;
852  case 0:
853  default:
854  m_lang_mode = V4L2_TUNER_MODE_LANG1;
855  break;
856  }
857  }
858  else if (opt == "mpeg2aspectratio")
859  {
860  if (value == "Square")
861  m_aspect_ratio = V4L2_MPEG_VIDEO_ASPECT_1x1;
862  else if (value == "4:3")
863  m_aspect_ratio = V4L2_MPEG_VIDEO_ASPECT_4x3;
864  else if (value == "16:9")
865  m_aspect_ratio = V4L2_MPEG_VIDEO_ASPECT_16x9;
866  else if (value == "2.21:1")
867  m_aspect_ratio = V4L2_MPEG_VIDEO_ASPECT_221x100;
868  else
869  m_aspect_ratio = V4L2_MPEG_VIDEO_ASPECT_16x9;
870  }
871  else if (opt == "mpeg2audtype")
872  {
873  if (value == "Layer I")
874  m_audio_layer = V4L2_MPEG_AUDIO_ENCODING_LAYER_1; // plus one?
875  else if (value == "Layer II")
876  m_audio_layer = V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
877  else if (value == "Layer III")
878  m_audio_layer = V4L2_MPEG_AUDIO_ENCODING_LAYER_3;
879  else
880  {
881  LOG(VB_GENERAL, LOG_ERR, LOC + "MPEG2 audio layer: " +
882  QString("%1 is invalid").arg(value));
883  }
884  }
885  else if (opt == "audiocodec")
886  {
887  if (value.startsWith("V4L2:"))
888  {
889  // Find the value in the options returns by the driver
891  (DriverOption::AUDIO_ENCODING, value.mid(5));
892  }
893  }
894  else
895  return false;
896 
897  LOG(VB_RECORD, LOG_INFO, LOC + QString("SetOption('%1', '%2') -- success")
898  .arg(opt).arg(value));
899  return true;
900 }
901 
903 {
904  if (m_streaming_cnt.load() > 0)
905  return true;
906 
907  return true;
908 }
909 
911 {
912  if (m_streaming_cnt.load() > 0)
913  {
914  LOG(VB_RECORD, LOG_INFO, LOC + QString("GetSignalStrength() -- "
915  "returning cached value (%1)")
916  .arg(m_signal_strength));
917  return m_signal_strength;
918  }
919 
921  return m_signal_strength;
922 }
923 
924 void V4L2encStreamHandler::SetBitrate(int bitrate, int maxbitrate,
925  int bitratemode, const QString & reason)
926 {
927  if (maxbitrate == bitrate)
928  {
929  LOG(VB_RECORD, LOG_INFO, LOC +
930  QString("SetBitrate() -- %1 bitrate %2 kbps CBR")
931  .arg(reason).arg(bitrate));
932  }
933  else
934  {
935  LOG(VB_RECORD, LOG_INFO, LOC +
936  QString("SetBitrate() -- %1 bitrate %2/%3 kbps VBR")
937  .arg(reason).arg(bitrate).arg(maxbitrate));
938  }
939 
940  // Set VBR or CBR mode
941  if (bitratemode >= 0)
942  m_v4l2.SetVideoBitrateMode(bitratemode);
943 
944  // Set average bitrate
945  if (bitrate > 0)
946  m_v4l2.SetVideoBitrate(bitrate * 1000);
947 
948  // Set max bitrate
949  if (maxbitrate > 0)
950  m_v4l2.SetVideoBitratePeak(maxbitrate * 1000);
951 }
952 
954 {
955  m_width = m_height = -1;
956 
957  int width, height, pix;
958  int idx = 0;
959  for ( ; idx < 10; ++idx)
960  {
961  if (m_v4l2.GetResolution(width, height))
962  break;
963  if (idx == 5)
964  {
965  m_v4l2.Close();
967  }
968  std::this_thread::sleep_for(std::chrono::microseconds(100));
969  }
970  if (idx == 5)
971  return false;
972 
973  m_width = width;
974  m_height = height;
975  pix = width * height;
976 
977  int old_mode = m_bitrate_mode;
978  int old_max = m_max_bitrate;
979  int old_avg = m_bitrate;
980 
981  if (m_low_bitrate > 0 && pix <= 768*1080)
982  {
986  }
987  else if (m_high_bitrate > 0 && pix >= 1920*1080)
988  {
992  }
993  else if (m_medium_bitrate > 0)
994  {
998  }
1000 
1001  if ((old_max != m_max_bitrate) || (old_avg != m_bitrate) ||
1002  old_mode != m_bitrate_mode)
1003  {
1004  if (old_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
1005  {
1006  LOG(VB_RECORD, LOG_INFO, LOC +
1007  QString("Old bitrate %1 CBR").arg(old_avg));
1008  }
1009  else
1010  {
1011  LOG(VB_RECORD, LOG_INFO,LOC +
1012  QString("Old bitrate %1/%2 VBR").arg(old_avg).arg(old_max));
1013  }
1014 
1016  }
1017 
1018  return true;
1019 }
1020 
1022 {
1023  LOG(VB_GENERAL, LOG_INFO, LOC + "ConfigureVBI() -- begin");
1024 
1025 
1026  LOG(VB_RECORD, LOG_INFO, LOC + "ConfigureVBI() -- end");
1027 
1028  return false;
1029 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
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)
static V4L2encStreamHandler * Get(const QString &devname, int audioinput, int inputid)
#define O_NONBLOCK
Definition: mythmedia.cpp:25
bool IsEOF(void) const
bool SetLanguageMode(int mode)
bool SetStreamType(int value)
bool GetResolution(int &width, int &height) const
bool SetAudioBitrateL2(int value)
volatile bool m_running_desired
static int find_index(const int *audio_rate, int value)
QWaitCondition m_running_state_changed
bool SetAudioInput(int value)
V4L2encStreamHandler(const QString &device, int audio_input, int inputid)
bool StopEncoding(void)
QString m_device
static const char * s_stream_types[]
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)
unsigned int uint
Definition: compat.h:140
QMutex m_listener_lock
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
bool IsErrored(void) const
static const int s_audio_rateL1[]
def read(device=None, features=[])
Definition: disc.py:35
bool SetAudioSamplingRate(int value)
int m_lang_mode
0 is Main Lang; 1 is SAP Lang; 2 is Dual
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)
static const int s_audio_rateL2[]
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)
bool StartEncoding(void)
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
static const int s_audio_rateL3[]
bool SetVideoBitratePeak(int value)
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
static const unsigned int kSize
Definition: tspacket.h:220
bool HasAudioSupport(void) const
StreamDataList m_stream_data_list
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.
QMutex m_start_stop_lock
bool IsEncoder(void) const
static void Return(V4L2encStreamHandler *&ref, int inputid)
bool ResumeEncoding(void)
bool SetAudioCodec(int value)
int GetOptionValue(DriverOption::category_t cat, const QString &desc)
DeviceReadBuffer * m_drb
bool IsRunning(void) const