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_request_helper = false;
34  m_unpauseWait.wakeAll();
35  }
36 
37  if (m_vbi_thread)
38  {
39  m_vbi_thread->wait();
40  delete m_vbi_thread;
41  m_vbi_thread = nullptr;
43  }
44 }
45 
47 {
49  while (m_vbi_thread && m_vbi_thread->isRunning())
50  m_vbi_thread->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_audiodevice = value;
63  else if (name == "vbidevice")
64  m_vbidevice = 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_vbi_fd >= 0)
100  return m_vbi_fd;
101 
102  struct VBIData *vbi_cb = nullptr;
103  struct vbi *pal_tt = nullptr;
104  uint width = 0, start_line = 0, line_count = 0;
105 
106  QByteArray vbidev = m_vbidevice.toLatin1();
107  if (VBIMode::PAL_TT == m_vbimode)
108  {
109  pal_tt = vbi_open(vbidev.constData(), nullptr, 99, -1);
110  if (pal_tt)
111  {
112  fd = pal_tt->fd;
113  vbi_cb = new VBIData;
114  memset(vbi_cb, 0, sizeof(VBIData));
115  vbi_cb->nvr = this;
116  vbi_add_handler(pal_tt, (void*) vbi_event, vbi_cb);
117  }
118  }
119  else if (VBIMode::NTSC_CC == m_vbimode)
120  {
121  fd = open(vbidev.constData(), O_RDONLY/*|O_NONBLOCK*/);
122  }
123  else
124  {
125  LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid CC/Teletext mode");
126  return -1;
127  }
128 
129  if (fd < 0)
130  {
131  LOG(VB_GENERAL, LOG_ERR, LOC +
132  QString("Can't open vbi device: '%1'").arg(m_vbidevice));
133  return -1;
134  }
135 
137  {
138 #ifdef USING_V4L2
139  struct v4l2_format fmt {};
140  fmt.type = V4L2_BUF_TYPE_VBI_CAPTURE;
141  if (0 != ioctl(fd, VIDIOC_G_FMT, &fmt))
142  {
143 #ifdef USING_V4L1
144  LOG(VB_RECORD, LOG_INFO, "V4L2 VBI setup failed, trying v1 ioctl");
145  // Try V4L v1 VBI ioctls, iff V4L v2 fails
146  struct vbi_format old_fmt;
147  memset(&old_fmt, 0, sizeof(vbi_format));
148  if (ioctl(fd, VIDIOCGVBIFMT, &old_fmt) < 0)
149  {
150  LOG(VB_GENERAL, LOG_ERR, LOC +
151  "Failed to query vbi capabilities (V4L1)");
152  close(fd);
153  return -1;
154  }
155  fmt.fmt.vbi.sampling_rate = old_fmt.sampling_rate;
156  fmt.fmt.vbi.offset = 0;
157  fmt.fmt.vbi.samples_per_line = old_fmt.samples_per_line;
158  fmt.fmt.vbi.start[0] = old_fmt.start[0];
159  fmt.fmt.vbi.start[1] = old_fmt.start[1];
160  fmt.fmt.vbi.count[0] = old_fmt.count[0];
161  fmt.fmt.vbi.count[1] = old_fmt.count[1];
162  fmt.fmt.vbi.flags = old_fmt.flags;
163 #else // if !USING_V4L1
164  LOG(VB_RECORD, LOG_ERR, "V4L2 VBI setup failed");
165  close(fd);
166  return -1;
167 #endif // !USING_V4L1
168  }
169  LOG(VB_RECORD, LOG_INFO, LOC +
170  QString("vbi_format rate: %1"
171  "\n\t\t\t offset: %2"
172  "\n\t\t\tsamples_per_line: %3"
173  "\n\t\t\t starts: %4, %5"
174  "\n\t\t\t counts: %6, %7"
175  "\n\t\t\t flags: 0x%8")
176  .arg(fmt.fmt.vbi.sampling_rate)
177  .arg(fmt.fmt.vbi.offset)
178  .arg(fmt.fmt.vbi.samples_per_line)
179  .arg(fmt.fmt.vbi.start[0])
180  .arg(fmt.fmt.vbi.start[1])
181  .arg(fmt.fmt.vbi.count[0])
182  .arg(fmt.fmt.vbi.count[1])
183  .arg(fmt.fmt.vbi.flags,0,16));
184 
185  width = fmt.fmt.vbi.samples_per_line;
186  start_line = fmt.fmt.vbi.start[0];
187  line_count = fmt.fmt.vbi.count[0];
188  if (line_count != fmt.fmt.vbi.count[1])
189  {
190  LOG(VB_GENERAL, LOG_ERR, LOC +
191  "VBI must have the same number of "
192  "odd and even fields for our decoder");
193  close(fd);
194  return -1;
195  }
196  if (start_line > 21 || start_line + line_count < 22)
197  {
198  LOG(VB_GENERAL, LOG_ERR, LOC + "VBI does not include line 21");
199  // TODO We could try to set the VBI format ourselves..
200  close(fd);
201  return -1;
202  }
203 #endif // USING_V4L2
204  }
205 
206  if (VBIMode::PAL_TT == m_vbimode)
207  {
208  m_pal_vbi_cb = vbi_cb;
209  m_pal_vbi_tt = pal_tt;
210  }
211  else if (VBIMode::NTSC_CC == m_vbimode)
212  {
213  m_ntsc_vbi_width = width;
214  m_ntsc_vbi_start_line = start_line;
215  m_ntsc_vbi_line_count = line_count;
216  m_vbi608 = new VBI608Extractor();
217  }
218 
219  m_vbi_fd = fd;
220 
221  return fd;
222 }
223 
225 {
226  if (m_vbi_fd < 0)
227  return;
228 
229  if (m_pal_vbi_tt)
230  {
233  delete m_pal_vbi_cb;
234  m_pal_vbi_cb = nullptr;
235  }
236  else
237  {
238  delete m_vbi608; m_vbi608 = nullptr;
239  close(m_vbi_fd);
240  }
241 
242  m_vbi_fd = -1;
243 }
244 
246 {
247  if (m_vbi_fd < 0)
248  return;
249 
250  unsigned char *buf = nullptr, *ptr = nullptr, *ptr_end = nullptr;
251  if (m_ntsc_vbi_width)
252  {
254  buf = ptr = new unsigned char[sz];
255  ptr_end = buf + sz;
256  }
257 
258  while (IsHelperRequested() && !IsErrored())
259  {
260  if (PauseAndWait())
261  continue;
262 
263  if (!IsHelperRequested() || IsErrored())
264  break;
265 
266  struct timeval tv {0, 5000};
267  fd_set rdset;
268 
269  FD_ZERO(&rdset);
270  FD_SET(m_vbi_fd, &rdset);
271 
272  int nr = select(m_vbi_fd + 1, &rdset, nullptr, nullptr, &tv);
273  if (nr < 0)
274  LOG(VB_GENERAL, LOG_ERR, LOC + "vbi select failed" + ENO);
275 
276  if (nr <= 0)
277  {
278  if (nr==0)
279  LOG(VB_GENERAL, LOG_DEBUG, LOC + "vbi select timed out");
280  continue; // either failed or timed out..
281  }
282  if (VBIMode::PAL_TT == m_vbimode)
283  {
287  {
288  // decode VBI as teletext subtitles
290  }
291  }
292  else if (VBIMode::NTSC_CC == m_vbimode)
293  {
294  int ret = read(m_vbi_fd, ptr, ptr_end - ptr);
295  ptr = (ret > 0) ? ptr + ret : ptr;
296  if ((ptr_end - ptr) == 0)
297  {
298  unsigned char *line21_field1 =
299  buf + ((21 - m_ntsc_vbi_start_line) * m_ntsc_vbi_width);
300  unsigned char *line21_field2 =
302  * m_ntsc_vbi_width);
303  bool cc1 = m_vbi608->ExtractCC12(line21_field1, m_ntsc_vbi_width);
304  bool cc2 = m_vbi608->ExtractCC34(line21_field2, m_ntsc_vbi_width);
305  if (cc1 || cc2)
306  {
307  int code1 = m_vbi608->GetCode1();
308  int code2 = m_vbi608->GetCode2();
309  code1 = (0xFFFF==code1) ? -1 : code1;
310  code2 = (0xFFFF==code2) ? -1 : code2;
311  FormatCC(code1, code2);
312  }
313  ptr = buf;
314  }
315  else if (ret < 0)
316  {
317  LOG(VB_GENERAL, LOG_ERR, LOC + "Reading VBI data" + ENO);
318  }
319  }
320  }
321 
322  delete [] buf;
323 }
324 
325 /* vim: set expandtab tabstop=4 shiftwidth=4: */
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
uint m_ntsc_vbi_width
Definition: v4lrecorder.h:51
struct vbi * m_pal_vbi_tt
Definition: v4lrecorder.h:50
#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:48
virtual bool PauseAndWait(int timeout=100)
If m_request_pause is true, sets pause and blocks up to timeout milliseconds or until unpaused,...
volatile bool m_request_helper
Definition: v4lrecorder.h:58
virtual ~V4LRecorder()
Definition: v4lrecorder.cpp:29
bool m_request_recording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:332
virtual bool IsHelperRequested(void) const
Definition: v4lrecorder.cpp:53
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
QString m_audiodevice
Definition: v4lrecorder.h:46
bool isRunning(void) const
Definition: mthread.cpp:274
uint m_ntsc_vbi_line_count
Definition: v4lrecorder.h:53
QString m_vbidevice
Definition: v4lrecorder.h:47
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
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void vbi_handler(struct vbi *vbi, int fd)
Definition: vbi.c:401
struct VBIData * m_pal_vbi_cb
Definition: v4lrecorder.h:49
uint m_ntsc_vbi_start_line
Definition: v4lrecorder.h:52
bool foundteletextpage
Definition: v4lrecorder.h:20
bool ExtractCC12(const unsigned char *buf, uint width)
#define EV_XPACKET
Definition: vt.h:22
VBIThread * m_vbi_thread
Definition: v4lrecorder.h:55
uint16_t GetCode2(void) const
Definition: vbi.h:21
virtual void StopRecording(void)
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
bool ExtractCC34(const unsigned char *buf, uint width)
int vbi_add_handler(struct vbi *vbi, void *handler, void *data)
Definition: vbi.c:444
int type
Definition: vt.h:10
#define PG_SUBTITLE
Definition: vt.h:58
static uint Parse(QString vbiformat)
Definition: tv.h:16
int fd
Definition: vbi.h:23
void vbi_del_handler(struct vbi *vbi, void *handler, void *data)
Definition: vbi.c:462
void * p1
Definition: vt.h:13
void vbi_close(struct vbi *vbi)
Definition: vbi.c:658
struct vbi * vbi_open(const char *vbi_dev_name, struct cache *ca, int fine_tune, int big_buf)
Definition: vbi.c:607
static void vbi_event(struct VBIData *data, struct vt_event *ev)
Definition: v4lrecorder.cpp:71
VBI608Extractor * m_vbi608
Definition: v4lrecorder.h:54
virtual void FormatCC(uint, uint)
Definition: v4lrecorder.h:43