summaryrefslogtreecommitdiff
path: root/examples/wss.c (plain)
blob: 997dfe45378de55f5f31c95bd7cb5f5c8d48a332
1/*
2 * libzvbi WSS capture example
3 *
4 * Copyright (C) 2005 Michael H. Schimek
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28/* $Id: wss.c,v 1.11 2008/02/19 00:52:04 mschimek Exp $ */
29
30/* This example shows how to extract Wide Screen Signalling data
31 (EN 300 294) from video images. The signal is transmitted on the
32 first half of PAL/SECAM scan line 23, which ITU-R BT.601 defines
33 as the first line of a 576 line picture.
34
35 The author is not aware of any drivers which can capture a scan
36 line as raw VBI and video data at the same time, and sliced VBI
37 capturing is not supported yet by libzvbi. Note some drivers like
38 the Linux saa7134 driver cannot capture line 23 at all.
39
40 gcc -o wss wss.c `pkg-config zvbi-0.2 --cflags --libs` */
41
42#ifdef HAVE_CONFIG_H
43# include "config.h"
44#endif
45
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <assert.h>
50
51#ifdef ENABLE_V4L2
52
53#include <fcntl.h> /* low-level i/o */
54#include <unistd.h>
55#include <errno.h>
56#include <sys/stat.h>
57#include <sys/types.h>
58#include <sys/time.h>
59#include <sys/mman.h>
60#include <sys/ioctl.h>
61
62#include <libzvbi.h>
63
64#include <asm/types.h> /* for videodev2.h */
65#include "videodev2k.h"
66
67#define CLEAR(x) memset (&(x), 0, sizeof (x))
68
69struct buffer {
70 void * start;
71 size_t length;
72};
73
74static const char * dev_name = "/dev/video";
75
76static int fd;
77static struct buffer * buffers;
78static unsigned int n_buffers;
79
80static int quit;
81
82static vbi_raw_decoder rd;
83
84static void
85errno_exit (const char * s)
86{
87 fprintf (stderr, "%s error %d, %s\n",
88 s, errno, strerror (errno));
89
90 exit (EXIT_FAILURE);
91}
92
93static int
94xioctl (int fd,
95 int request,
96 void * p)
97{
98 int r;
99
100 do r = ioctl (fd, request, p);
101 while (-1 == r && EINTR == errno);
102
103 return r;
104}
105
106static void
107decode_wss_625 (uint8_t * buf)
108{
109 static const char *formats [] = {
110 "Full format 4:3, 576 lines",
111 "Letterbox 14:9 centre, 504 lines",
112 "Letterbox 14:9 top, 504 lines",
113 "Letterbox 16:9 centre, 430 lines",
114 "Letterbox 16:9 top, 430 lines",
115 "Letterbox > 16:9 centre",
116 "Full format 14:9 centre, 576 lines",
117 "Anamorphic 16:9, 576 lines"
118 };
119 static const char *subtitles [] = {
120 "none",
121 "in active image area",
122 "out of active image area",
123 "<invalid>"
124 };
125 int g1;
126 int parity;
127
128 g1 = buf[0] & 15;
129
130 parity = g1;
131 parity ^= parity >> 2;
132 parity ^= parity >> 1;
133 g1 &= 7;
134
135 printf ("WSS PAL: ");
136 if (!(parity & 1))
137 printf ("<parity error> ");
138 printf ("%s; %s mode; %s colour coding; %s helper; "
139 "reserved b7=%d; %s Teletext subtitles; "
140 "open subtitles: %s; %s surround sound; "
141 "copyright %s; copying %s\n",
142 formats[g1],
143 (buf[0] & 0x10) ? "film" : "camera",
144 (buf[0] & 0x20) ? "MA/CP" : "standard",
145 (buf[0] & 0x40) ? "modulated" : "no",
146 !!(buf[0] & 0x80),
147 (buf[1] & 0x01) ? "have" : "no",
148 subtitles[(buf[1] >> 1) & 3],
149 (buf[1] & 0x08) ? "have" : "no",
150 (buf[1] & 0x10) ? "asserted" : "unknown",
151 (buf[1] & 0x20) ? "restricted" : "not restricted");
152}
153
154static void
155process_image (const void * p)
156{
157 vbi_sliced sliced[1];
158 unsigned int n_lines;
159
160 n_lines = vbi_raw_decode (&rd, (uint8_t *) p, sliced);
161 if (0 /* test */) {
162 /* Error ignored. */
163 write (STDOUT_FILENO, p, rd.bytes_per_line);
164 } else if (n_lines > 0) {
165 assert (VBI_SLICED_WSS_625 == sliced[0].id);
166 assert (1 == n_lines);
167 decode_wss_625 (sliced[0].data);
168 } else {
169 fputc ('.', stdout);
170 fflush (stdout);
171 }
172}
173
174static void
175init_decoder (void)
176{
177 unsigned int services;
178
179 vbi_raw_decoder_init (&rd);
180
181 rd.scanning = 625;
182 rd.sampling_format = VBI_PIXFMT_YUYV;
183
184 /* Should be calculated from VIDIOC_CROPCAP information.
185 Common sampling rates are 14.75 MHz to get 768 PAL/SECAM
186 square pixels per line, and 13.5 MHz according to ITU-R Rec.
187 BT.601 with 720 pixels/line. Note BT.601 overscans the line:
188 13.5e6 / 720 > 14.75e6 / 768. Don't be fooled by a driver
189 scaling 768 square pixels to 720. */
190 rd.sampling_rate = 768 * 14.75e6 / 768;
191
192 rd.bytes_per_line = 768 * 2;
193
194 /* Should be calculated from VIDIOC_CROPCAP information. */
195 rd.offset = 0;
196
197 rd.start[0] = 23;
198 rd.count[0] = 1;
199
200 rd.start[1] = 0;
201 rd.count[1] = 0;
202
203 rd.interlaced = FALSE; /* just one line */
204 rd.synchronous = TRUE;
205
206 services = vbi_raw_decoder_add_services (&rd,
207 VBI_SLICED_WSS_625,
208 /* strict */ 2);
209 if (0 == services) {
210 fprintf (stderr, "Cannot decode WSS\n");
211 exit (EXIT_FAILURE);
212 }
213}
214
215static void
216mainloop (void)
217{
218 quit = 0;
219
220 while (!quit) {
221 struct v4l2_buffer buf;
222
223 for (;;) {
224 fd_set fds;
225 struct timeval tv;
226 int r;
227
228 FD_ZERO (&fds);
229 FD_SET (fd, &fds);
230
231 tv.tv_sec = 2;
232 tv.tv_usec = 0;
233
234 r = select (fd + 1, &fds, NULL, NULL, &tv);
235
236 if (-1 == r) {
237 if (EINTR == errno) {
238 /* XXX should subtract the elapsed
239 time from timeout here. */
240 continue;
241 }
242
243 errno_exit ("select");
244 }
245
246 if (0 == r) {
247 fprintf (stderr, "select timeout\n");
248 exit (EXIT_FAILURE);
249 }
250
251 break;
252 }
253
254 CLEAR (buf);
255
256 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
257 buf.memory = V4L2_MEMORY_MMAP;
258
259 if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
260 if (EAGAIN == errno)
261 continue;
262
263 errno_exit ("VIDIOC_DQBUF");
264 }
265
266 assert (buf.index < n_buffers);
267
268 process_image (buffers[buf.index].start);
269
270 if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
271 errno_exit ("VIDIOC_QBUF");
272 }
273}
274
275static void
276start_capturing (void)
277{
278 unsigned int i;
279 enum v4l2_buf_type type;
280
281 for (i = 0; i < n_buffers; ++i) {
282 struct v4l2_buffer buf;
283
284 CLEAR (buf);
285
286 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
287 buf.memory = V4L2_MEMORY_MMAP;
288 buf.index = i;
289
290 if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
291 errno_exit ("VIDIOC_QBUF");
292 }
293
294 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
295
296 if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
297 errno_exit ("VIDIOC_STREAMON");
298}
299
300static void
301init_device (void)
302{
303 struct v4l2_capability cap;
304 v4l2_std_id std_id;
305 struct v4l2_format fmt;
306 struct v4l2_requestbuffers req;
307
308 if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) {
309 if (EINVAL == errno) {
310 fprintf (stderr, "%s is no V4L2 device\n",
311 dev_name);
312 exit (EXIT_FAILURE);
313 } else {
314 errno_exit ("VIDIOC_QUERYCAP");
315 }
316 }
317
318 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
319 fprintf (stderr, "%s is no video capture device\n",
320 dev_name);
321 exit (EXIT_FAILURE);
322 }
323
324 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
325 fprintf (stderr, "%s does not support streaming I/O\n",
326 dev_name);
327 exit (EXIT_FAILURE);
328 }
329
330 std_id = V4L2_STD_PAL;
331
332 if (-1 == xioctl (fd, VIDIOC_S_STD, &std_id))
333 errno_exit ("VIDIOC_S_STD");
334
335 CLEAR (fmt);
336
337 /* We need the top field without vertical scaling,
338 width must be at least 320 pixels. */
339
340 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
341 fmt.fmt.pix.width = 768;
342 fmt.fmt.pix.height = 576;
343 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
344 fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
345
346 if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))
347 errno_exit ("VIDIOC_S_FMT");
348
349 /* XXX the driver may adjust width and height, some
350 even change the pixelformat, that should be checked here. */
351
352 CLEAR (req);
353
354 req.count = 4;
355 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
356 req.memory = V4L2_MEMORY_MMAP;
357
358 if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {
359 if (EINVAL == errno) {
360 fprintf (stderr, "%s does not support "
361 "memory mapping\n", dev_name);
362 exit (EXIT_FAILURE);
363 } else {
364 errno_exit ("VIDIOC_REQBUFS");
365 }
366 }
367
368 if (req.count < 2) {
369 fprintf (stderr, "Insufficient buffer memory on %s\n",
370 dev_name);
371 exit (EXIT_FAILURE);
372 }
373
374 buffers = calloc (req.count, sizeof (*buffers));
375
376 if (!buffers) {
377 fprintf (stderr, "Out of memory\n");
378 exit (EXIT_FAILURE);
379 }
380
381 for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
382 struct v4l2_buffer buf;
383
384 CLEAR (buf);
385
386 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
387 buf.memory = V4L2_MEMORY_MMAP;
388 buf.index = n_buffers;
389
390 if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf))
391 errno_exit ("VIDIOC_QUERYBUF");
392
393 buffers[n_buffers].length = buf.length;
394 buffers[n_buffers].start =
395 mmap (NULL /* start anywhere */,
396 buf.length,
397 PROT_READ | PROT_WRITE /* required */,
398 MAP_SHARED /* recommended */,
399 fd, buf.m.offset);
400
401 if (MAP_FAILED == buffers[n_buffers].start)
402 errno_exit ("mmap");
403 }
404}
405
406static void
407open_device (void)
408{
409 struct stat st;
410
411 if (-1 == stat (dev_name, &st)) {
412 fprintf (stderr, "Cannot identify '%s': %d, %s\n",
413 dev_name, errno, strerror (errno));
414 exit (EXIT_FAILURE);
415 }
416
417 if (!S_ISCHR (st.st_mode)) {
418 fprintf (stderr, "%s is no device\n", dev_name);
419 exit (EXIT_FAILURE);
420 }
421
422 fd = open (dev_name, O_RDWR | O_NONBLOCK, 0);
423
424 if (-1 == fd) {
425 fprintf (stderr, "Cannot open '%s': %d, %s\n",
426 dev_name, errno, strerror (errno));
427 exit (EXIT_FAILURE);
428 }
429}
430
431int
432main (void)
433{
434 /* Helps debugging. */
435 vbi_set_log_fn (/* mask: log everything */ -1,
436 vbi_log_on_stderr,
437 /* user_data */ NULL);
438
439 open_device ();
440
441 init_device ();
442
443 init_decoder ();
444
445 start_capturing ();
446
447 mainloop ();
448
449 exit (EXIT_SUCCESS);
450
451 return 0;
452}
453
454#else /* !ENABLE_V4L2 */
455
456int
457main (int argc,
458 char ** argv)
459{
460 fprintf (stderr, "Sorry, V4L2 only. Patches welcome.\n");
461
462 exit (EXIT_FAILURE);
463
464 return 0;
465}
466
467#endif /* !ENABLE_V4L2 */
468