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