MythTV  master
v4lrecorder.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 
3 #ifdef USING_V4L1
4 #include <linux/videodev.h> // for vbi_format
5 #endif // USING_V4L1
6 
7 #ifdef USING_V4L2
8 #include <linux/videodev2.h>
9 #endif // USING_V4L2
10 
11 #include <sys/ioctl.h> // for ioctl
12 #include <sys/time.h> // for gettimeofday
13 #include <unistd.h> // for IO_NONBLOCK
14 #include <fcntl.h> // for IO_NONBLOCK
15 
16 #include "vbi608extractor.h"
17 #include "mythcontext.h"
18 #include "mythlogging.h"
19 #include "v4lrecorder.h"
20 #include "vbitext/vbi.h"
21 #include "tv_rec.h"
22 
23 #define TVREC_CARDNUM \
24  ((m_tvrec != nullptr) ? QString::number(m_tvrec->GetInputId()) : "NULL")
25 
26 #define LOC QString("V4LRec[%1](%2): ") \
27  .arg(TVREC_CARDNUM).arg(m_videodevice)
28 
30 {
31  {
32  QMutexLocker locker(&m_pauseLock);
33  m_requestHelper = false;
34  m_unpauseWait.wakeAll();
35  }
36 
37  if (m_vbiThread)
38  {
39  m_vbiThread->wait();
40  delete m_vbiThread;
41  m_vbiThread = nullptr;
43  }
44 }
45 
47 {
49  while (m_vbiThread && m_vbiThread->isRunning())
50  m_vbiThread->wait();
51 }
52 
54 {
55  QMutexLocker locker(&m_pauseLock);
57 }
58 
59 void V4LRecorder::SetOption(const QString &name, const QString &value)
60 {
61  if (name == "audiodevice")
62  m_audioDeviceName = value;
63  else if (name == "vbidevice")
64  m_vbiDeviceName = value;
65  else if (name == "vbiformat")
66  m_vbiMode = VBIMode::Parse(value);
67  else
69 }
70 
71 static void vbi_event(struct VBIData *data, struct vt_event *ev)
72 {
73  switch (ev->type)
74  {
75  case EV_PAGE:
76  {
77  auto *vtp = (struct vt_page *) ev->p1;
78  if (vtp->flags & PG_SUBTITLE)
79  {
80 #if 0
81  LOG(VB_GENERAL, LOG_DEBUG, QString("subtitle page %1.%2")
82  .arg(vtp->pgno, 0, 16) .arg(vtp->subno, 0, 16));
83 #endif
84  data->foundteletextpage = true;
85  memcpy(&(data->teletextpage), vtp, sizeof(vt_page));
86  }
87  }
88  break;
89 
90  case EV_HEADER:
91  case EV_XPACKET:
92  break;
93  }
94 }
95 
97 {
98  int fd = -1;
99  if (m_vbiFd >= 0)
100  return m_vbiFd;
101 
102  struct VBIData *vbi_cb = nullptr;
103  struct vbi *pal_tt = nullptr;
104  uint width = 0;
105  uint start_line = 0;
106  uint line_count = 0;
107 
108  QByteArray vbidev = m_vbiDeviceName.toLatin1();
109  if (VBIMode::PAL_TT == m_vbiMode)
110  {
111  pal_tt = vbi_open(vbidev.constData(), nullptr, 99, -1);
112  if (pal_tt)
113  {
114  fd = pal_tt->fd;
115  vbi_cb = new VBIData;
116  memset(vbi_cb, 0, sizeof(VBIData));
117  vbi_cb->nvr = this;
118  vbi_add_handler(pal_tt, (void*) vbi_event, vbi_cb);
119  }
120  }
121  else if (VBIMode::NTSC_CC == m_vbiMode)
122  {
123  fd = open(vbidev.constData(), O_RDONLY/*|O_NONBLOCK*/);
124  }
125  else
126  {
127  LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid CC/Teletext mode");
128  return -1;
129  }
130 
131  if (fd < 0)
132  {
133  LOG(VB_GENERAL, LOG_ERR, LOC +
134  QString("Can't open vbi device: '%1'").arg(m_vbiDeviceName));
135  return -1;
136  }
137 
139  {
140 #ifdef USING_V4L2
141  struct v4l2_format fmt {};
142  fmt.type = V4L2_BUF_TYPE_VBI_CAPTURE;
143  if (0 != ioctl(fd, VIDIOC_G_FMT, &fmt))
144  {
145 #ifdef USING_V4L1
146  LOG(VB_RECORD, LOG_INFO, "V4L2 VBI setup failed, trying v1 ioctl");
147  // Try V4L v1 VBI ioctls, iff V4L v2 fails
148  struct vbi_format old_fmt;
149  memset(&old_fmt, 0, sizeof(vbi_format));
150  if (ioctl(fd, VIDIOCGVBIFMT, &old_fmt) < 0)
151  {
152  LOG(VB_GENERAL, LOG_ERR, LOC +
153  "Failed to query vbi capabilities (V4L1)");
154  close(fd);
155  return -1;
156  }
157  fmt.fmt.vbi.sampling_rate = old_fmt.sampling_rate;
158  fmt.fmt.vbi.offset = 0;
159  fmt.fmt.vbi.samples_per_line = old_fmt.samples_per_line;
160  fmt.fmt.vbi.start[0] = old_fmt.start[0];
161  fmt.fmt.vbi.start[1] = old_fmt.start[1];
162  fmt.fmt.vbi.count[0] = old_fmt.count[0];
163  fmt.fmt.vbi.count[1] = old_fmt.count[1];
164  fmt.fmt.vbi.flags = old_fmt.flags;
165 #else // if !USING_V4L1
166  LOG(VB_RECORD, LOG_ERR, "V4L2 VBI setup failed");
167  close(fd);
168  return -1;
169 #endif // !USING_V4L1
170  }
171  LOG(VB_RECORD, LOG_INFO, LOC +
172  QString("vbi_format rate: %1"
173  "\n\t\t\t offset: %2"
174  "\n\t\t\tsamples_per_line: %3"
175  "\n\t\t\t starts: %4, %5"
176  "\n\t\t\t counts: %6, %7"
177  "\n\t\t\t flags: 0x%8")
178  .arg(fmt.fmt.vbi.sampling_rate)
179  .arg(fmt.fmt.vbi.offset)
180  .arg(fmt.fmt.vbi.samples_per_line)
181  .arg(fmt.fmt.vbi.start[0])
182  .arg(fmt.fmt.vbi.start[1])
183  .arg(fmt.fmt.vbi.count[0])
184  .arg(fmt.fmt.vbi.count[1])
185  .arg(fmt.fmt.vbi.flags,0,16));
186 
187  width = fmt.fmt.vbi.samples_per_line;
188  start_line = fmt.fmt.vbi.start[0];
189  line_count = fmt.fmt.vbi.count[0];
190  if (line_count != fmt.fmt.vbi.count[1])
191  {
192  LOG(VB_GENERAL, LOG_ERR, LOC +
193  "VBI must have the same number of "
194  "odd and even fields for our decoder");
195  close(fd);
196  return -1;
197  }
198  if (start_line > 21 || start_line + line_count < 22)
199  {
200  LOG(VB_GENERAL, LOG_ERR, LOC + "VBI does not include line 21");
201  // TODO We could try to set the VBI format ourselves..
202  close(fd);
203  return -1;
204  }
205 #endif // USING_V4L2
206  }
207 
208  if (VBIMode::PAL_TT == m_vbiMode)
209  {
210  m_palVbiCb = vbi_cb;
211  m_palVbiTt = pal_tt;
212  }
213  else if (VBIMode::NTSC_CC == m_vbiMode)
214  {
215  m_ntscVbiWidth = width;
216  m_ntscVbiStartLine = start_line;
217  m_ntscVbiLineCount = line_count;
218  m_vbi608 = new VBI608Extractor();
219  }
220 
221  m_vbiFd = fd;
222 
223  return fd;
224 }
225 
227 {
228  if (m_vbiFd < 0)
229  return;
230 
231  if (m_palVbiTt)
232  {
235  delete m_palVbiCb;
236  m_palVbiCb = nullptr;
237  }
238  else
239  {
240  delete m_vbi608; m_vbi608 = nullptr;
241  close(m_vbiFd);
242  }
243 
244  m_vbiFd = -1;
245 }
246 
248 {
249  if (m_vbiFd < 0)
250  return;
251 
252  unsigned char *buf = nullptr;
253  unsigned char *ptr = nullptr;
254  unsigned char *ptr_end = nullptr;
255  if (m_ntscVbiWidth)
256  {
258  buf = ptr = new unsigned char[sz];
259  ptr_end = buf + sz;
260  }
261 
262  while (IsHelperRequested() && !IsErrored())
263  {
264  if (PauseAndWait())
265  continue;
266 
267  if (!IsHelperRequested() || IsErrored())
268  break;
269 
270  struct timeval tv {0, 5000};
271  fd_set rdset;
272 
273  FD_ZERO(&rdset); // NOLINT(readability-isolate-declaration)
274  FD_SET(m_vbiFd, &rdset);
275 
276  int nr = select(m_vbiFd + 1, &rdset, nullptr, nullptr, &tv);
277  if (nr < 0)
278  LOG(VB_GENERAL, LOG_ERR, LOC + "vbi select failed" + ENO);
279 
280  if (nr <= 0)
281  {
282  if (nr==0)
283  LOG(VB_GENERAL, LOG_DEBUG, LOC + "vbi select timed out");
284  continue; // either failed or timed out..
285  }
286  if (VBIMode::PAL_TT == m_vbiMode)
287  {
288  m_palVbiCb->foundteletextpage = false;
291  {
292  // decode VBI as teletext subtitles
294  }
295  }
296  else if (VBIMode::NTSC_CC == m_vbiMode)
297  {
298  int ret = read(m_vbiFd, ptr, ptr_end - ptr);
299  ptr = (ret > 0) ? ptr + ret : ptr;
300  if ((ptr_end - ptr) == 0)
301  {
302  unsigned char *line21_field1 =
303  buf + ((21 - m_ntscVbiStartLine) * m_ntscVbiWidth);
304  unsigned char *line21_field2 =
305  buf + ((m_ntscVbiLineCount + 21 - m_ntscVbiStartLine)
306  * m_ntscVbiWidth);
307  bool cc1 = m_vbi608->ExtractCC12(line21_field1, m_ntscVbiWidth);
308  bool cc2 = m_vbi608->ExtractCC34(line21_field2, m_ntscVbiWidth);
309  if (cc1 || cc2)
310  {
311  int code1 = m_vbi608->GetCode1();
312  int code2 = m_vbi608->GetCode2();
313  code1 = (0xFFFF==code1) ? -1 : code1;
314  code2 = (0xFFFF==code2) ? -1 : code2;
315  FormatCC(code1, code2);
316  }
317  ptr = buf;
318  }
319  else if (ret < 0)
320  {
321  LOG(VB_GENERAL, LOG_ERR, LOC + "Reading VBI data" + ENO);
322  }
323  }
324  }
325 
326  delete [] buf;
327 }
328 
329 /* vim: set expandtab tabstop=4 shiftwidth=4: */
QString m_audioDeviceName
Definition: v4lrecorder.h:46
void RunVBIDevice(void)
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: v4lrecorder.cpp:59
#define EV_PAGE
Definition: vt.h:20
QMutex m_pauseLock
Definition: recorderbase.h:326
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
~V4LRecorder() override
Definition: v4lrecorder.cpp:29
#define EV_HEADER
Definition: vt.h:21
void CloseVBIDevice(void)
bool IsErrored(void) override
Tells us whether an unrecoverable error has been encountered.
Definition: dtvrecorder.h:47
virtual bool PauseAndWait(int timeout=100)
If m_requestPause is true, sets pause and blocks up to timeout milliseconds or until unpaused,...
uint m_ntscVbiStartLine
Definition: v4lrecorder.h:52
virtual bool IsHelperRequested(void) const
Definition: v4lrecorder.cpp:53
VBIThread * m_vbiThread
Definition: v4lrecorder.h:55
uint16_t GetCode1(void) const
def read(device=None, features=[])
Definition: disc.py:35
Definition: vt.h:36
QWaitCondition m_unpauseWait
Definition: recorderbase.h:330
int OpenVBIDevice(void)
Definition: v4lrecorder.cpp:96
RecorderBase * nvr
Definition: v4lrecorder.h:18
#define close
Definition: compat.h:16
uint m_ntscVbiWidth
Definition: v4lrecorder.h:51
bool m_requestRecording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:332
bool isRunning(void) const
Definition: mthread.cpp:274
unsigned int uint
Definition: compat.h:140
const char * name
Definition: frequencies.h:97
Definition: vt.h:8
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
virtual void FormatTT(struct VBIData *)
Definition: v4lrecorder.h:42
unsigned char data[VT_HEIGHT][VT_WIDTH]
Definition: vt.h:43
void StopRecording(void) override
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
Definition: v4lrecorder.cpp:46
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: dtvrecorder.cpp:79
#define LOC
Definition: v4lrecorder.cpp:26
static uint Parse(const QString &vbiformat)
Definition: tv.h:16
uint m_ntscVbiLineCount
Definition: v4lrecorder.h:53
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void vbi_handler(struct vbi *vbi, int fd)
Definition: vbi.c:402
bool foundteletextpage
Definition: v4lrecorder.h:20
bool ExtractCC12(const unsigned char *buf, uint width)
#define EV_XPACKET
Definition: vt.h:22
uint16_t GetCode2(void) const
QString m_vbiDeviceName
Definition: v4lrecorder.h:47
Definition: vbi.h:21
virtual void StopRecording(void)
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
struct vbi * m_palVbiTt
Definition: v4lrecorder.h:50
bool ExtractCC34(const unsigned char *buf, uint width)
int vbi_add_handler(struct vbi *vbi, void *handler, void *data)
Definition: vbi.c:445
int type
Definition: vt.h:10
#define PG_SUBTITLE
Definition: vt.h:58
int fd
Definition: vbi.h:23
void vbi_del_handler(struct vbi *vbi, void *handler, void *data)
Definition: vbi.c:463
void * p1
Definition: vt.h:13
void vbi_close(struct vbi *vbi)
Definition: vbi.c:656
struct vbi * vbi_open(const char *vbi_dev_name, struct cache *ca, int fine_tune, int big_buf)
Definition: vbi.c:605
static void vbi_event(struct VBIData *data, struct vt_event *ev)
Definition: v4lrecorder.cpp:71
struct VBIData * m_palVbiCb
Definition: v4lrecorder.h:49
VBI608Extractor * m_vbi608
Definition: v4lrecorder.h:54
virtual void FormatCC(uint, uint)
Definition: v4lrecorder.h:43
volatile bool m_requestHelper
Definition: v4lrecorder.h:58