examples/wss.c

WSS capture example.

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 */

Generated on Fri Apr 27 20:40:21 2007 for ZVBI Library by  doxygen 1.4.7