MythTV master
v4lrecorder.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
2
3#include "libmythbase/mythconfig.h"
4
5#if CONFIG_V4L2
6#include <linux/videodev2.h>
7#endif // CONFIG_V4L2
8
9#include <sys/ioctl.h> // for ioctl
10#include <sys/time.h> // for gettimeofday
11#include <unistd.h> // for IO_NONBLOCK
12#include <fcntl.h> // for IO_NONBLOCK
13
15
17#include "v4lrecorder.h"
18#include "tv_rec.h"
19
20#define TVREC_CARDNUM \
21 ((m_tvrec != nullptr) ? QString::number(m_tvrec->GetInputId()) : "NULL")
22
23#define LOC QString("V4LRec[%1](%2): ") \
24 .arg(TVREC_CARDNUM, m_videodevice)
25
27{
28 {
29 QMutexLocker locker(&m_pauseLock);
30 m_requestHelper = false;
31 m_unpauseWait.wakeAll();
32 }
33
34 if (m_vbiThread)
35 {
37 delete m_vbiThread;
38 m_vbiThread = nullptr;
40 }
41}
42
44{
48}
49
51{
52 QMutexLocker locker(&m_pauseLock);
54}
55
56void V4LRecorder::SetOption(const QString &name, const QString &value)
57{
58 if (name == "audiodevice")
59 m_audioDeviceName = value;
60 else if (name == "vbidevice")
61 m_vbiDeviceName = value;
62 else if (name == "vbiformat")
64 else
65 DTVRecorder::SetOption(name, value);
66}
67
69{
70 int fd = -1;
71 if (m_vbiFd >= 0)
72 return m_vbiFd;
73
74 uint width = 0;
75 uint start_line = 0;
76 uint line_count = 0;
77
78 QByteArray vbidev = m_vbiDeviceName.toLatin1();
80 {
81 fd = open(vbidev.constData(), O_RDONLY/*|O_NONBLOCK*/);
82 }
83 else
84 {
85 LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid CC/Teletext mode");
86 return -1;
87 }
88
89 if (fd < 0)
90 {
91 LOG(VB_GENERAL, LOG_ERR, LOC +
92 QString("Can't open vbi device: '%1'").arg(m_vbiDeviceName));
93 return -1;
94 }
95
97 {
98#if CONFIG_V4L2
99 struct v4l2_format fmt {};
100 fmt.type = V4L2_BUF_TYPE_VBI_CAPTURE;
101 if (0 != ioctl(fd, VIDIOC_G_FMT, &fmt))
102 {
103 LOG(VB_RECORD, LOG_ERR, "V4L2 VBI setup failed");
104 close(fd);
105 return -1;
106 }
107 LOG(VB_RECORD, LOG_INFO, LOC +
108 QString("vbi_format rate: %1"
109 "\n\t\t\t offset: %2"
110 "\n\t\t\tsamples_per_line: %3"
111 "\n\t\t\t starts: %4, %5"
112 "\n\t\t\t counts: %6, %7"
113 "\n\t\t\t flags: 0x%8")
114 .arg(fmt.fmt.vbi.sampling_rate)
115 .arg(fmt.fmt.vbi.offset)
116 .arg(fmt.fmt.vbi.samples_per_line)
117 .arg(fmt.fmt.vbi.start[0])
118 .arg(fmt.fmt.vbi.start[1])
119 .arg(fmt.fmt.vbi.count[0])
120 .arg(fmt.fmt.vbi.count[1])
121 .arg(fmt.fmt.vbi.flags,0,16));
122
123 width = fmt.fmt.vbi.samples_per_line;
124 start_line = fmt.fmt.vbi.start[0];
125 line_count = fmt.fmt.vbi.count[0];
126 if (line_count != fmt.fmt.vbi.count[1])
127 {
128 LOG(VB_GENERAL, LOG_ERR, LOC +
129 "VBI must have the same number of "
130 "odd and even fields for our decoder");
131 close(fd);
132 return -1;
133 }
134 if (start_line > 21 || start_line + line_count < 22)
135 {
136 LOG(VB_GENERAL, LOG_ERR, LOC + "VBI does not include line 21");
137 // TODO We could try to set the VBI format ourselves..
138 close(fd);
139 return -1;
140 }
141#endif // CONFIG_V4L2
142 }
143
145 {
146 m_ntscVbiWidth = width;
147 m_ntscVbiStartLine = start_line;
148 m_ntscVbiLineCount = line_count;
150 }
151
152 m_vbiFd = fd;
153
154 return fd;
155}
156
158{
159 if (m_vbiFd < 0)
160 return;
161
162 {
163 delete m_vbi608; m_vbi608 = nullptr;
164 close(m_vbiFd);
165 }
166
167 m_vbiFd = -1;
168}
169
171{
172 if (m_vbiFd < 0)
173 return;
174
175 unsigned char *buf = nullptr;
176 unsigned char *ptr = nullptr;
177 unsigned char *ptr_end = nullptr;
178 if (m_ntscVbiWidth)
179 {
181 buf = ptr = new unsigned char[sz];
182 ptr_end = buf + sz;
183 }
184
185 while (IsHelperRequested() && !IsErrored())
186 {
187 if (PauseAndWait())
188 continue;
189
190 if (!IsHelperRequested() || IsErrored())
191 break;
192
193 struct timeval tv {0, 5000};
194 fd_set rdset;
195
196 FD_ZERO(&rdset); // NOLINT(readability-isolate-declaration)
197 FD_SET(m_vbiFd, &rdset);
198
199 int nr = select(m_vbiFd + 1, &rdset, nullptr, nullptr, &tv);
200 if (nr < 0)
201 LOG(VB_GENERAL, LOG_ERR, LOC + "vbi select failed" + ENO);
202
203 if (nr <= 0)
204 {
205 if (nr==0)
206 LOG(VB_GENERAL, LOG_DEBUG, LOC + "vbi select timed out");
207 continue; // either failed or timed out..
208 }
210 {
211 int ret = read(m_vbiFd, ptr, ptr_end - ptr);
212 ptr = (ret > 0) ? ptr + ret : ptr;
213 if ((ptr_end - ptr) == 0)
214 {
215 unsigned char *line21_field1 =
216 buf + ((21 - m_ntscVbiStartLine) * static_cast<size_t>(m_ntscVbiWidth));
217 unsigned char *line21_field2 =
219 * static_cast<size_t>(m_ntscVbiWidth));
220 bool cc1 = m_vbi608->ExtractCC12(line21_field1, m_ntscVbiWidth);
221 bool cc2 = m_vbi608->ExtractCC34(line21_field2, m_ntscVbiWidth);
222 if (cc1 || cc2)
223 {
224 int code1 = m_vbi608->GetCode1();
225 int code2 = m_vbi608->GetCode2();
226 code1 = (0xFFFF==code1) ? -1 : code1;
227 code2 = (0xFFFF==code2) ? -1 : code2;
228 FormatCC(code1, code2);
229 }
230 ptr = buf;
231 }
232 else if (ret < 0)
233 {
234 LOG(VB_GENERAL, LOG_ERR, LOC + "Reading VBI data" + ENO);
235 }
236 }
237 }
238
239 delete [] buf;
240}
241
242/* vim: set expandtab tabstop=4 shiftwidth=4: */
bool IsErrored(void) override
Tells us whether an unrecoverable error has been encountered.
Definition: dtvrecorder.h:46
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: dtvrecorder.cpp:92
bool isRunning(void) const
Definition: mthread.cpp:263
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
QMutex m_pauseLock
Definition: recorderbase.h:313
virtual void StopRecording(void)
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
virtual bool PauseAndWait(std::chrono::milliseconds timeout=100ms)
If m_requestPause is true, sets pause and blocks up to timeout milliseconds or until unpaused,...
bool m_requestRecording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:319
QWaitCondition m_unpauseWait
Definition: recorderbase.h:317
~V4LRecorder() override
Definition: v4lrecorder.cpp:26
uint m_ntscVbiLineCount
Definition: v4lrecorder.h:42
virtual bool IsHelperRequested(void) const
Definition: v4lrecorder.cpp:50
VBIThread * m_vbiThread
Definition: v4lrecorder.h:44
uint m_ntscVbiWidth
Definition: v4lrecorder.h:40
int OpenVBIDevice(void)
Definition: v4lrecorder.cpp:68
VBI608Extractor * m_vbi608
Definition: v4lrecorder.h:43
void CloseVBIDevice(void)
void StopRecording(void) override
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
Definition: v4lrecorder.cpp:43
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: v4lrecorder.cpp:56
void RunVBIDevice(void)
uint m_ntscVbiStartLine
Definition: v4lrecorder.h:41
QString m_vbiDeviceName
Definition: v4lrecorder.h:38
virtual void FormatCC(uint, uint)
Definition: v4lrecorder.h:34
volatile bool m_requestHelper
Definition: v4lrecorder.h:46
QString m_audioDeviceName
Definition: v4lrecorder.h:37
bool ExtractCC34(const unsigned char *buf, uint width)
uint16_t GetCode1(void) const
uint16_t GetCode2(void) const
bool ExtractCC12(const unsigned char *buf, uint width)
static uint Parse(const QString &vbiformat)
Definition: tv.h:17
@ NTSC_CC
Definition: tv.h:14
#define close
Definition: compat.h:39
unsigned int uint
Definition: freesurround.h:24
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
def read(device=None, features=[])
Definition: disc.py:35
#define LOC
Definition: v4lrecorder.cpp:23