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