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.1 2006/05/07 20:55: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 (n_lines > 0) { 00149 assert (VBI_SLICED_WSS_625 == sliced[0].id); 00150 decode_wss_625 (sliced[0].data); 00151 } else { 00152 fputc ('.', stdout); 00153 fflush (stdout); 00154 } 00155 } 00156 00157 static void 00158 init_decoder (void) 00159 { 00160 unsigned int services; 00161 00162 vbi_raw_decoder_init (&rd); 00163 00164 rd.scanning = 625; 00165 rd.sampling_format = VBI_PIXFMT_YUYV; 00166 00167 /* Should be calculated from VIDIOC_CROPCAP information. 00168 Common sampling rates are 14.75 MHz to get 768 PAL/SECAM 00169 square pixels per line, and 13.5 MHz according to ITU-R Rec. 00170 BT.601, 720 pixels/line. Note BT.601 overscans the line: 00171 13.5e6 / 720 > 14.75e6 / 768. Don't be fooled by a driver 00172 scaling 768 square pixels to 720. */ 00173 rd.sampling_rate = 768 / 768 * 14750000; 00174 00175 /* Misnamed, should be samples_per_line. */ 00176 rd.bytes_per_line = 768; 00177 00178 /* Should be calculated from VIDIOC_CROPCAP information. */ 00179 rd.offset = 0; //6.8e-6 * rd.sampling_rate; 00180 00181 rd.start[0] = 23; 00182 rd.count[0] = 1; 00183 00184 rd.start[1] = 0; 00185 rd.count[1] = 0; 00186 00187 rd.interlaced = FALSE; /* just one line */ 00188 rd.synchronous = TRUE; 00189 00190 services = vbi_raw_decoder_add_services (&rd, 00191 VBI_SLICED_WSS_625, 00192 /* strict */ 0); 00193 if (0 == services) { 00194 fprintf (stderr, "Cannot decode WSS\n"); 00195 exit (EXIT_FAILURE); 00196 } 00197 } 00198 00199 static void 00200 mainloop (void) 00201 { 00202 quit = 0; 00203 00204 while (!quit) { 00205 struct v4l2_buffer buf; 00206 00207 for (;;) { 00208 fd_set fds; 00209 struct timeval tv; 00210 int r; 00211 00212 FD_ZERO (&fds); 00213 FD_SET (fd, &fds); 00214 00215 tv.tv_sec = 2; 00216 tv.tv_usec = 0; 00217 00218 r = select (fd + 1, &fds, NULL, NULL, &tv); 00219 00220 if (-1 == r) { 00221 if (EINTR == errno) { 00222 /* XXX should subtract the elapsed 00223 time from timeout here. */ 00224 continue; 00225 } 00226 00227 errno_exit ("select"); 00228 } 00229 00230 if (0 == r) { 00231 fprintf (stderr, "select timeout\n"); 00232 exit (EXIT_FAILURE); 00233 } 00234 00235 break; 00236 } 00237 00238 CLEAR (buf); 00239 00240 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00241 buf.memory = V4L2_MEMORY_MMAP; 00242 00243 if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) { 00244 if (EAGAIN == errno) 00245 continue; 00246 00247 errno_exit ("VIDIOC_DQBUF"); 00248 } 00249 00250 assert (buf.index < n_buffers); 00251 00252 process_image (buffers[buf.index].start); 00253 00254 if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) 00255 errno_exit ("VIDIOC_QBUF"); 00256 } 00257 } 00258 00259 static void 00260 start_capturing (void) 00261 { 00262 unsigned int i; 00263 enum v4l2_buf_type type; 00264 00265 for (i = 0; i < n_buffers; ++i) { 00266 struct v4l2_buffer buf; 00267 00268 CLEAR (buf); 00269 00270 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00271 buf.memory = V4L2_MEMORY_MMAP; 00272 buf.index = i; 00273 00274 if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) 00275 errno_exit ("VIDIOC_QBUF"); 00276 } 00277 00278 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00279 00280 if (-1 == xioctl (fd, VIDIOC_STREAMON, &type)) 00281 errno_exit ("VIDIOC_STREAMON"); 00282 } 00283 00284 static void 00285 init_device (void) 00286 { 00287 struct v4l2_capability cap; 00288 v4l2_std_id std_id; 00289 struct v4l2_format fmt; 00290 struct v4l2_requestbuffers req; 00291 00292 if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) { 00293 if (EINVAL == errno) { 00294 fprintf (stderr, "%s is no V4L2 device\n", 00295 dev_name); 00296 exit (EXIT_FAILURE); 00297 } else { 00298 errno_exit ("VIDIOC_QUERYCAP"); 00299 } 00300 } 00301 00302 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { 00303 fprintf (stderr, "%s is no video capture device\n", 00304 dev_name); 00305 exit (EXIT_FAILURE); 00306 } 00307 00308 if (!(cap.capabilities & V4L2_CAP_STREAMING)) { 00309 fprintf (stderr, "%s does not support streaming I/O\n", 00310 dev_name); 00311 exit (EXIT_FAILURE); 00312 } 00313 00314 std_id = V4L2_STD_PAL; 00315 00316 if (-1 == xioctl (fd, VIDIOC_S_STD, &std_id)) 00317 errno_exit ("VIDIOC_S_STD"); 00318 00319 CLEAR (fmt); 00320 00321 /* We need the top field without vertical scaling, 00322 width must be at least 320 pixels. */ 00323 00324 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00325 fmt.fmt.pix.width = 768; 00326 fmt.fmt.pix.height = 576; 00327 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 00328 fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; 00329 00330 if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) 00331 errno_exit ("VIDIOC_S_FMT"); 00332 00333 /* XXX the driver may adjust width and height, some 00334 even change the pixelformat, that should be checked here. */ 00335 00336 CLEAR (req); 00337 00338 req.count = 4; 00339 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00340 req.memory = V4L2_MEMORY_MMAP; 00341 00342 if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) { 00343 if (EINVAL == errno) { 00344 fprintf (stderr, "%s does not support " 00345 "memory mapping\n", dev_name); 00346 exit (EXIT_FAILURE); 00347 } else { 00348 errno_exit ("VIDIOC_REQBUFS"); 00349 } 00350 } 00351 00352 if (req.count < 2) { 00353 fprintf (stderr, "Insufficient buffer memory on %s\n", 00354 dev_name); 00355 exit (EXIT_FAILURE); 00356 } 00357 00358 buffers = calloc (req.count, sizeof (*buffers)); 00359 00360 if (!buffers) { 00361 fprintf (stderr, "Out of memory\n"); 00362 exit (EXIT_FAILURE); 00363 } 00364 00365 for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { 00366 struct v4l2_buffer buf; 00367 00368 CLEAR (buf); 00369 00370 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 00371 buf.memory = V4L2_MEMORY_MMAP; 00372 buf.index = n_buffers; 00373 00374 if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf)) 00375 errno_exit ("VIDIOC_QUERYBUF"); 00376 00377 buffers[n_buffers].length = buf.length; 00378 buffers[n_buffers].start = 00379 mmap (NULL /* start anywhere */, 00380 buf.length, 00381 PROT_READ | PROT_WRITE /* required */, 00382 MAP_SHARED /* recommended */, 00383 fd, buf.m.offset); 00384 00385 if (MAP_FAILED == buffers[n_buffers].start) 00386 errno_exit ("mmap"); 00387 } 00388 } 00389 00390 static void 00391 open_device (void) 00392 { 00393 struct stat st; 00394 00395 if (-1 == stat (dev_name, &st)) { 00396 fprintf (stderr, "Cannot identify '%s': %d, %s\n", 00397 dev_name, errno, strerror (errno)); 00398 exit (EXIT_FAILURE); 00399 } 00400 00401 if (!S_ISCHR (st.st_mode)) { 00402 fprintf (stderr, "%s is no device\n", dev_name); 00403 exit (EXIT_FAILURE); 00404 } 00405 00406 fd = open (dev_name, O_RDWR | O_NONBLOCK, 0); 00407 00408 if (-1 == fd) { 00409 fprintf (stderr, "Cannot open '%s': %d, %s\n", 00410 dev_name, errno, strerror (errno)); 00411 exit (EXIT_FAILURE); 00412 } 00413 } 00414 00415 int 00416 main (void) 00417 { 00418 open_device (); 00419 00420 init_device (); 00421 00422 init_decoder (); 00423 00424 start_capturing (); 00425 00426 mainloop (); 00427 00428 exit (EXIT_SUCCESS); 00429 00430 return 0; 00431 } 00432 00433 #else /* !ENABLE_V4L2 */ 00434 00435 int 00436 main (int argc, 00437 char ** argv) 00438 { 00439 fprintf (stderr, "Sorry, V4L2 only. Patches welcome.\n"); 00440 00441 exit (EXIT_FAILURE); 00442 00443 return 0; 00444 } 00445 00446 #endif /* !ENABLE_V4L2 */