MythTV  master
v4lrecorder.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 
3 #ifdef USING_V4L2
4 #include <linux/videodev2.h>
5 #endif // USING_V4L2
6 
7 #include <sys/ioctl.h> // for ioctl
8 #include <sys/time.h> // for gettimeofday
9 #include <unistd.h> // for IO_NONBLOCK
10 #include <fcntl.h> // for IO_NONBLOCK
11 
12 #include "libmyth/mythcontext.h"
14 
16 #include "v4lrecorder.h"
17 #include "vbitext/vbi.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  {
36  m_vbiThread->wait();
37  delete m_vbiThread;
38  m_vbiThread = nullptr;
40  }
41 }
42 
44 {
46  while (m_vbiThread && m_vbiThread->isRunning())
47  m_vbiThread->wait();
48 }
49 
51 {
52  QMutexLocker locker(&m_pauseLock);
54 }
55 
56 void 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")
63  m_vbiMode = VBIMode::Parse(value);
64  else
65  DTVRecorder::SetOption(name, value);
66 }
67 
68 static void vbi_event(void *data_in, struct vt_event *ev)
69 {
70  auto *data = static_cast<struct VBIData *>(data_in);
71  switch (ev->type)
72  {
73  case EV_PAGE:
74  {
75  auto *vtp = (struct vt_page *) ev->p1;
76  if (vtp->flags & PG_SUBTITLE)
77  {
78 #if 0
79  LOG(VB_GENERAL, LOG_DEBUG, QString("subtitle page %1.%2")
80  .arg(vtp->pgno, 0, 16) .arg(vtp->subno, 0, 16));
81 #endif
82  data->foundteletextpage = true;
83  memcpy(&(data->teletextpage), vtp, sizeof(vt_page));
84  }
85  }
86  break;
87 
88  case EV_HEADER:
89  case EV_XPACKET:
90  break;
91  }
92 }
93 
95 {
96  int fd = -1;
97  if (m_vbiFd >= 0)
98  return m_vbiFd;
99 
100  struct VBIData *vbi_cb = nullptr;
101  struct vbi *pal_tt = nullptr;
102  uint width = 0;
103  uint start_line = 0;
104  uint line_count = 0;
105 
106  QByteArray vbidev = m_vbiDeviceName.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, 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_vbiDeviceName));
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  LOG(VB_RECORD, LOG_ERR, "V4L2 VBI setup failed");
144  close(fd);
145  return -1;
146  }
147  LOG(VB_RECORD, LOG_INFO, LOC +
148  QString("vbi_format rate: %1"
149  "\n\t\t\t offset: %2"
150  "\n\t\t\tsamples_per_line: %3"
151  "\n\t\t\t starts: %4, %5"
152  "\n\t\t\t counts: %6, %7"
153  "\n\t\t\t flags: 0x%8")
154  .arg(fmt.fmt.vbi.sampling_rate)
155  .arg(fmt.fmt.vbi.offset)
156  .arg(fmt.fmt.vbi.samples_per_line)
157  .arg(fmt.fmt.vbi.start[0])
158  .arg(fmt.fmt.vbi.start[1])
159  .arg(fmt.fmt.vbi.count[0])
160  .arg(fmt.fmt.vbi.count[1])
161  .arg(fmt.fmt.vbi.flags,0,16));
162 
163  width = fmt.fmt.vbi.samples_per_line;
164  start_line = fmt.fmt.vbi.start[0];
165  line_count = fmt.fmt.vbi.count[0];
166  if (line_count != fmt.fmt.vbi.count[1])
167  {
168  LOG(VB_GENERAL, LOG_ERR, LOC +
169  "VBI must have the same number of "
170  "odd and even fields for our decoder");
171  close(fd);
172  return -1;
173  }
174  if (start_line > 21 || start_line + line_count < 22)
175  {
176  LOG(VB_GENERAL, LOG_ERR, LOC + "VBI does not include line 21");
177  // TODO We could try to set the VBI format ourselves..
178  close(fd);
179  return -1;
180  }
181 #endif // USING_V4L2
182  }
183 
184  if (VBIMode::PAL_TT == m_vbiMode)
185  {
186  m_palVbiCb = vbi_cb;
187  m_palVbiTt = pal_tt;
188  }
189  else if (VBIMode::NTSC_CC == m_vbiMode)
190  {
191  m_ntscVbiWidth = width;
192  m_ntscVbiStartLine = start_line;
193  m_ntscVbiLineCount = line_count;
194  m_vbi608 = new VBI608Extractor();
195  }
196 
197  m_vbiFd = fd;
198 
199  return fd;
200 }
201 
203 {
204  if (m_vbiFd < 0)
205  return;
206 
207  if (m_palVbiTt)
208  {
211  delete m_palVbiCb;
212  m_palVbiCb = nullptr;
213  }
214  else
215  {
216  delete m_vbi608; m_vbi608 = nullptr;
217  close(m_vbiFd);
218  }
219 
220  m_vbiFd = -1;
221 }
222 
224 {
225  if (m_vbiFd < 0)
226  return;
227 
228  unsigned char *buf = nullptr;
229  unsigned char *ptr = nullptr;
230  unsigned char *ptr_end = nullptr;
231  if (m_ntscVbiWidth)
232  {
234  buf = ptr = new unsigned char[sz];
235  ptr_end = buf + sz;
236  }
237 
238  while (IsHelperRequested() && !IsErrored())
239  {
240  if (PauseAndWait())
241  continue;
242 
243  if (!IsHelperRequested() || IsErrored())
244  break;
245 
246  struct timeval tv {0, 5000};
247  fd_set rdset;
248 
249  FD_ZERO(&rdset); // NOLINT(readability-isolate-declaration)
250  FD_SET(m_vbiFd, &rdset);
251 
252  int nr = select(m_vbiFd + 1, &rdset, nullptr, nullptr, &tv);
253  if (nr < 0)
254  LOG(VB_GENERAL, LOG_ERR, LOC + "vbi select failed" + ENO);
255 
256  if (nr <= 0)
257  {
258  if (nr==0)
259  LOG(VB_GENERAL, LOG_DEBUG, LOC + "vbi select timed out");
260  continue; // either failed or timed out..
261  }
262  if (VBIMode::PAL_TT == m_vbiMode)
263  {
264  m_palVbiCb->foundteletextpage = false;
267  {
268  // decode VBI as teletext subtitles
270  }
271  }
272  else if (VBIMode::NTSC_CC == m_vbiMode)
273  {
274  int ret = read(m_vbiFd, ptr, ptr_end - ptr);
275  ptr = (ret > 0) ? ptr + ret : ptr;
276  if ((ptr_end - ptr) == 0)
277  {
278  unsigned char *line21_field1 =
279  buf + ((21 - m_ntscVbiStartLine) * static_cast<size_t>(m_ntscVbiWidth));
280  unsigned char *line21_field2 =
281  buf + ((m_ntscVbiLineCount + 21 - m_ntscVbiStartLine)
282  * static_cast<size_t>(m_ntscVbiWidth));
283  bool cc1 = m_vbi608->ExtractCC12(line21_field1, m_ntscVbiWidth);
284  bool cc2 = m_vbi608->ExtractCC34(line21_field2, m_ntscVbiWidth);
285  if (cc1 || cc2)
286  {
287  int code1 = m_vbi608->GetCode1();
288  int code2 = m_vbi608->GetCode2();
289  code1 = (0xFFFF==code1) ? -1 : code1;
290  code2 = (0xFFFF==code2) ? -1 : code2;
291  FormatCC(code1, code2);
292  }
293  ptr = buf;
294  }
295  else if (ret < 0)
296  {
297  LOG(VB_GENERAL, LOG_ERR, LOC + "Reading VBI data" + ENO);
298  }
299  }
300  }
301 
302  delete [] buf;
303 }
304 
305 /* vim: set expandtab tabstop=4 shiftwidth=4: */
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
vbi.h
V4LRecorder::m_vbiThread
VBIThread * m_vbiThread
Definition: v4lrecorder.h:56
vbi
Definition: vbi.h:17
VBI608Extractor::ExtractCC12
bool ExtractCC12(const unsigned char *buf, uint width)
Definition: vbi608extractor.cpp:320
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
discid.disc.read
def read(device=None, features=[])
Definition: disc.py:35
V4LRecorder::m_requestHelper
volatile bool m_requestHelper
Definition: v4lrecorder.h:59
V4LRecorder::OpenVBIDevice
int OpenVBIDevice(void)
Definition: v4lrecorder.cpp:94
VBI608Extractor
Definition: vbi608extractor.h:32
VBIMode::NTSC_CC
@ NTSC_CC
Definition: tv.h:14
V4LRecorder::m_ntscVbiWidth
uint m_ntscVbiWidth
Definition: v4lrecorder.h:52
DTVRecorder::IsErrored
bool IsErrored(void) override
Tells us whether an unrecoverable error has been encountered.
Definition: dtvrecorder.h:46
vt_page
Definition: vt.h:36
V4LRecorder::m_palVbiCb
struct VBIData * m_palVbiCb
Definition: v4lrecorder.h:50
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
VBIMode::Parse
static uint Parse(const QString &vbiformat)
Definition: tv.h:17
vbi_close
void vbi_close(struct vbi *vbi)
Definition: vbi.cpp:642
VBIData::foundteletextpage
bool foundteletextpage
Definition: v4lrecorder.h:21
VBIMode::PAL_TT
@ PAL_TT
Definition: tv.h:13
RecorderBase::m_pauseLock
QMutex m_pauseLock
Definition: recorderbase.h:315
close
#define close
Definition: compat.h:43
V4LRecorder::SetOption
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: v4lrecorder.cpp:56
EV_XPACKET
#define EV_XPACKET
Definition: vt.h:22
V4LRecorder::CloseVBIDevice
void CloseVBIDevice(void)
Definition: v4lrecorder.cpp:202
mythlogging.h
V4LRecorder::m_ntscVbiLineCount
uint m_ntscVbiLineCount
Definition: v4lrecorder.h:54
v4lrecorder.h
V4LRecorder::~V4LRecorder
~V4LRecorder() override
Definition: v4lrecorder.cpp:26
V4LRecorder::StopRecording
void StopRecording(void) override
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
Definition: v4lrecorder.cpp:43
V4LRecorder::m_ntscVbiStartLine
uint m_ntscVbiStartLine
Definition: v4lrecorder.h:53
vbi_open
struct vbi * vbi_open(const char *vbi_dev_name, [[maybe_unused]] struct cache *ca, int fine_tune, int big_buf)
Definition: vbi.cpp:588
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:91
LOC
#define LOC
Definition: v4lrecorder.cpp:23
V4LRecorder::IsHelperRequested
virtual bool IsHelperRequested(void) const
Definition: v4lrecorder.cpp:50
vbi_add_handler
int vbi_add_handler(struct vbi *vbi, vbic_handler handler, void *data)
Definition: vbi.cpp:455
V4LRecorder::m_palVbiTt
struct vbi * m_palVbiTt
Definition: v4lrecorder.h:51
RecorderBase::StopRecording
virtual void StopRecording(void)
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
Definition: recorderbase.cpp:230
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:68
PG_SUBTITLE
#define PG_SUBTITLE
Definition: vt.h:58
V4LRecorder::m_vbiMode
int m_vbiMode
Definition: v4lrecorder.h:49
V4LRecorder::m_vbi608
VBI608Extractor * m_vbi608
Definition: v4lrecorder.h:55
VBIData
Definition: v4lrecorder.h:17
EV_PAGE
#define EV_PAGE
Definition: vt.h:20
vbi_handler
void vbi_handler(struct vbi *vbi, [[maybe_unused]] int fd)
Definition: vbi.cpp:414
mythcontext.h
tv_rec.h
MThread::isRunning
bool isRunning(void) const
Definition: mthread.cpp:263
V4LRecorder::FormatCC
virtual void FormatCC(uint, uint)
Definition: v4lrecorder.h:44
VBI608Extractor::GetCode1
uint16_t GetCode1(void) const
Definition: vbi608extractor.h:37
V4LRecorder::FormatTT
virtual void FormatTT(struct VBIData *)
Definition: v4lrecorder.h:43
vbi608extractor.h
VBI608Extractor::GetCode2
uint16_t GetCode2(void) const
Definition: vbi608extractor.h:38
VBIData::nvr
RecorderBase * nvr
Definition: v4lrecorder.h:19
V4LRecorder::m_audioDeviceName
QString m_audioDeviceName
Definition: v4lrecorder.h:47
vbi_del_handler
void vbi_del_handler(struct vbi *vbi, vbic_handler handler, void *data)
Definition: vbi.cpp:471
V4LRecorder::RunVBIDevice
void RunVBIDevice(void)
Definition: v4lrecorder.cpp:223
RecorderBase::m_unpauseWait
QWaitCondition m_unpauseWait
Definition: recorderbase.h:319
vt_event
Definition: vt.h:8
vt_page::data
unsigned char data[VT_HEIGHT][VT_WIDTH]
Definition: vt.h:43
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:350
RecorderBase::PauseAndWait
virtual bool PauseAndWait(std::chrono::milliseconds timeout=100ms)
If m_requestPause is true, sets pause and blocks up to timeout milliseconds or until unpaused,...
Definition: recorderbase.cpp:332
V4LRecorder::m_vbiFd
int m_vbiFd
Definition: v4lrecorder.h:58
uint
unsigned int uint
Definition: freesurround.h:24
RecorderBase::m_requestRecording
bool m_requestRecording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:321
V4LRecorder::m_vbiDeviceName
QString m_vbiDeviceName
Definition: v4lrecorder.h:48