00001 /* 00002 * libzvbi WSS capture example 00003 * 00004 * Copyright (C) 2005 Michael H. Schimek 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00019 */ 00020 00021 /* $Id: wss.c,v 1.4 2006/10/27 04:52:08 mschimek Exp $ */ 00022 00023 /* This example shows how to extract Wide Screen Signalling data 00024 (EN 300 294) from video images. The signal is transmitted on the 00025 first half of PAL/SECAM scan line 23, which ITU-R BT.601 defines 00026 as the first line of a 576 line picture. 00027 00028 The author is not aware of any drivers which can capture a scan 00029 line as raw VBI and video data at the same time, and sliced VBI 00030 capturing is not supported yet by libzvbi. Note some drivers like 00031 saa7134 cannot capture line 23 at all. 00032 00033 gcc -o wss wss.c `pkg-config zvbi-0.2 --cflags --libs` */ 00034 00035 #ifdef HAVE_CONFIG_H 00036 # include "config.h" 00037 #endif 00038 00039 #include <stdio.h> 00040 #include <stdlib.h> 00041 #include <string.h> 00042 #include <assert.h> 00043 00044 #ifdef ENABLE_V4L2 00045 00046 #include <fcntl.h> /* low-level i/o */ 00047 #include <unistd.h> 00048 #include <errno.h> 00049 #include <sys/stat.h> 00050 #include <sys/types.h> 00051 #include <sys/time.h> 00052 #include <sys/mman.h> 00053 #include <sys/ioctl.h> 00054 00055 #include <libzvbi.h> 00056 00057 #include <asm/types.h> /* for videodev2.h */ 00058 #include "videodev2k.h" 00059 00060 #define CLEAR(x) memset (&(x), 0, sizeof (x)) 00061 00062 struct buffer { 00063 void * start; 00064 size_t length; 00065 }; 00066 00067 static const char * dev_name = "/dev/video"; 00068 00069 static int fd; 00070 static struct buffer * buffers; 00071 static unsigned int n_buffers; 00072 00073 static int quit; 00074 00075 static vbi_raw_decoder rd; 00076 00077 static void 00078 errno_exit (const char * s) 00079 { 00080 fprintf (stderr, "%s error %d, %s\n", 00081 s, errno, strerror (errno)); 00082 00083 exit (EXIT_FAILURE); 00084 } 00085 00086 static int 00087 xioctl (int fd, 00088 int request, 00089 void * p) 00090 { 00091 int r; 00092 00093 do r = ioctl (fd, request, p); 00094 while (-1 == r && EINTR == errno); 00095 00096 return r; 00097 } 00098 00099 static void 00100 decode_wss_625 (uint8_t * buf) 00101 { 00102 static const char *formats [] = { 00103 "Full format 4:3, 576 lines", 00104 "Letterbox 14:9 centre, 504 lines", 00105 "Letterbox 14:9 top, 504 lines", 00106 "Letterbox 16:9 centre, 430 lines", 00107 "Letterbox 16:9 top, 430 lines", 00108 "Letterbox > 16:9 centre", 00109 "Full format 14:9 centre, 576 lines", 00110 "Anamorphic 16:9, 576 lines" 00111 }; 00112 static const char *subtitles [] = { 00113 "none", 00114 "in active image area", 00115 "out of active image area", 00116 "<invalid>" 00117 }; 00118 int g1; 00119 int parity; 00120 00121 g1 = buf[0] & 15; 00122 00123 parity = g1; 00124 parity ^= parity >> 2; 00125 parity ^= parity >> 1; 00126 g1 &= 7; 00127 00128 printf ("WSS PAL: "); 00129 if (!(parity & 1)) 00130 printf ("<parity error> "); 00131 printf ("%s; %s mode; %s colour coding; %s helper; " 00132 "reserved b7=%d; %s Teletext subtitles; " 00133 "open subtitles: %s; %s surround sound; " 00134 "copyright %s; copying %s\n", 00135 formats[g1], 00136 (buf[0] & 0x10) ? "film" : "camera", 00137 (buf[0] & 0x20) ? "MA/CP" : "standard", 00138 (buf[0] & 0x40) ? "modulated" : "no", 00139 !!(buf[0] & 0x80), 00140 (buf[1] & 0x01) ? "have" : "no", 00141 subtitles[(buf[1] >> 1) & 3], 00142 (buf[1] & 0x08) ? "have" : "no", 00143 (buf[1] & 0x10) ? "asserted" : "unknown", 00144 (buf[1] & 0x20) ? "restricted" : "not restricted"); 00145 } 00146 00147 static void 00148 process_image (const void * p) 00149 { 00150 vbi_sliced sliced[1]; 00151 unsigned int n_lines; 00152 00153 n_lines = vbi_raw_decode (&rd, (uint8_t *) p, sliced); 00154 if (0 /* test */) { 00155 /* Error ignored. */ 00156 write (STDOUT_FILENO, p, rd.bytes_per_line); 00157 } else if (n_lines > 0) { 00158 assert (VBI_SLICED_WSS_625 == sliced[0].id); 00159 assert (1 == n_lines); 00160 decode_wss_625 (sliced[0].data); 00161 } else { 00162 fputc ('.', stdout); 00163 fflush (stdout); 00164 } 00165 } 00166 00167 static void 00168 init_decoder (void) 00169 { 00170 unsigned int services; 00171 00172 vbi_raw_decoder_init (&rd); 00173 00174 rd.scanning = 625; 00175 rd.sampling_format = VBI_PIXFMT_YUYV; 00176 00177 /* Should be calculated from VIDIOC_CROPCAP information. 00178 Common sampling rates are 14.75 MHz to get 768 PAL/SECAM 00179 square pixels per line, and 13.5 MHz according to ITU-R Rec. 00180 BT.601 with 720 pixels/line. Note BT.601 overscans the line: 00181 13.5e6 / 720 > 14.75e6 / 768. Don't be fooled by a driver 00182 scaling 768 square pixels to 720. */ 00183 rd.sampling_rate = 768 * 14750000 / 768; 00184 00185 rd.bytes_per_line = 768 * 2; 00186 00187 /* Should be calculated from VIDIOC_CROPCAP information. */ 00188 rd.offset = 0; 00189 00190 rd.start[0] = 23; 00191 rd.count[0] = 1; 00192 00193 rd.start[1] = 0; 00194 rd.count[1] = 0; 00195 00196 rd.interlaced = FALSE; /* just one line */ 00197 rd.synchronous = TRUE; 00198 00199 services = vbi_raw_decoder_add_services (&rd, 00200 VBI_SLICED_WSS_625, 00201 /* strict */ 2); 00202 if (0 == services) { 00203 fprintf (stderr, "Cannot decode WSS\n"); 00204 exit (EXIT_FAILURE); 00205 } 00206 } 00207 00208 static void 00209 mainloop (void) 00210 { 00211 quit = 0; 00212 00213 while (!quit) { 00214 struct v4l2_buffer buf; 00215 00216 for (;;) { 00217 fd_set fds; 00218 struct timeval tv; 00219 int r; 00220 00221 FD_ZERO (&fds); 00222 FD_SET (fd, &fds); 00223 00224 tv.tv_sec = 2; 00225 tv.tv_usec = 0; 00226 00227 r = select (fd + 1, &fds, NULL, NULL, &tv); 00228 00229 if (-1 == r) { 00230 if (EINTR == errno) { 00231 /* XXX should subtract the elapsed 00232 time from timeout here. */ 00233 continue; 00234 } 00235 00236 errno_exit ("select"); 00237 } 00238 00239 if (0 == r) { 00240 fprintf (stderr, "select timeout\n"); 00241 exit (EXIT_FAILURE); 00242 } 00243 00244 break; 00245 } 00246 00247 CLEAR (buf); 00248 00249 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00250 buf.memory = V4L2_MEMORY_MMAP; 00251 00252 if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) { 00253 if (EAGAIN == errno) 00254 continue; 00255 00256 errno_exit ("VIDIOC_DQBUF"); 00257 } 00258 00259 assert (buf.index < n_buffers); 00260 00261 process_image (buffers[buf.index].start); 00262 00263 if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) 00264 errno_exit ("VIDIOC_QBUF"); 00265 } 00266 } 00267 00268 static void 00269 start_capturing (void) 00270 { 00271 unsigned int i; 00272 enum v4l2_buf_type type; 00273 00274 for (i = 0; i < n_buffers; ++i) { 00275 struct v4l2_buffer buf; 00276 00277 CLEAR (buf); 00278 00279 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00280 buf.memory = V4L2_MEMORY_MMAP; 00281 buf.index = i; 00282 00283 if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) 00284 errno_exit ("VIDIOC_QBUF"); 00285 } 00286 00287 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00288 00289 if (-1 == xioctl (fd, VIDIOC_STREAMON, &type)) 00290 errno_exit ("VIDIOC_STREAMON"); 00291 } 00292 00293 static void 00294 init_device (void) 00295 { 00296 struct v4l2_capability cap; 00297 v4l2_std_id std_id; 00298 struct v4l2_format fmt; 00299 struct v4l2_requestbuffers req; 00300 00301 if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) { 00302 if (EINVAL == errno) { 00303 fprintf (stderr, "%s is no V4L2 device\n", 00304 dev_name); 00305 exit (EXIT_FAILURE); 00306 } else { 00307 errno_exit ("VIDIOC_QUERYCAP"); 00308 } 00309 } 00310 00311 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { 00312 fprintf (stderr, "%s is no video capture device\n", 00313 dev_name); 00314 exit (EXIT_FAILURE); 00315 } 00316 00317 if (!(cap.capabilities & V4L2_CAP_STREAMING)) { 00318 fprintf (stderr, "%s does not support streaming I/O\n", 00319 dev_name); 00320 exit (EXIT_FAILURE); 00321 } 00322 00323 std_id = V4L2_STD_PAL; 00324 00325 if (-1 == xioctl (fd, VIDIOC_S_STD, &std_id)) 00326 errno_exit ("VIDIOC_S_STD"); 00327 00328 CLEAR (fmt); 00329 00330 /* We need the top field without vertical scaling, 00331 width must be at least 320 pixels. */ 00332 00333 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00334 fmt.fmt.pix.width = 768; 00335 fmt.fmt.pix.height = 576; 00336 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 00337 fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; 00338 00339 if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) 00340 errno_exit ("VIDIOC_S_FMT"); 00341 00342 /* XXX the driver may adjust width and height, some 00343 even change the pixelformat, that should be checked here. */ 00344 00345 CLEAR (req); 00346 00347 req.count = 4; 00348 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00349 req.memory = V4L2_MEMORY_MMAP; 00350 00351 if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) { 00352 if (EINVAL == errno) { 00353 fprintf (stderr, "%s does not support " 00354 "memory mapping\n", dev_name); 00355 exit (EXIT_FAILURE); 00356 } else { 00357 errno_exit ("VIDIOC_REQBUFS"); 00358 } 00359 } 00360 00361 if (req.count < 2) { 00362 fprintf (stderr, "Insufficient buffer memory on %s\n", 00363 dev_name); 00364 exit (EXIT_FAILURE); 00365 } 00366 00367 buffers = calloc (req.count, sizeof (*buffers)); 00368 00369 if (!buffers) { 00370 fprintf (stderr, "Out of memory\n"); 00371 exit (EXIT_FAILURE); 00372 } 00373 00374 for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { 00375 struct v4l2_buffer buf; 00376 00377 CLEAR (buf); 00378 00379 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00380 buf.memory = V4L2_MEMORY_MMAP; 00381 buf.index = n_buffers; 00382 00383 if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf)) 00384 errno_exit ("VIDIOC_QUERYBUF"); 00385 00386 buffers[n_buffers].length = buf.length; 00387 buffers[n_buffers].start = 00388 mmap (NULL /* start anywhere */, 00389 buf.length, 00390 PROT_READ | PROT_WRITE /* required */, 00391 MAP_SHARED /* recommended */, 00392 fd, buf.m.offset); 00393 00394 if (MAP_FAILED == buffers[n_buffers].start) 00395 errno_exit ("mmap"); 00396 } 00397 } 00398 00399 static void 00400 open_device (void) 00401 { 00402 struct stat st; 00403 00404 if (-1 == stat (dev_name, &st)) { 00405 fprintf (stderr, "Cannot identify '%s': %d, %s\n", 00406 dev_name, errno, strerror (errno)); 00407 exit (EXIT_FAILURE); 00408 } 00409 00410 if (!S_ISCHR (st.st_mode)) { 00411 fprintf (stderr, "%s is no device\n", dev_name); 00412 exit (EXIT_FAILURE); 00413 } 00414 00415 fd = open (dev_name, O_RDWR | O_NONBLOCK, 0); 00416 00417 if (-1 == fd) { 00418 fprintf (stderr, "Cannot open '%s': %d, %s\n", 00419 dev_name, errno, strerror (errno)); 00420 exit (EXIT_FAILURE); 00421 } 00422 } 00423 00424 int 00425 main (void) 00426 { 00427 /* Helps debugging. */ 00428 vbi_set_log_fn (/* mask: log everything */ -1, 00429 vbi_log_on_stderr, 00430 /* user_data */ NULL); 00431 00432 open_device (); 00433 00434 init_device (); 00435 00436 init_decoder (); 00437 00438 start_capturing (); 00439 00440 mainloop (); 00441 00442 exit (EXIT_SUCCESS); 00443 00444 return 0; 00445 } 00446 00447 #else /* !ENABLE_V4L2 */ 00448 00449 int 00450 main (int argc, 00451 char ** argv) 00452 { 00453 fprintf (stderr, "Sorry, V4L2 only. Patches welcome.\n"); 00454 00455 exit (EXIT_FAILURE); 00456 00457 return 0; 00458 } 00459 00460 #endif /* !ENABLE_V4L2 */