examples/wss.c

WSS capture example.

/*
* libzvbi WSS capture example
*
* Copyright (C) 2005 Michael H. Schimek
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* $Id: wss.c,v 1.11 2008-02-19 00:52:04 mschimek Exp $ */
/* This example shows how to extract Wide Screen Signalling data
(EN 300 294) from video images. The signal is transmitted on the
first half of PAL/SECAM scan line 23, which ITU-R BT.601 defines
as the first line of a 576 line picture.
The author is not aware of any drivers which can capture a scan
line as raw VBI and video data at the same time, and sliced VBI
capturing is not supported yet by libzvbi. Note some drivers like
the Linux saa7134 driver cannot capture line 23 at all.
gcc -o wss wss.c `pkg-config zvbi-0.2 --cflags --libs` */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifdef ENABLE_V4L2
#include <fcntl.h> /* low-level i/o */
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <libzvbi.h>
#include <asm/types.h> /* for videodev2.h */
#include "videodev2k.h"
#define CLEAR(x) memset (&(x), 0, sizeof (x))
struct buffer {
void * start;
size_t length;
};
static const char * dev_name = "/dev/video";
static int fd;
static struct buffer * buffers;
static unsigned int n_buffers;
static int quit;
static vbi_raw_decoder rd;
static void
errno_exit (const char * s)
{
fprintf (stderr, "%s error %d, %s\n",
s, errno, strerror (errno));
exit (EXIT_FAILURE);
}
static int
xioctl (int fd,
int request,
void * p)
{
int r;
do r = ioctl (fd, request, p);
while (-1 == r && EINTR == errno);
return r;
}
static void
decode_wss_625 (uint8_t * buf)
{
static const char *formats [] = {
"Full format 4:3, 576 lines",
"Letterbox 14:9 centre, 504 lines",
"Letterbox 14:9 top, 504 lines",
"Letterbox 16:9 centre, 430 lines",
"Letterbox 16:9 top, 430 lines",
"Letterbox > 16:9 centre",
"Full format 14:9 centre, 576 lines",
"Anamorphic 16:9, 576 lines"
};
static const char *subtitles [] = {
"none",
"in active image area",
"out of active image area",
"<invalid>"
};
int g1;
int parity;
g1 = buf[0] & 15;
parity = g1;
parity ^= parity >> 2;
parity ^= parity >> 1;
g1 &= 7;
printf ("WSS PAL: ");
if (!(parity & 1))
printf ("<parity error> ");
printf ("%s; %s mode; %s colour coding; %s helper; "
"reserved b7=%d; %s Teletext subtitles; "
"open subtitles: %s; %s surround sound; "
"copyright %s; copying %s\n",
formats[g1],
(buf[0] & 0x10) ? "film" : "camera",
(buf[0] & 0x20) ? "MA/CP" : "standard",
(buf[0] & 0x40) ? "modulated" : "no",
!!(buf[0] & 0x80),
(buf[1] & 0x01) ? "have" : "no",
subtitles[(buf[1] >> 1) & 3],
(buf[1] & 0x08) ? "have" : "no",
(buf[1] & 0x10) ? "asserted" : "unknown",
(buf[1] & 0x20) ? "restricted" : "not restricted");
}
static void
process_image (const void * p)
{
vbi_sliced sliced[1];
unsigned int n_lines;
n_lines = vbi_raw_decode (&rd, (uint8_t *) p, sliced);
if (0 /* test */) {
/* Error ignored. */
if (write (STDOUT_FILENO, p, rd.bytes_per_line) == -1)
fprintf(stderr, "Failed to write to file\n");
} else if (n_lines > 0) {
assert (VBI_SLICED_WSS_625 == sliced[0].id);
assert (1 == n_lines);
decode_wss_625 (sliced[0].data);
} else {
fputc ('.', stdout);
fflush (stdout);
}
}
static void
init_decoder (void)
{
unsigned int services;
vbi_raw_decoder_init (&rd);
rd.scanning = 625;
rd.sampling_format = VBI_PIXFMT_YUYV;
/* Should be calculated from VIDIOC_CROPCAP information.
Common sampling rates are 14.75 MHz to get 768 PAL/SECAM
square pixels per line, and 13.5 MHz according to ITU-R Rec.
BT.601 with 720 pixels/line. Note BT.601 overscans the line:
13.5e6 / 720 > 14.75e6 / 768. Don't be fooled by a driver
scaling 768 square pixels to 720. */
rd.sampling_rate = 768 * 14.75e6 / 768;
rd.bytes_per_line = 768 * 2;
/* Should be calculated from VIDIOC_CROPCAP information. */
rd.offset = 0;
rd.start[0] = 23;
rd.count[0] = 1;
rd.start[1] = 0;
rd.count[1] = 0;
rd.interlaced = FALSE; /* just one line */
rd.synchronous = TRUE;
services = vbi_raw_decoder_add_services (&rd,
VBI_SLICED_WSS_625,
/* strict */ 2);
if (0 == services) {
fprintf (stderr, "Cannot decode WSS\n");
exit (EXIT_FAILURE);
}
}
static void
mainloop (void)
{
quit = 0;
while (!quit) {
struct v4l2_buffer buf;
for (;;) {
fd_set fds;
struct timeval tv;
int r;
FD_ZERO (&fds);
FD_SET (fd, &fds);
tv.tv_sec = 2;
tv.tv_usec = 0;
r = select (fd + 1, &fds, NULL, NULL, &tv);
if (-1 == r) {
if (EINTR == errno) {
/* XXX should subtract the elapsed
time from timeout here. */
continue;
}
errno_exit ("select");
}
if (0 == r) {
fprintf (stderr, "select timeout\n");
exit (EXIT_FAILURE);
}
break;
}
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
if (EAGAIN == errno)
continue;
errno_exit ("VIDIOC_DQBUF");
}
assert (buf.index < n_buffers);
process_image (buffers[buf.index].start);
if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
errno_exit ("VIDIOC_QBUF");
}
}
static void
start_capturing (void)
{
unsigned int i;
enum v4l2_buf_type type;
for (i = 0; i < n_buffers; ++i) {
struct v4l2_buffer buf;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
errno_exit ("VIDIOC_QBUF");
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
errno_exit ("VIDIOC_STREAMON");
}
static void
init_device (void)
{
struct v4l2_capability cap;
v4l2_std_id std_id;
struct v4l2_format fmt;
struct v4l2_requestbuffers req;
if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) {
if (EINVAL == errno) {
fprintf (stderr, "%s is no V4L2 device\n",
dev_name);
exit (EXIT_FAILURE);
} else {
errno_exit ("VIDIOC_QUERYCAP");
}
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
fprintf (stderr, "%s is no video capture device\n",
dev_name);
exit (EXIT_FAILURE);
}
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
fprintf (stderr, "%s does not support streaming I/O\n",
dev_name);
exit (EXIT_FAILURE);
}
std_id = V4L2_STD_PAL;
if (-1 == xioctl (fd, VIDIOC_S_STD, &std_id))
errno_exit ("VIDIOC_S_STD");
CLEAR (fmt);
/* We need the top field without vertical scaling,
width must be at least 320 pixels. */
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 768;
fmt.fmt.pix.height = 576;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))
errno_exit ("VIDIOC_S_FMT");
/* XXX the driver may adjust width and height, some
even change the pixelformat, that should be checked here. */
CLEAR (req);
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {
if (EINVAL == errno) {
fprintf (stderr, "%s does not support "
"memory mapping\n", dev_name);
exit (EXIT_FAILURE);
} else {
errno_exit ("VIDIOC_REQBUFS");
}
}
if (req.count < 2) {
fprintf (stderr, "Insufficient buffer memory on %s\n",
dev_name);
exit (EXIT_FAILURE);
}
buffers = calloc (req.count, sizeof (*buffers));
if (!buffers) {
fprintf (stderr, "Out of memory\n");
exit (EXIT_FAILURE);
}
for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
struct v4l2_buffer buf;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf))
errno_exit ("VIDIOC_QUERYBUF");
buffers[n_buffers].length = buf.length;
buffers[n_buffers].start =
mmap (NULL /* start anywhere */,
buf.length,
PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,
fd, buf.m.offset);
if (MAP_FAILED == buffers[n_buffers].start)
errno_exit ("mmap");
}
}
static void
open_device (void)
{
struct stat st;
if (-1 == stat (dev_name, &st)) {
fprintf (stderr, "Cannot identify '%s': %d, %s\n",
dev_name, errno, strerror (errno));
exit (EXIT_FAILURE);
}
if (!S_ISCHR (st.st_mode)) {
fprintf (stderr, "%s is no device\n", dev_name);
exit (EXIT_FAILURE);
}
fd = open (dev_name, O_RDWR | O_NONBLOCK, 0);
if (-1 == fd) {
fprintf (stderr, "Cannot open '%s': %d, %s\n",
dev_name, errno, strerror (errno));
exit (EXIT_FAILURE);
}
}
int
main (void)
{
/* Helps debugging. */
vbi_set_log_fn (/* mask: log everything */ -1,
vbi_log_on_stderr,
/* user_data */ NULL);
open_device ();
init_device ();
init_decoder ();
start_capturing ();
mainloop ();
exit (EXIT_SUCCESS);
return 0;
}
#else /* !ENABLE_V4L2 */
int
main (int argc,
char ** argv)
{
fprintf (stderr, "Sorry, V4L2 only. Patches welcome.\n");
exit (EXIT_FAILURE);
return 0;
}
#endif /* !ENABLE_V4L2 */
vbi_set_log_fn
void vbi_set_log_fn(vbi_log_mask mask, vbi_log_fn *log_fn, void *user_data)
Definition vbi.c:125
vbi_raw_decoder_add_services
unsigned int vbi_raw_decoder_add_services(vbi_raw_decoder *rd, unsigned int services, int strict)
Definition decoder.c:670
vbi_raw_decode
int vbi_raw_decode(vbi_raw_decoder *rd, uint8_t *raw, vbi_sliced *out)
Definition decoder.c:507
vbi_raw_decoder_init
void vbi_raw_decoder_init(vbi_raw_decoder *rd)
Definition decoder.c:819
vbi_raw_decoder
Raw vbi decoder context.
Definition decoder.h:308
vbi_raw_decoder::sampling_format
vbi_pixfmt sampling_format
Definition decoder.h:319
vbi_raw_decoder::bytes_per_line
int bytes_per_line
Definition decoder.h:331
vbi_raw_decoder::sampling_rate
int sampling_rate
Definition decoder.h:324
vbi_raw_decoder::scanning
int scanning
Definition decoder.h:315
vbi_raw_decoder::interlaced
vbi_bool interlaced
Definition decoder.h:360
vbi_raw_decoder::count
int count[2]
Definition decoder.h:352
vbi_raw_decoder::start
int start[2]
Definition decoder.h:345
vbi_raw_decoder::synchronous
vbi_bool synchronous
Definition decoder.h:368
vbi_raw_decoder::offset
int offset
Definition decoder.h:338
vbi_sliced
This structure holds one scan line of sliced vbi data.
Definition sliced.h:320