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  struct vt_page *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  memset(&fmt, 0, sizeof(fmt));
141  fmt.type = V4L2_BUF_TYPE_VBI_CAPTURE;
142  if (0 != ioctl(fd, VIDIOC_G_FMT, &fmt))
143  {
144 #ifdef USING_V4L1
145  LOG(VB_RECORD, LOG_INFO, "V4L2 VBI setup failed, trying v1 ioctl");
146  // Try V4L v1 VBI ioctls, iff V4L v2 fails
147  struct vbi_format old_fmt;
148  memset(&old_fmt, 0, sizeof(vbi_format));
149  if (ioctl(fd, VIDIOCGVBIFMT, &old_fmt) < 0)
150  {
151  LOG(VB_GENERAL, LOG_ERR, LOC +
152  "Failed to query vbi capabilities (V4L1)");
153  close(fd);
154  return -1;
155  }
156  fmt.fmt.vbi.sampling_rate = old_fmt.sampling_rate;
157  fmt.fmt.vbi.offset = 0;
158  fmt.fmt.vbi.samples_per_line = old_fmt.samples_per_line;
159  fmt.fmt.vbi.start[0] = old_fmt.start[0];
160  fmt.fmt.vbi.start[1] = old_fmt.start[1];
161  fmt.fmt.vbi.count[0] = old_fmt.count[0];
162  fmt.fmt.vbi.count[1] = old_fmt.count[1];
163  fmt.fmt.vbi.flags = old_fmt.flags;
164 #else // if !USING_V4L1
165  LOG(VB_RECORD, LOG_ERR, "V4L2 VBI setup failed");
166  close(fd);
167  return -1;
168 #endif // !USING_V4L1
169  }
170  LOG(VB_RECORD, LOG_INFO, LOC +
171  QString("vbi_format rate: %1"
172  "\n\t\t\t offset: %2"
173  "\n\t\t\tsamples_per_line: %3"
174  "\n\t\t\t starts: %4, %5"
175  "\n\t\t\t counts: %6, %7"
176  "\n\t\t\t flags: 0x%8")
177  .arg(fmt.fmt.vbi.sampling_rate)
178  .arg(fmt.fmt.vbi.offset)
179  .arg(fmt.fmt.vbi.samples_per_line)
180  .arg(fmt.fmt.vbi.start[0])
181  .arg(fmt.fmt.vbi.start[1])
182  .arg(fmt.fmt.vbi.count[0])
183  .arg(fmt.fmt.vbi.count[1])
184  .arg(fmt.fmt.vbi.flags,0,16));
185 
186  width = fmt.fmt.vbi.samples_per_line;
187  start_line = fmt.fmt.vbi.start[0];
188  line_count = fmt.fmt.vbi.count[0];
189  if (line_count != fmt.fmt.vbi.count[1])
190  {
191  LOG(VB_GENERAL, LOG_ERR, LOC +
192  "VBI must have the same number of "
193  "odd and even fields for our decoder");
194  close(fd);
195  return -1;
196  }
197  if (start_line > 21 || start_line + line_count < 22)
198  {
199  LOG(VB_GENERAL, LOG_ERR, LOC + "VBI does not include line 21");
200  // TODO We could try to set the VBI format ourselves..
201  close(fd);
202  return -1;
203  }
204 #endif // USING_V4L2
205  }
206 
207  if (VBIMode::PAL_TT == m_vbimode)
208  {
209  m_pal_vbi_cb = vbi_cb;
210  m_pal_vbi_tt = pal_tt;
211  }
212  else if (VBIMode::NTSC_CC == m_vbimode)
213  {
214  m_ntsc_vbi_width = width;
215  m_ntsc_vbi_start_line = start_line;
216  m_ntsc_vbi_line_count = line_count;
217  m_vbi608 = new VBI608Extractor();
218  }
219 
220  m_vbi_fd = fd;
221 
222  return fd;
223 }
224 
226 {
227  if (m_vbi_fd < 0)
228  return;
229 
230  if (m_pal_vbi_tt)
231  {
234  delete m_pal_vbi_cb;
235  m_pal_vbi_cb = nullptr;
236  }
237  else
238  {
239  delete m_vbi608; m_vbi608 = nullptr;
240  close(m_vbi_fd);
241  }
242 
243  m_vbi_fd = -1;
244 }
245 
247 {
248  if (m_vbi_fd < 0)
249  return;
250 
251  unsigned char *buf = nullptr, *ptr = nullptr, *ptr_end = nullptr;
252  if (m_ntsc_vbi_width)
253  {
255  buf = ptr = new unsigned char[sz];
256  ptr_end = buf + sz;
257  }
258 
259  while (IsHelperRequested() && !IsErrored())
260  {
261  if (PauseAndWait())
262  continue;
263 
264  if (!IsHelperRequested() || IsErrored())
265  break;
266 
267  struct timeval tv;
268  fd_set rdset;
269 
270  tv.tv_sec = 0;
271  tv.tv_usec = 5000;
272  FD_ZERO(&rdset);
273  FD_SET(m_vbi_fd, &rdset);
274 
275  int nr = select(m_vbi_fd + 1, &rdset, nullptr, nullptr, &tv);
276  if (nr < 0)
277  LOG(VB_GENERAL, LOG_ERR, LOC + "vbi select failed" + ENO);
278 
279  if (nr <= 0)
280  {
281  if (nr==0)
282  LOG(VB_GENERAL, LOG_DEBUG, LOC + "vbi select timed out");
283  continue; // either failed or timed out..
284  }
285  if (VBIMode::PAL_TT == m_vbimode)
286  {
290  {
291  // decode VBI as teletext subtitles
293  }
294  }
295  else if (VBIMode::NTSC_CC == m_vbimode)
296  {
297  int ret = read(m_vbi_fd, ptr, ptr_end - ptr);
298  ptr = (ret > 0) ? ptr + ret : ptr;
299  if ((ptr_end - ptr) == 0)
300  {
301  unsigned char *line21_field1 =
302  buf + ((21 - m_ntsc_vbi_start_line) * m_ntsc_vbi_width);
303  unsigned char *line21_field2 =
305  * m_ntsc_vbi_width);
306  bool cc1 = m_vbi608->ExtractCC12(line21_field1, m_ntsc_vbi_width);
307  bool cc2 = m_vbi608->ExtractCC34(line21_field2, m_ntsc_vbi_width);
308  if (cc1 || cc2)
309  {
310  int code1 = m_vbi608->GetCode1();
311  int code2 = m_vbi608->GetCode2();
312  code1 = (0xFFFF==code1) ? -1 : code1;
313  code2 = (0xFFFF==code2) ? -1 : code2;
314  FormatCC(code1, code2);
315  }
316  ptr = buf;
317  }
318  else if (ret < 0)
319  {
320  LOG(VB_GENERAL, LOG_ERR, LOC + "Reading VBI data" + ENO);
321  }
322  }
323  }
324 
325  delete [] buf;
326 }
327 
328 /* 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
int pgno
Definition: vt.h:38
#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)
unsigned int uint
Definition: compat.h:140
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
int flags
Definition: vt.h:40
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
const char * name
Definition: ParseText.cpp:328
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:83
#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.
int subno
Definition: vt.h:38
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