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