closedcaption: Include zvbi raw vbi decoder code
authorEdward Hervey <edward@centricular.com>
Thu, 15 Mar 2018 06:07:16 +0000 (07:07 +0100)
committerEdward Hervey <bilboed@bilboed.com>
Mon, 28 May 2018 12:50:01 +0000 (14:50 +0200)
Current code from zapping/zvbi as of 2018-03-14. Files copied
are all LGPL v2+.

Changes from original zvbi code:
* Switch to gst-debug logging system
* Use glib for endianness detection
* Fix compilation warnings

13 files changed:
ext/closedcaption/Makefile.am
ext/closedcaption/bcd.h [new file with mode: 0644]
ext/closedcaption/bit_slicer.c [new file with mode: 0644]
ext/closedcaption/bit_slicer.h [new file with mode: 0644]
ext/closedcaption/decoder.c [new file with mode: 0644]
ext/closedcaption/decoder.h [new file with mode: 0644]
ext/closedcaption/macros.h [new file with mode: 0644]
ext/closedcaption/misc.h [new file with mode: 0644]
ext/closedcaption/raw_decoder.c [new file with mode: 0644]
ext/closedcaption/raw_decoder.h [new file with mode: 0644]
ext/closedcaption/sampling_par.c [new file with mode: 0644]
ext/closedcaption/sampling_par.h [new file with mode: 0644]
ext/closedcaption/sliced.h [new file with mode: 0644]

index 13185e7..2941e85 100644 (file)
@@ -1,6 +1,24 @@
 plugin_LTLIBRARIES = libgstclosedcaption.la
 
+zvbi_sources = \
+       bit_slicer.c \
+       decoder.c \
+        raw_decoder.c \
+       sampling_par.c
+
+zvbi_headers = \
+       bcd.h \
+       bit_slicer.h \
+       decoder.h \
+       macros.h \
+       misc.h \
+       raw_decoder.h \
+       sampling_par.h \
+       sliced.h
+
 libgstclosedcaption_la_SOURCES = \
+       $(zvbi_sources) \
+       $(zvbi_headers) \
        gstccextractor.c \
        gstccextractor.h \
        gstclosedcaption.c
@@ -18,4 +36,5 @@ libgstclosedcaption_la_LIBADD = \
 libgstclosedcaption_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 
 noinst_HEADERS = \
-       gstccextractor.h
+       gstccextractor.h \
+       $(zvbi_headers)
diff --git a/ext/closedcaption/bcd.h b/ext/closedcaption/bcd.h
new file mode 100644 (file)
index 0000000..ad63bf4
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ *  libzvbi -- BCD arithmetic for Teletext page numbers
+ *
+ *  Copyright (C) 2001, 2002 Michael H. Schimek
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the 
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA  02110-1301  USA.
+ */
+
+/* $Id: bcd.h,v 1.19 2008-02-21 07:18:52 mschimek Exp $ */
+
+#ifndef BCD_H
+#define BCD_H
+
+#include "misc.h"
+
+/**
+ * @addtogroup BCD BCD arithmetic for Teletext page numbers
+ * @ingroup HiDec
+ *
+ * Teletext page numbers are expressed as packed binary coded decimal
+ * numbers in range 0x100 to 0x8FF. The bcd format encodes one decimal
+ * digit in every hex nibble (four bits) of the number. Page numbers
+ * containing digits 0xA to 0xF are reserved for various system purposes
+ * and not intended for display.
+ */
+
+/* Public */
+
+/**
+ * @ingroup HiDec
+ * 
+ * Teletext or Closed Caption page number. For Teletext pages
+ * this is a packed bcd number in range 0x100 ... 0x8FF. Page
+ * numbers containing digits 0xA to 0xF are reserved for various
+ * system purposes, these pages are not intended for display.
+ * 
+ * Closed Caption page numbers between 1 ... 8 correspond
+ * to the four Caption and Text channels:
+ * <table>
+ * <tr><td>1</td><td>Caption 1</td><td>
+ *   "Primary synchronous caption service [English]"</td></tr>
+ * <tr><td>2</td><td>Caption 2</td><td>
+ *   "Special non-synchronous data that is intended to
+ *   augment information carried in the program"</td></tr>
+ * <tr><td>3</td><td>Caption 3</td><td>
+ *   "Secondary synchronous caption service, usually
+ *    second language [Spanish, French]"</td></tr>
+ * <tr><td>4</td><td>Caption 4</td><td>
+ *   "Special non-synchronous data similar to Caption 2"</td></tr>
+ * <tr><td>5</td><td>Text 1</td><td>
+ *   "First text service, data usually not program related"</td></tr>
+ * <tr><td>6</td><td>Text 2</td><td>
+ *   "Second text service, additional data usually not program related
+ *    [ITV data]"</td></tr>
+ * <tr><td>7</td><td>Text 3</td><td>
+ *   "Additional text channel"</td></tr>
+ * <tr><td>8</td><td>Text 4</td><td>
+ *   "Additional text channel"</td></tr>
+ * </table>
+ */
+/* XXX unsigned? */
+typedef int vbi_pgno;
+
+/**
+ * @ingroup HiDec
+ *
+ * This is the subpage number only applicable to Teletext pages,
+ * a packed bcd number in range 0x00 ... 0x99. On special 'clock' pages
+ * (for example listing the current time in different time zones)
+ * it can assume values between 0x0000 ... 0x2359 expressing
+ * local time. These are not actually subpages.
+ */
+typedef int vbi_subno;
+
+/**
+ * @ingroup HiDec
+ */
+#define VBI_ANY_SUBNO 0x3F7F
+/**
+ * @ingroup HiDec
+ */
+#define VBI_NO_SUBNO 0x3F7F
+
+/**
+ * @ingroup BCD
+ * @param dec Decimal number.
+ * 
+ * Converts a two's complement binary between 0 ... 999 to a
+ * packed bcd number in range  0x000 ... 0x999. Extra digits in
+ * the input will be discarded.
+ * 
+ * @return
+ * BCD number.
+ */
+_vbi_inline unsigned int
+vbi_dec2bcd(unsigned int dec)
+{
+       return (dec % 10) + ((dec / 10) % 10) * 16 + ((dec / 100) % 10) * 256;
+}
+
+/**
+ * @ingroup BCD
+ * @since 0.2.28
+ */
+#define vbi_bin2bcd(n) vbi_dec2bcd(n)
+
+/**
+ * @ingroup BCD
+ * @param bcd BCD number.
+ * 
+ * Converts a packed bcd number between 0x000 ... 0xFFF to a two's
+ * complement binary in range 0 ... 999. Extra digits in the input
+ * will be discarded.
+ * 
+ * @return
+ * Decimal number. The result is undefined when the bcd number contains
+ * hex digits 0xA ... 0xF.
+ **/
+_vbi_inline unsigned int
+vbi_bcd2dec(unsigned int bcd)
+{
+       return (bcd & 15) + ((bcd >> 4) & 15) * 10 + ((bcd >> 8) & 15) * 100;
+}
+
+/**
+ * @ingroup BCD
+ * @since 0.2.28
+ */
+#define vbi_bcd2bin(n) vbi_bcd2dec(n)
+
+/**
+ * @ingroup BCD
+ * @param a BCD number.
+ * @param b BCD number.
+ * 
+ * Adds two packed bcd numbers, returning a packed bcd sum. Arguments
+ * and result are in range 0xF000&nbsp;0000 ... 0x0999&nbsp;9999, that
+ * is -10**7 ... +10**7 - 1 in decimal notation. To subtract you can
+ * add the 10's complement, e. g. -1 = 0xF999&nbsp;9999.
+ * 
+ * @return
+ * Packed bcd number. The result is undefined when any of the arguments
+ * contain hex digits 0xA ... 0xF.
+ */
+_vbi_inline unsigned int
+vbi_add_bcd(unsigned int a, unsigned int b)
+{
+       unsigned int t;
+
+       a += 0x06666666;
+       t  = a + b;
+       b ^= a ^ t;
+       b  = (~b & 0x11111110) >> 3;
+       b |= b * 2;
+
+       return t - b;
+}
+
+/**
+ * @ingroup BCD
+ * @param bcd BCD number.
+ * 
+ * Tests if @a bcd forms a valid BCD number. The argument must be
+ * in range 0x0000&nbsp;0000 ... 0x0999&nbsp;9999.
+ * 
+ * @return
+ * @c FALSE if @a bcd contains hex digits 0xA ... 0xF.
+ */
+_vbi_inline vbi_bool
+vbi_is_bcd(unsigned int bcd)
+{
+       static const unsigned int x = 0x06666666;
+
+       return (((bcd + x) ^ (bcd ^ x)) & 0x11111110) == 0;
+}
+
+/**
+ * @ingroup BCD
+ * @param bcd Unsigned BCD number.
+ * @param maximum Unsigned maximum value.
+ *
+ * Compares an unsigned packed bcd number digit-wise against a maximum
+ * value, for example 0x295959. @a maximum can contain digits 0x0
+ * ... 0xF.
+ *
+ * @return
+ * @c TRUE if any digit of @a bcd is greater than the
+ * corresponding digit of @a maximum.
+ *
+ * @since 0.2.28
+ */
+_vbi_inline vbi_bool
+vbi_bcd_digits_greater         (unsigned int           bcd,
+                                unsigned int           maximum)
+{
+       maximum ^= ~0;
+
+       return 0 != (((bcd + maximum) ^ bcd ^ maximum) & 0x11111110);
+}
+
+/* Private */
+
+#endif /* BCD_H */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/ext/closedcaption/bit_slicer.c b/ext/closedcaption/bit_slicer.c
new file mode 100644 (file)
index 0000000..3bb8702
--- /dev/null
@@ -0,0 +1,1017 @@
+/*
+ *  libzvbi - Bit slicer
+ *
+ *  Copyright (C) 2000-2007 Michael H. Schimek
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the 
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA  02110-1301  USA.
+ */
+
+/* $Id: bit_slicer.c,v 1.16 2008-02-19 00:35:14 mschimek Exp $ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "misc.h"
+#include "bit_slicer.h"
+
+#  define VBI_PIXFMT_Y8 VBI_PIXFMT_YUV420
+#  define VBI_PIXFMT_RGB24_LE VBI_PIXFMT_RGB24
+#  define VBI_PIXFMT_BGR24_LE VBI_PIXFMT_BGR24
+#  define VBI_PIXFMT_RGBA24_LE VBI_PIXFMT_RGBA32_LE
+#  define VBI_PIXFMT_BGRA24_LE VBI_PIXFMT_BGRA32_LE
+#  define VBI_PIXFMT_RGBA24_BE VBI_PIXFMT_RGBA32_BE
+#  define VBI_PIXFMT_BGRA24_BE VBI_PIXFMT_BGRA32_BE
+#  define VBI_PIXFMT_RGB8 101
+#  define vbi_pixfmt_bytes_per_pixel VBI_PIXFMT_BPP
+
+/**
+ * $addtogroup BitSlicer Bit Slicer
+ * $ingroup Raw
+ * $brief Converting a single scan line of raw VBI
+ *   data to sliced VBI data.
+ *
+ * These are low level functions most useful if you want to decode
+ * data services not covered by libzvbi. Usually you will want to
+ * use the raw VBI decoder, converting several lines of different
+ * data services at once.
+ */
+
+/* This is time critical, tinker with care.
+
+   What about all these macros? They are templates to avoid a
+   pixel format switch within time critical loops. Instead we
+   compile bit slicer functions for different pixel formats.
+
+   I would use inline functions for proper type checking, but
+   there's no guarantee the compiler really will inline. */
+
+/* Read a green sample, e.g. rrrrrggg gggbbbbb. endian is const. */
+#define GREEN2(raw, endian)                                            \
+       (((raw)[0 + endian] + (raw)[1 - endian] * 256) & bs->green_mask)
+
+/* Read a sample with pixfmt conversion. pixfmt is const. */
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define GREEN(raw)                                                     \
+       ((VBI_PIXFMT_RGB8 == pixfmt) ?                                  \
+        *(const uint8_t *)(raw) & bs->green_mask :                     \
+        ((VBI_PIXFMT_RGB16_LE == pixfmt) ?                             \
+         *(const uint16_t *)(raw) & bs->green_mask :                   \
+         ((VBI_PIXFMT_RGB16_BE == pixfmt) ?                            \
+          GREEN2 (raw, 1) :                                            \
+          (raw)[0])))
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+#define GREEN(raw)                                                     \
+       ((VBI_PIXFMT_RGB8 == pixfmt) ?                                  \
+        *(const uint8_t *)(raw) & bs->green_mask :                     \
+        ((VBI_PIXFMT_RGB16_LE == pixfmt) ?                             \
+         GREEN2 (raw, 0) :                                             \
+         ((VBI_PIXFMT_RGB16_BE == pixfmt) ?                            \
+          *(const uint16_t *)(raw) & bs->green_mask :                  \
+          (raw)[0])))
+#else
+#define GREEN(raw)                                                     \
+       ((VBI_PIXFMT_RGB8 == pixfmt) ?                                  \
+        *(const uint8_t *)(raw) & bs->green_mask :                     \
+        ((VBI_PIXFMT_RGB16_LE == pixfmt) ?                             \
+         GREEN2 (raw, 0) :                                             \
+         ((VBI_PIXFMT_RGB16_BE == pixfmt) ?                            \
+          GREEN2 (raw, 1) :                                            \
+          (raw)[0])))
+#endif
+
+/* raw0 = raw[index >> 8], linear interpolated. */
+#define SAMPLE(_kind)                                                  \
+do {                                                                   \
+       const uint8_t *r;                                               \
+                                                                       \
+       r = raw + (i >> 8) * bpp;                                       \
+       raw0 = GREEN (r);                                               \
+       raw1 = GREEN (r + bpp);                                         \
+       raw0 = (int)(raw1 - raw0) * (i & 255) + (raw0 << 8);            \
+       if (collect_points) {                                           \
+               points->kind = _kind;                                   \
+               points->index = (raw - raw_start) * 256 + i;            \
+               points->level = raw0;                                   \
+               points->thresh = tr;                                    \
+               ++points;                                               \
+       }                                                               \
+} while (0)
+
+#define PAYLOAD()                                                      \
+do {                                                                   \
+       i = bs->phase_shift; /* current bit position << 8 */            \
+       tr *= 256;                                                      \
+       c = 0;                                                          \
+                                                                       \
+       for (j = bs->frc_bits; j > 0; --j) {                            \
+               SAMPLE (VBI3_FRC_BIT);                                  \
+               c = c * 2 + (raw0 >= tr);                               \
+               i += bs->step; /* next bit */                           \
+       }                                                               \
+                                                                       \
+       if (c != bs->frc)                                               \
+               return FALSE;                                           \
+                                                                       \
+       switch (bs->endian) {                                           \
+       case 3: /* bitwise, lsb first */                                \
+               for (j = 0; j < bs->payload; ++j) {                     \
+                       SAMPLE (VBI3_PAYLOAD_BIT);                      \
+                       c = (c >> 1) + ((raw0 >= tr) << 7);             \
+                       i += bs->step;                                  \
+                       if ((j & 7) == 7)                               \
+                               *buffer++ = c;                          \
+               }                                                       \
+               *buffer = c >> ((8 - bs->payload) & 7);                 \
+               break;                                                  \
+                                                                       \
+       case 2: /* bitwise, msb first */                                \
+               for (j = 0; j < bs->payload; ++j) {                     \
+                       SAMPLE (VBI3_PAYLOAD_BIT);                      \
+                       c = c * 2 + (raw0 >= tr);                       \
+                       i += bs->step;                                  \
+                       if ((j & 7) == 7)                               \
+                               *buffer++ = c;                          \
+               }                                                       \
+               *buffer = c & ((1 << (bs->payload & 7)) - 1);           \
+               break;                                                  \
+                                                                       \
+       case 1: /* octets, lsb first */                                 \
+               for (j = bs->payload; j > 0; --j) {                     \
+                       for (k = 0, c = 0; k < 8; ++k) {                \
+                               SAMPLE (VBI3_PAYLOAD_BIT);              \
+                               c += (raw0 >= tr) << k;                 \
+                               i += bs->step;                          \
+                       }                                               \
+                       *buffer++ = c;                                  \
+               }                                                       \
+               break;                                                  \
+                                                                       \
+       default: /* octets, msb first */                                \
+               for (j = bs->payload; j > 0; --j) {                     \
+                       for (k = 0; k < 8; ++k) {                       \
+                               SAMPLE (VBI3_PAYLOAD_BIT);              \
+                               c = c * 2 + (raw0 >= tr);               \
+                               i += bs->step;                          \
+                       }                                               \
+                       *buffer++ = c;                                  \
+               }                                                       \
+               break;                                                  \
+       }                                                               \
+} while (0)
+
+#define CRI()                                                          \
+do {                                                                   \
+       unsigned int tavg;                                              \
+       unsigned char b; /* current bit */                              \
+                                                                       \
+       tavg = (t + (oversampling / 2)) / oversampling;                 \
+       b = (tavg >= tr);                                               \
+                                                                       \
+       if (unlikely (b ^ b1)) {                                        \
+               cl = bs->oversampling_rate >> 1;                        \
+       } else {                                                        \
+               cl += bs->cri_rate;                                     \
+                                                                       \
+               if (cl >= bs->oversampling_rate) {                      \
+                       if (collect_points) {                           \
+                               points->kind = VBI3_CRI_BIT;            \
+                               points->index = (raw - raw_start) << 8; \
+                               points->level = tavg << 8;              \
+                               points->thresh = tr << 8;               \
+                               ++points;                               \
+                       }                                               \
+                                                                       \
+                       cl -= bs->oversampling_rate;                    \
+                       c = c * 2 + b;                                  \
+                       if ((c & bs->cri_mask) == bs->cri) {            \
+                               PAYLOAD ();                             \
+                               if (collect_points) {                   \
+                                       *n_points = points              \
+                                               - points_start;         \
+                               }                                       \
+                               return TRUE;                            \
+                       }                                               \
+               }                                                       \
+       }                                                               \
+                                                                       \
+       b1 = b;                                                         \
+                                                                       \
+       if (oversampling > 1)                                           \
+               t += raw1;                                              \
+} while (0)
+
+#define CORE()                                                         \
+do {                                                                   \
+       const uint8_t *raw_start;                                       \
+       unsigned int i, j, k;                                           \
+       unsigned int cl;        /* clock */                             \
+       unsigned int thresh0;   /* old 0/1 threshold */                 \
+       unsigned int tr;        /* current threshold */                 \
+       unsigned int c;         /* current byte */                      \
+       unsigned int t;         /* t = raw[0] * j + raw[1] * (1 - j) */ \
+       unsigned int raw0;      /* oversampling temporary */            \
+       unsigned int raw1;                                              \
+       unsigned char b1;       /* previous bit */                      \
+                                                                       \
+       thresh0 = bs->thresh;                                           \
+       raw_start = raw;                                                \
+       raw += bs->skip;                                                \
+                                                                       \
+       cl = 0;                                                         \
+       c = 0;                                                          \
+       b1 = 0;                                                         \
+                                                                       \
+       for (i = bs->cri_samples; i > 0; --i) {                         \
+               tr = bs->thresh >> thresh_frac;                         \
+               raw0 = GREEN (raw);                                     \
+               raw1 = GREEN (raw + bpp);                               \
+               raw1 -= raw0;                                           \
+               bs->thresh += (int)(raw0 - tr) * (int) ABS ((int) raw1); \
+               t = raw0 * oversampling;                                \
+                                                                       \
+               for (j = oversampling; j > 0; --j)                      \
+                       CRI ();                                         \
+                                                                       \
+               raw += bpp;                                             \
+       }                                                               \
+                                                                       \
+       bs->thresh = thresh0;                                           \
+                                                                       \
+       if (collect_points)                                             \
+               *n_points = points - points_start;                      \
+                                                                       \
+       return FALSE;                                                   \
+} while (0)
+
+#define BIT_SLICER(fmt, os, tf)                                                \
+static vbi_bool                                                                \
+bit_slicer_ ## fmt             (vbi3_bit_slicer *      bs,             \
+                                uint8_t *              buffer,         \
+                                vbi3_bit_slicer_point *points,         \
+                                unsigned int *         n_points,       \
+                                const uint8_t *        raw)            \
+{                                                                      \
+       static const vbi_pixfmt pixfmt = VBI_PIXFMT_ ## fmt;            \
+       unsigned int bpp =                                              \
+               vbi_pixfmt_bytes_per_pixel (VBI_PIXFMT_ ## fmt);        \
+       static const unsigned int oversampling = os;                    \
+       static const vbi3_bit_slicer_point *points_start = NULL;        \
+       static const vbi_bool collect_points = FALSE;                   \
+       unsigned int thresh_frac = tf;                                  \
+                                                                       \
+       CORE ();                                                        \
+}
+
+#define DEF_THR_FRAC 9
+
+BIT_SLICER (Y8, 4, DEF_THR_FRAC)        /* any format with 0 bytes between Y or G */
+    BIT_SLICER (YUYV, 4, DEF_THR_FRAC)  /* 1 byte */
+    BIT_SLICER (RGB24_LE, 4, DEF_THR_FRAC)      /* 2 bytes */
+    BIT_SLICER (RGBA24_LE, 4, DEF_THR_FRAC)     /* 3 bytes */
+    BIT_SLICER (RGB16_LE, 4, bs->thresh_frac)
+    BIT_SLICER (RGB16_BE, 4, bs->thresh_frac)
+
+     static const unsigned int LP_AVG = 4;
+
+     static vbi_bool
+         low_pass_bit_slicer_Y8 (vbi3_bit_slicer * bs,
+    uint8_t * buffer,
+    vbi3_bit_slicer_point * points, unsigned int *n_points, const uint8_t * raw)
+{
+  vbi3_bit_slicer_point *points_start;
+  const uint8_t *raw_start;
+  unsigned int i, j, k, m;
+  unsigned int cl;              /* clock */
+  unsigned int thresh0;         /* old 0/1 threshold */
+  unsigned int tr;              /* current threshold */
+  unsigned int c;               /* current byte */
+  unsigned int raw0;            /* oversampling temporary */
+  unsigned char b1;             /* previous bit */
+  unsigned int bps;
+  unsigned int raw0sum;
+
+  points_start = points;
+
+  raw_start = raw;
+  raw += bs->skip;
+
+  bps = bs->bytes_per_sample;
+
+  thresh0 = bs->thresh;
+
+  c = -1;
+  cl = 0;
+  b1 = 0;
+
+  raw0sum = raw[0];
+  for (m = bps; m < (bps << LP_AVG); m += bps) {
+    raw0sum += raw[m];
+  }
+
+  i = bs->cri_samples;
+
+  for (;;) {
+    unsigned char b;            /* current bit */
+
+    tr = bs->thresh >> bs->thresh_frac;
+    raw0 = raw0sum;
+    raw0sum = raw0sum + raw[bps << LP_AVG]
+        - raw[0];
+    raw += bps;
+    bs->thresh += (int) (raw0 - tr)
+        * (int) ABS ((int) (raw0sum - raw0));
+
+    b = (raw0 >= tr);
+
+    if (unlikely (b ^ b1)) {
+      cl = bs->oversampling_rate >> 1;
+    } else {
+      cl += bs->cri_rate;
+
+      if (cl >= bs->oversampling_rate) {
+        if (unlikely (NULL != points)) {
+          points->kind = VBI3_CRI_BIT;
+          points->index = (raw - raw_start)
+              * 256 / bs->bytes_per_sample + (1 << LP_AVG) * 128;
+          points->level = raw0 << (8 - LP_AVG);
+          points->thresh = tr << (8 - LP_AVG);
+          ++points;
+        }
+
+        cl -= bs->oversampling_rate;
+        c = c * 2 + b;
+        if ((c & bs->cri_mask) == bs->cri) {
+          break;
+        }
+      }
+    }
+
+    b1 = b;
+
+    if (0 == --i) {
+      bs->thresh = thresh0;
+
+      if (unlikely (NULL != points))
+        *n_points = points - points_start;
+
+      return FALSE;
+    }
+  }
+
+#define LP_SAMPLE(_kind)                                               \
+do {                                                                   \
+       unsigned int ii = (i >> 8) * bps;                               \
+                                                                       \
+       raw0 = raw[ii];                                                 \
+       for (m = bps; m < (bps << LP_AVG); m += bps)                    \
+               raw0 += raw[ii + m];                                    \
+       if (unlikely (NULL != points)) {                                \
+               points->kind = _kind;                                   \
+               points->index = (raw - raw_start)                       \
+                       * 256 / bs->bytes_per_sample                    \
+                       + (1 << LP_AVG) * 128                           \
+                       + ii * 256;                                     \
+               points->level = raw0 << (8 - LP_AVG);                   \
+               points->thresh = tr << (8 - LP_AVG);                    \
+               ++points;                                               \
+       }                                                               \
+} while (0)
+
+  i = bs->phase_shift;          /* current bit position << 8 */
+  c = 0;
+
+  for (j = bs->frc_bits; j > 0; --j) {
+    LP_SAMPLE (VBI3_FRC_BIT);
+    c = c * 2 + (raw0 >= tr);
+    i += bs->step;              /* next bit */
+  }
+
+  if (c != bs->frc)
+    return FALSE;
+
+  c = 0;
+
+  switch (bs->endian) {
+    case 3:                    /* bitwise, lsb first */
+      for (j = 0; j < bs->payload; ++j) {
+        LP_SAMPLE (VBI3_PAYLOAD_BIT);
+        c = (c >> 1) + ((raw0 >= tr) << 7);
+        i += bs->step;
+        if ((j & 7) == 7)
+          *buffer++ = c;
+      }
+      *buffer = c >> ((8 - bs->payload) & 7);
+      break;
+
+    case 2:                    /* bitwise, msb first */
+      for (j = 0; j < bs->payload; ++j) {
+        LP_SAMPLE (VBI3_PAYLOAD_BIT);
+        c = c * 2 + (raw0 >= tr);
+        i += bs->step;
+        if ((j & 7) == 7)
+          *buffer++ = c;
+      }
+      *buffer = c & ((1 << (bs->payload & 7)) - 1);
+      break;
+
+    case 1:                    /* octets, lsb first */
+      j = bs->payload;
+      do {
+        for (k = 0; k < 8; ++k) {
+          LP_SAMPLE (VBI3_PAYLOAD_BIT);
+          c = (c >> 1) + ((raw0 >= tr) << 7);
+          i += bs->step;
+        }
+        *buffer++ = c;
+      } while (--j > 0);
+      break;
+
+    default:                   /* octets, msb first */
+      j = bs->payload;
+      do {
+        for (k = 0; k < 8; ++k) {
+          LP_SAMPLE (VBI3_PAYLOAD_BIT);
+          c = c * 2 + (raw0 >= tr);
+          i += bs->step;
+        }
+        *buffer++ = c;
+      } while (--j > 0);
+      break;
+  }
+
+  if (unlikely (NULL != points)) {
+    *n_points = points - points_start;
+  }
+
+  return TRUE;
+}
+
+static vbi_bool
+null_function (vbi3_bit_slicer * bs,
+    uint8_t * buffer,
+    vbi3_bit_slicer_point * points, unsigned int *n_points, const uint8_t * raw)
+{
+  buffer = buffer;              /* unused */
+  points = points;
+  n_points = n_points;
+  raw = raw;
+
+  warning (&bs->log, "vbi3_bit_slicer_set_params() not called.");
+
+  return FALSE;
+}
+
+/**
+ * @param bs Pointer to vbi3_bit_slicer object allocated with
+ *   vbi3_bit_slicer_new().
+ * @param buffer Output data.
+ * @param buffer_size Size of the output buffer. The buffer must be
+ +   large enough to store the number of bits given as @a payload_bits to
+ *   vbi3_bit_slicer_new().
+ * @param points Information about the bits sampled by the bit slicer
+ *   are stored here.
+ * @param n_points The number of sampling points stored in the
+ *   @a points array will be stored here.
+ * @param max_points Size of the @a points array. The array must be
+ *   large enough to store one sampling point for all @a crc_bits,
+ *   @a frc_bits and @a payload_bits given to vbi3_bit_slicer_new().
+ * @param raw Input data. At least the number of pixels or samples
+ *  given as @a samples_per_line to vbi3_bit_slicer_new().
+ * 
+ * Like vbi3_bit_slicer_slice() but additionally provides information
+ * about where and how bits were sampled. This is mainly interesting
+ * for debugging.
+ *
+ * @returns
+ * @c FALSE if the @a buffer or @a points array is too small, if the
+ * pixel format is not supported or if the raw data does not contain
+ * the expected information, i. e. the CRI/FRC has not been found. In
+ * these cases the @a buffer remains unmodified but the @a points
+ * array may contain data.
+ *
+ * @bug
+ * Currently this function is only implemented for
+ * raw data in planar YUV formats and @c VBI3_PIXFMT_Y8.
+ */
+vbi_bool
+    vbi3_bit_slicer_slice_with_points
+    (vbi3_bit_slicer * bs,
+    uint8_t * buffer,
+    unsigned int buffer_size,
+    vbi3_bit_slicer_point * points,
+    unsigned int *n_points, unsigned int max_points, const uint8_t * raw) {
+  static const vbi_pixfmt pixfmt = VBI_PIXFMT_Y8;
+  static const unsigned int bpp = 1;
+  static const unsigned int oversampling = 4;   /* see above */
+  static const unsigned int thresh_frac = DEF_THR_FRAC;
+  static const vbi_bool collect_points = TRUE;
+  vbi3_bit_slicer_point *points_start;
+
+  assert (NULL != bs);
+  assert (NULL != buffer);
+  assert (NULL != points);
+  assert (NULL != n_points);
+  assert (NULL != raw);
+
+  points_start = points;
+  *n_points = 0;
+
+  if (bs->payload > buffer_size * 8) {
+    warning (&bs->log,
+        "buffer_size %u < %u bits of payload.", buffer_size * 8, bs->payload);
+    return FALSE;
+  }
+
+  if (bs->total_bits > max_points) {
+    warning (&bs->log,
+        "max_points %u < %u CRI, FRC and payload bits.",
+        max_points, bs->total_bits);
+    return FALSE;
+  }
+
+  if (low_pass_bit_slicer_Y8 == bs->func) {
+    return bs->func (bs, buffer, points, n_points, raw);
+  } else if (bit_slicer_Y8 != bs->func) {
+    warning (&bs->log,
+        "Function not implemented for pixfmt %u.", bs->sample_format);
+    return bs->func (bs, buffer,
+        /* points */ NULL,
+        /* n_points */ NULL,
+        raw);
+  }
+
+  CORE ();
+}
+
+/**
+ * @param bs Pointer to vbi3_bit_slicer object allocated with
+ *   vbi3_bit_slicer_new(). You must also call
+ *   vbi3_bit_slicer_set_params() before calling this function.
+ * @param buffer Output data.
+ * @param buffer_size Size of the output buffer. The buffer must be
+ +   large enough to store the number of bits given as @a payload to
+ *   vbi3_bit_slicer_new().
+ * @param raw Input data. At least the number of pixels or samples
+ *  given as @a samples_per_line to vbi3_bit_slicer_new().
+ * 
+ * Decodes one scan line of raw vbi data. Note the bit slicer tries
+ * to adapt to the average signal amplitude, you should avoid
+ * using the same vbi3_bit_slicer object for data from different
+ * devices.
+ *
+ * @return
+ * @c FALSE if the @a buffer is too small or if the raw data does not
+ * contain the expected information, i. e. the CRI/FRC has not been
+ * found. This may also result from a too weak or noisy signal. Error
+ * correction must be implemented at a higher layer. When the function
+ * fails, the @a buffer remains unmodified.
+ */
+vbi_bool
+vbi3_bit_slicer_slice (vbi3_bit_slicer * bs,
+    uint8_t * buffer, unsigned int buffer_size, const uint8_t * raw)
+{
+  assert (NULL != bs);
+  assert (NULL != buffer);
+  assert (NULL != raw);
+
+  if (bs->payload > buffer_size * 8) {
+    warning (&bs->log,
+        "buffer_size %u < %u bits of payload.", buffer_size * 8, bs->payload);
+    return FALSE;
+  }
+
+  return bs->func (bs, buffer,
+      /* points */ NULL,
+      /* n_points */ NULL,
+      raw);
+}
+
+/**
+ * @param bs Pointer to vbi3_bit_slicer object allocated with
+ *   vbi3_bit_slicer_new().
+ * @param sample_format Format of the raw data, see vbi3_pixfmt.
+ *   Note the bit slicer looks only at the green component of RGB
+ *   pixels.
+ * @param sampling_rate Raw vbi sampling rate in Hz, that is the number
+ *   of samples or pixels sampled per second by the hardware.
+ * @param sample_offset The bit slicer shall skip this number of samples at
+ *   the start of the line.
+ * @param samples_per_line Number of samples or pixels in one raw vbi
+ *   line later passed to vbi3_bit_slicer_slice(). This limits the number of
+ *   bytes read from the raw data buffer. Do not to confuse the value
+ *   with bytes per line.
+ * @param cri The Clock Run In is a NRZ modulated sequence of '1'
+ *   and '0' bits prepending most data transmissions to synchronize data
+ *   acquisition circuits. The bit slicer compares the bits in this
+ *   word, lsb last transmitted, against the transmitted CRI. Decoding
+ *   of FRC and payload starts with the next bit after a match, thus
+ *   @a cri must contain a unique bit sequence. For example 0xAB to
+ *   match '101010101011xxx'.
+ * @param cri_mask Of the CRI bits in @a cri, only these bits are
+ *   significant for a match. For instance it is wise not to rely on
+ *   the very first CRI bits transmitted.
+ * @param cri_bits Number of CRI bits, must not exceed 32.
+ * @param cri_rate CRI bit rate in Hz, the number of CRI bits
+ *   transmitted per second.
+ * @param cri_end Number of samples between the start of the line and
+ *   the latest possible end of the CRI. This is useful when
+ *   the transmission is much shorter than samples_per_line, otherwise
+ *   just pass @c ~0 and a limit will be calculated.
+ * @param frc The FRaming Code usually following the CRI is a bit
+ *   sequence identifying the data service. There is no mask parameter,
+ *   all bits must match. We assume FRC has the same @a modulation as
+ *   the payload and is transmitted at @a payload_rate.
+ * @param frc_bits Number of FRC bits, must not exceed 32.
+ * @param payload_bits Number of payload bits. Only this data
+ *   will be stored in the vbi3_bit_slicer_slice() output. If this number
+ *   is no multiple of eight, the most significant bits of the
+ *   last byte are undefined.
+ * @param payload_rate Payload bit rate in Hz, the number of payload
+ *   bits transmitted per second.
+ * @param modulation Modulation of the payload, see vbi3_modulation.
+ * 
+ * Initializes a vbi3_bit_slicer object for use with
+ * vbi3_bit_slicer_slice(). This is a low level function, see also
+ * vbi3_raw_decoder_new().
+ *
+ * @returns
+ * @c FALSE when the parameters are invalid (e. g.
+ * @a samples_per_line too small to contain CRI, FRC and payload).
+ */
+vbi_bool
+vbi3_bit_slicer_set_params (vbi3_bit_slicer * bs,
+    vbi_pixfmt sample_format,
+    unsigned int sampling_rate,
+    unsigned int sample_offset,
+    unsigned int samples_per_line,
+    unsigned int cri,
+    unsigned int cri_mask,
+    unsigned int cri_bits,
+    unsigned int cri_rate,
+    unsigned int cri_end,
+    unsigned int frc,
+    unsigned int frc_bits,
+    unsigned int payload_bits,
+    unsigned int payload_rate, vbi3_modulation modulation)
+{
+  unsigned int c_mask;
+  unsigned int f_mask;
+  unsigned int min_samples_per_bit;
+  unsigned int oversampling;
+  unsigned int data_bits;
+  unsigned int data_samples;
+  unsigned int cri_samples;
+  unsigned int skip;
+
+  assert (NULL != bs);
+  assert (cri_bits <= 32);
+  assert (frc_bits <= 32);
+  assert (payload_bits <= 32767);
+  assert (samples_per_line <= 32767);
+
+  if (cri_rate > sampling_rate) {
+    warning (&bs->log,
+        "cri_rate %u > sampling_rate %u.", cri_rate, sampling_rate);
+    goto failure;
+  }
+
+  if (payload_rate > sampling_rate) {
+    warning (&bs->log,
+        "payload_rate %u > sampling_rate %u.", payload_rate, sampling_rate);
+    goto failure;
+  }
+
+  min_samples_per_bit = sampling_rate / MAX (cri_rate, payload_rate);
+
+  bs->sample_format = sample_format;
+
+  c_mask = (cri_bits == 32) ? ~0U : (1U << cri_bits) - 1;
+  f_mask = (frc_bits == 32) ? ~0U : (1U << frc_bits) - 1;
+
+  oversampling = 4;
+  skip = 0;
+
+  /* 0-1 threshold, start value. */
+  bs->thresh = 105 << DEF_THR_FRAC;
+  bs->thresh_frac = DEF_THR_FRAC;
+
+  switch (sample_format) {
+    case VBI_PIXFMT_YUV420:
+      bs->bytes_per_sample = 1;
+      bs->func = bit_slicer_Y8;
+      if (min_samples_per_bit > (3U << (LP_AVG - 1))) {
+        bs->func = low_pass_bit_slicer_Y8;
+        oversampling = 1;
+        bs->thresh <<= LP_AVG - 2;
+        bs->thresh_frac += LP_AVG - 2;
+      }
+      break;
+
+
+    case VBI_PIXFMT_YUYV:
+    case VBI_PIXFMT_YVYU:
+      bs->bytes_per_sample = 2;
+      bs->func = bit_slicer_YUYV;
+      if (min_samples_per_bit > (3U << (LP_AVG - 1))) {
+        bs->func = low_pass_bit_slicer_Y8;
+        oversampling = 1;
+        bs->thresh <<= LP_AVG - 2;
+        bs->thresh_frac += LP_AVG - 2;
+      }
+      break;
+
+    case VBI_PIXFMT_UYVY:
+    case VBI_PIXFMT_VYUY:
+      skip = 1;
+      bs->bytes_per_sample = 2;
+      bs->func = bit_slicer_YUYV;
+      if (min_samples_per_bit > (3U << (LP_AVG - 1))) {
+        bs->func = low_pass_bit_slicer_Y8;
+        oversampling = 1;
+        bs->thresh <<= LP_AVG - 2;
+        bs->thresh_frac += LP_AVG - 2;
+      }
+      break;
+
+    case VBI_PIXFMT_RGBA24_LE:
+    case VBI_PIXFMT_BGRA24_LE:
+      skip = 1;
+      bs->bytes_per_sample = 4;
+      bs->func = bit_slicer_RGBA24_LE;
+      if (min_samples_per_bit > (3U << (LP_AVG - 1))) {
+        bs->func = low_pass_bit_slicer_Y8;
+        oversampling = 1;
+        bs->thresh <<= LP_AVG - 2;
+        bs->thresh_frac += LP_AVG - 2;
+      }
+      break;
+
+    case VBI_PIXFMT_RGBA24_BE:
+    case VBI_PIXFMT_BGRA24_BE:
+      skip = 2;
+      bs->bytes_per_sample = 4;
+      bs->func = bit_slicer_RGBA24_LE;
+      if (min_samples_per_bit > (3U << (LP_AVG - 1))) {
+        bs->func = low_pass_bit_slicer_Y8;
+        oversampling = 1;
+        bs->thresh <<= LP_AVG - 2;
+        bs->thresh_frac += LP_AVG - 2;
+      }
+      break;
+
+    case VBI_PIXFMT_RGB24_LE:
+    case VBI_PIXFMT_BGR24_LE:
+      skip = 1;
+      bs->bytes_per_sample = 3;
+      bs->func = bit_slicer_RGB24_LE;
+      if (min_samples_per_bit > (3U << (LP_AVG - 1))) {
+        bs->func = low_pass_bit_slicer_Y8;
+        oversampling = 1;
+        bs->thresh <<= LP_AVG - 2;
+        bs->thresh_frac += LP_AVG - 2;
+      }
+      break;
+
+    case VBI_PIXFMT_RGB16_LE:
+    case VBI_PIXFMT_BGR16_LE:
+      bs->func = bit_slicer_RGB16_LE;
+      bs->green_mask = 0x07E0;
+      bs->thresh = 105 << (5 - 2 + 12);
+      bs->thresh_frac = 12;
+      bs->bytes_per_sample = 2;
+      break;
+
+    case VBI_PIXFMT_RGB16_BE:
+    case VBI_PIXFMT_BGR16_BE:
+      bs->func = bit_slicer_RGB16_BE;
+      bs->green_mask = 0x07E0;
+      bs->thresh = 105 << (5 - 2 + 12);
+      bs->thresh_frac = 12;
+      bs->bytes_per_sample = 2;
+      break;
+
+    case VBI_PIXFMT_RGBA15_LE:
+    case VBI_PIXFMT_BGRA15_LE:
+      bs->func = bit_slicer_RGB16_LE;
+      bs->green_mask = 0x03E0;
+      bs->thresh = 105 << (5 - 3 + 11);
+      bs->thresh_frac = 11;
+      bs->bytes_per_sample = 2;
+      break;
+
+    case VBI_PIXFMT_RGBA15_BE:
+    case VBI_PIXFMT_BGRA15_BE:
+      bs->func = bit_slicer_RGB16_BE;
+      bs->green_mask = 0x03E0;
+      bs->thresh = 105 << (5 - 3 + 11);
+      bs->thresh_frac = 11;
+      bs->bytes_per_sample = 2;
+      break;
+
+    case VBI_PIXFMT_ARGB15_LE:
+    case VBI_PIXFMT_ABGR15_LE:
+      bs->func = bit_slicer_RGB16_LE;
+      bs->green_mask = 0x07C0;
+      bs->thresh = 105 << (6 - 3 + 12);
+      bs->thresh_frac = 12;
+      bs->bytes_per_sample = 2;
+      break;
+
+    case VBI_PIXFMT_ARGB15_BE:
+    case VBI_PIXFMT_ABGR15_BE:
+      bs->func = bit_slicer_RGB16_BE;
+      bs->green_mask = 0x07C0;
+      bs->thresh = 105 << (6 - 3 + 12);
+      bs->thresh_frac = 12;
+      bs->bytes_per_sample = 2;
+      break;
+
+
+    default:
+      warning (&bs->log,
+          "Unknown sample_format 0x%x.", (unsigned int) sample_format);
+      return FALSE;
+  }
+
+  bs->skip = sample_offset * bs->bytes_per_sample + skip;
+
+  bs->cri_mask = cri_mask & c_mask;
+  bs->cri = cri & bs->cri_mask;
+
+  /* We stop searching for CRI when CRI, FRC and payload
+     cannot possibly fit anymore. Additionally this eliminates
+     a data end check in the payload loop. */
+  cri_samples = (sampling_rate * (int64_t) cri_bits) / cri_rate;
+
+  data_bits = payload_bits + frc_bits;
+  data_samples = (sampling_rate * (int64_t) data_bits) / payload_rate;
+
+  bs->total_bits = cri_bits + data_bits;
+
+  if ((sample_offset > samples_per_line)
+      || ((cri_samples + data_samples)
+          > (samples_per_line - sample_offset))) {
+    warning (&bs->log,
+        "%u samples_per_line too small for "
+        "sample_offset %u + %u cri_bits (%u samples) "
+        "+ %u frc_bits and %u payload_bits "
+        "(%u samples).",
+        samples_per_line, sample_offset,
+        cri_bits, cri_samples, frc_bits, payload_bits, data_samples);
+    goto failure;
+  }
+
+  cri_end = MIN (cri_end, samples_per_line - data_samples);
+
+  bs->cri_samples = cri_end - sample_offset;
+  bs->cri_rate = cri_rate;
+
+  bs->oversampling_rate = sampling_rate * oversampling;
+
+  bs->frc = frc & f_mask;
+  bs->frc_bits = frc_bits;
+
+  /* Payload bit distance in 1/256 raw samples. */
+  bs->step = (sampling_rate * (int64_t) 256) / payload_rate;
+
+  if (payload_bits & 7) {
+    /* Use bit routines. */
+    bs->payload = payload_bits;
+    bs->endian = 3;
+  } else {
+    /* Use faster octet routines. */
+    bs->payload = payload_bits >> 3;
+    bs->endian = 1;
+  }
+
+  switch (modulation) {
+    case VBI3_MODULATION_NRZ_MSB:
+      --bs->endian;
+
+      /* fall through */
+
+    case VBI3_MODULATION_NRZ_LSB:
+      bs->phase_shift = (int)
+          (sampling_rate * 256.0 / cri_rate * .5 + bs->step * .5 + 128);
+      break;
+
+    case VBI3_MODULATION_BIPHASE_MSB:
+      --bs->endian;
+
+      /* fall through */
+
+    case VBI3_MODULATION_BIPHASE_LSB:
+      /* Phase shift between the NRZ modulated CRI and the
+         biphase modulated rest. */
+      bs->phase_shift = (int)
+          (sampling_rate * 256.0 / cri_rate * .5 + bs->step * .25 + 128);
+      break;
+  }
+
+  return TRUE;
+
+failure:
+  bs->func = null_function;
+
+  return FALSE;
+}
+
+void
+vbi3_bit_slicer_set_log_fn (vbi3_bit_slicer * bs,
+    vbi_log_mask mask, vbi_log_fn * log_fn, void *user_data)
+{
+  assert (NULL != bs);
+
+  if (NULL == log_fn)
+    mask = 0;
+
+  bs->log.mask = mask;
+  bs->log.fn = log_fn;
+  bs->log.user_data = user_data;
+}
+
+/**
+ * @internal
+ */
+void
+_vbi3_bit_slicer_destroy (vbi3_bit_slicer * bs)
+{
+  assert (NULL != bs);
+
+  /* Make unusable. */
+  CLEAR (*bs);
+}
+
+/**
+ * @internal
+ */
+vbi_bool
+_vbi3_bit_slicer_init (vbi3_bit_slicer * bs)
+{
+  assert (NULL != bs);
+
+  CLEAR (*bs);
+
+  bs->func = null_function;
+
+  return TRUE;
+}
+
+/**
+ * @param bs Pointer to a vbi3_bit_slicer object allocated with
+ *   vbi3_bit_slicer_new(), can be NULL.
+ *
+ * Deletes a vbi3_bit_slicer object.
+ */
+void
+vbi3_bit_slicer_delete (vbi3_bit_slicer * bs)
+{
+  if (NULL == bs)
+    return;
+
+  _vbi3_bit_slicer_destroy (bs);
+
+  vbi_free (bs);
+}
+
+/**
+ * Allocates a new vbi3_bit_slicer object.
+ *
+ * @returns
+ * @c NULL when out of memory.
+ */
+vbi3_bit_slicer *
+vbi3_bit_slicer_new (void)
+{
+  vbi3_bit_slicer *bs;
+
+  bs = vbi_malloc (sizeof (*bs));
+  if (NULL == bs) {
+    return NULL;
+  }
+
+  _vbi3_bit_slicer_init (bs);
+
+  return bs;
+}
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/ext/closedcaption/bit_slicer.h b/ext/closedcaption/bit_slicer.h
new file mode 100644 (file)
index 0000000..a537f4f
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ *  libzvbi -- Bit slicer
+ *
+ *  Copyright (C) 2000-2007 Michael H. Schimek
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the 
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA  02110-1301  USA.
+ */
+
+/* $Id: bit_slicer.h,v 1.11 2008-02-24 14:18:13 mschimek Exp $ */
+
+#ifndef __ZVBI_BIT_SLICER_H__
+#define __ZVBI_BIT_SLICER_H__
+
+#include "sampling_par.h"
+
+VBI_BEGIN_DECLS
+
+/**
+ * @addtogroup BitSlicer
+ * @{
+ */
+
+/**
+ * @brief Modulation used for VBI data transmission.
+ */
+typedef enum {
+       /**
+        * The data is 'non-return to zero' coded, logical '1' bits
+        * are described by high sample values, logical '0' bits by
+        * low values. The data is last significant bit first transmitted.
+        */
+       VBI3_MODULATION_NRZ_LSB,
+
+       /**
+        * 'Non-return to zero' coded, most significant bit first
+        * transmitted.
+        */
+       VBI3_MODULATION_NRZ_MSB,
+
+       /**
+        * The data is 'bi-phase' coded. Each data bit is described
+        * by two complementary signalling elements, a logical '1'
+        * by a sequence of '10' elements, a logical '0' by a '01'
+        * sequence. The data is last significant bit first transmitted.
+        */
+       VBI3_MODULATION_BIPHASE_LSB,
+
+       /** 'Bi-phase' coded, most significant bit first transmitted. */
+       VBI3_MODULATION_BIPHASE_MSB
+} vbi3_modulation;
+
+/**
+ * @brief Bit slicer context.
+ *
+ * The contents of this structure are private.
+ * Call vbi3_bit_slicer_new() to allocate a bit slicer context.
+ */
+typedef struct _vbi3_bit_slicer vbi3_bit_slicer;
+
+typedef enum {
+       VBI3_CRI_BIT = 1,
+       VBI3_FRC_BIT,
+       VBI3_PAYLOAD_BIT
+} vbi3_bit_slicer_bit;
+
+/**
+ * @brief Bit slicer sampling point.
+ *
+ * This structure contains information about
+ * a bit sampled by the bit slicer.
+ */
+typedef struct {
+       /** Whether this struct refers to a CRI, FRC or payload bit. */
+       vbi3_bit_slicer_bit     kind;
+
+       /** Number of the sample times 256. */
+       unsigned int            index;
+
+       /** Signal amplitude at this sample, in range 0 to 65535. */
+       unsigned int            level;
+
+       /** 0/1 threshold at this sample, in range 0 to 65535. */
+       unsigned int            thresh;
+} vbi3_bit_slicer_point;
+
+extern vbi_bool
+vbi3_bit_slicer_slice_with_points
+                               (vbi3_bit_slicer *      bs,
+                                uint8_t *              buffer,
+                                unsigned int           buffer_size,
+                                vbi3_bit_slicer_point *points,
+                                unsigned int *         n_points,
+                                unsigned int           max_points,
+                                const uint8_t *        raw)
+  _vbi_nonnull ((1, 2, 4, 5, 7));
+extern vbi_bool
+vbi3_bit_slicer_slice          (vbi3_bit_slicer *      bs,
+                                uint8_t *              buffer,
+                                unsigned int           buffer_size,
+                                const uint8_t *        raw)
+  _vbi_nonnull ((1, 2, 4));
+extern vbi_bool
+vbi3_bit_slicer_set_params     (vbi3_bit_slicer *      bs,
+                                vbi_pixfmt             sample_format,
+                                unsigned int           sampling_rate,
+                                unsigned int           sample_offset,
+                                unsigned int           samples_per_line,
+                                unsigned int           cri,
+                                unsigned int           cri_mask,
+                                unsigned int           cri_bits,
+                                unsigned int           cri_rate,
+                                unsigned int           cri_end,
+                                unsigned int           frc,
+                                unsigned int           frc_bits,
+                                unsigned int           payload_bits,
+                                unsigned int           payload_rate,
+                                vbi3_modulation        modulation)
+  _vbi_nonnull ((1));
+extern void
+vbi3_bit_slicer_set_log_fn     (vbi3_bit_slicer *      bs,
+                                vbi_log_mask           mask,
+                                vbi_log_fn *           log_fn,
+                                void *                 user_data)
+  _vbi_nonnull ((1));
+extern void
+vbi3_bit_slicer_delete         (vbi3_bit_slicer *      bs);
+extern vbi3_bit_slicer *
+vbi3_bit_slicer_new            (void)
+  _vbi_alloc;
+
+/* Private */
+
+typedef vbi_bool
+_vbi3_bit_slicer_fn            (vbi3_bit_slicer *      bs,
+                                uint8_t *              buffer,
+                                vbi3_bit_slicer_point *points,
+                                unsigned int *         n_points,
+                                const uint8_t *        raw);
+
+/** @internal */
+struct _vbi3_bit_slicer {
+       _vbi3_bit_slicer_fn *   func;
+
+       vbi_pixfmt              sample_format;
+       unsigned int            cri;
+       unsigned int            cri_mask;
+       unsigned int            thresh;
+       unsigned int            thresh_frac;
+       unsigned int            cri_samples;
+       unsigned int            cri_rate;
+       unsigned int            oversampling_rate;
+       unsigned int            phase_shift;
+       unsigned int            step;
+       unsigned int            frc;
+       unsigned int            frc_bits;
+       unsigned int            total_bits;
+       unsigned int            payload;
+       unsigned int            endian;
+       unsigned int            bytes_per_sample;
+       unsigned int            skip;
+       unsigned int            green_mask;
+
+       _vbi_log_hook           log;
+};
+
+extern void
+_vbi3_bit_slicer_destroy       (vbi3_bit_slicer *      bs)
+  _vbi_nonnull ((1));
+extern vbi_bool
+_vbi3_bit_slicer_init          (vbi3_bit_slicer *      bs)
+  _vbi_nonnull ((1));
+
+/** @} */
+
+VBI_END_DECLS
+
+#endif /* __ZVBI_BIT_SLICER_H__ */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/ext/closedcaption/decoder.c b/ext/closedcaption/decoder.c
new file mode 100644 (file)
index 0000000..98933b1
--- /dev/null
@@ -0,0 +1,832 @@
+/*
+ *  libzvbi -- Old raw VBI decoder
+ *
+ *  Copyright (C) 2000, 2001, 2002 Michael H. Schimek
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the 
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA  02110-1301  USA.
+ */
+
+/* $Id: decoder.c,v 1.25 2008-02-19 00:35:15 mschimek Exp $ */
+
+/* Note this code is only retained for compatibility with older versions
+   of libzvbi. vbi_raw_decoder is now just a wrapper for the new raw
+   decoder (raw_decoder.c) and bit slicer (bit_slicer.c). We'll drop
+   the old API in libzvbi 0.3. Other modules (e.g. io-v4l2k.c) should
+   already use the new raw VBI decoder directly. */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <pthread.h>
+
+#include "misc.h"
+#include "decoder.h"
+#include "raw_decoder.h"
+
+/**
+ * @addtogroup Rawdec Raw VBI decoder
+ * @ingroup Raw
+ * @brief Converting raw VBI samples to bits and bytes.
+ *
+ * The libzvbi already offers hardware interfaces to obtain sliced
+ * VBI data for further processing. However if you want to write your own
+ * interface or decode data services not covered by libzvbi you can use
+ * these lower level functions.
+ */
+
+/*
+ *  Bit Slicer
+ */
+
+#define OVERSAMPLING 4          /* 1, 2, 4, 8 */
+#define THRESH_FRAC 9
+
+/*
+ * Note this is just a template. The code is inlined,
+ * with bpp and endian being const.
+ *
+ * This function translates from the image format to
+ * plain bytes, with linear interpolation of samples.
+ * Could be further improved with a lowpass filter.
+ */
+static inline unsigned int
+sample (uint8_t * raw, int offs, int bpp, int endian)
+{
+  unsigned char frac = offs;
+  int raw0, raw1;
+
+  switch (bpp) {
+    case 14:                   /* 1:5:5:5 LE/BE */
+      raw += (offs >> 8) * 2;
+      raw0 = (raw[0 + endian] + raw[1 - endian] * 256) & 0x07C0;
+      raw1 = (raw[2 + endian] + raw[3 - endian] * 256) & 0x07C0;
+      return (raw1 - raw0) * frac + (raw0 << 8);
+
+    case 15:                   /* 5:5:5:1 LE/BE */
+      raw += (offs >> 8) * 2;
+      raw0 = (raw[0 + endian] + raw[1 - endian] * 256) & 0x03E0;
+      raw1 = (raw[2 + endian] + raw[3 - endian] * 256) & 0x03E0;
+      return (raw1 - raw0) * frac + (raw0 << 8);
+
+    case 16:                   /* 5:6:5 LE/BE */
+      raw += (offs >> 8) * 2;
+      raw0 = (raw[0 + endian] + raw[1 - endian] * 256) & 0x07E0;
+      raw1 = (raw[2 + endian] + raw[3 - endian] * 256) & 0x07E0;
+      return (raw1 - raw0) * frac + (raw0 << 8);
+
+    default:                   /* 8 (intermediate bytes skipped by caller) */
+      raw += (offs >> 8) * bpp;
+      return (raw[bpp] - raw[0]) * frac + (raw[0] << 8);
+  }
+}
+
+/*
+ * Note this is just a template. The code is inlined,
+ * with bpp being const.
+ */
+static inline vbi_bool
+bit_slicer_tmpl (vbi_bit_slicer * d, uint8_t * raw,
+    uint8_t * buf, int bpp, int endian)
+{
+  unsigned int i, j, k;
+  unsigned int cl = 0, thresh0 = d->thresh, tr;
+  unsigned int c = 0, t;
+  unsigned char b, b1 = 0;
+  int raw0, raw1, mask;
+
+  raw += d->skip;
+
+  if (bpp == 14)
+    mask = 0x07C0;
+  else if (bpp == 15)
+    mask = 0x03E0;
+  else if (bpp == 16)
+    mask = 0x07E0;
+
+  for (i = d->cri_bytes; i > 0; raw += (bpp >= 14 && bpp <= 16) ? 2 : bpp, i--) {
+    if (bpp >= 14 && bpp <= 16) {
+      raw0 = (raw[0 + endian] + raw[1 - endian] * 256) & mask;
+      raw1 = (raw[2 + endian] + raw[3 - endian] * 256) & mask;
+      tr = d->thresh >> THRESH_FRAC;
+      d->thresh += ((raw0 - tr) * (int) ABS (raw1 - raw0)) >>
+          ((bpp == 15) ? 2 : 3);
+      t = raw0 * OVERSAMPLING;
+    } else {
+      tr = d->thresh >> THRESH_FRAC;
+      d->thresh += ((int) raw[0] - tr) * (int) ABS (raw[bpp] - raw[0]);
+      t = raw[0] * OVERSAMPLING;
+    }
+
+    for (j = OVERSAMPLING; j > 0; j--) {
+      b = ((t + (OVERSAMPLING / 2)) / OVERSAMPLING >= tr);
+
+      if (b ^ b1) {
+        cl = d->oversampling_rate >> 1;
+      } else {
+        cl += d->cri_rate;
+
+        if (cl >= (unsigned int) d->oversampling_rate) {
+          cl -= d->oversampling_rate;
+
+          c = c * 2 + b;
+
+          if ((c & d->cri_mask) == d->cri) {
+            i = d->phase_shift;
+            tr *= 256;
+            c = 0;
+
+            for (j = d->frc_bits; j > 0; j--) {
+              c = c * 2 + (sample (raw, i, bpp, endian) >= tr);
+              i += d->step;
+            }
+
+            if (c ^= d->frc)
+              return FALSE;
+
+            /* CRI/FRC found, now get the
+               payload and exit */
+
+            switch (d->endian) {
+              case 3:
+                for (j = 0; j < (unsigned int) d->payload; j++) {
+                  c >>= 1;
+                  c += (sample (raw, i, bpp, endian) >= tr) << 7;
+                  i += d->step;
+
+                  if ((j & 7) == 7)
+                    *buf++ = c;
+                }
+
+                *buf = c >> ((8 - d->payload) & 7);
+                break;
+
+              case 2:
+                for (j = 0; j < (unsigned int) d->payload; j++) {
+                  c = c * 2 + (sample (raw, i, bpp, endian) >= tr);
+                  i += d->step;
+
+                  if ((j & 7) == 7)
+                    *buf++ = c;
+                }
+
+                *buf = c & ((1 << (d->payload & 7)) - 1);
+                break;
+
+              case 1:
+                for (j = d->payload; j > 0; j--) {
+                  for (k = 0; k < 8; k++) {
+                    c >>= 1;
+                    c += (sample (raw, i, bpp, endian) >= tr) << 7;
+                    i += d->step;
+                  }
+
+                  *buf++ = c;
+                }
+
+                break;
+
+              case 0:
+                for (j = d->payload; j > 0; j--) {
+                  for (k = 0; k < 8; k++) {
+                    c = c * 2 + (sample (raw, i, bpp, endian) >= tr);
+                    i += d->step;
+                  }
+
+                  *buf++ = c;
+                }
+
+                break;
+            }
+
+            return TRUE;
+          }
+        }
+      }
+
+      b1 = b;
+
+      if (OVERSAMPLING > 1) {
+        if (bpp >= 14 && bpp <= 16) {
+          t += raw1;
+          t -= raw0;
+        } else {
+          t += raw[bpp];
+          t -= raw[0];
+        }
+      }
+    }
+  }
+
+  d->thresh = thresh0;
+
+  return FALSE;
+}
+
+static vbi_bool
+bit_slicer_1 (vbi_bit_slicer * d, uint8_t * raw, uint8_t * buf)
+{
+  return bit_slicer_tmpl (d, raw, buf, 1, 0);
+}
+
+static vbi_bool
+bit_slicer_2 (vbi_bit_slicer * d, uint8_t * raw, uint8_t * buf)
+{
+  return bit_slicer_tmpl (d, raw, buf, 2, 0);
+}
+
+static vbi_bool
+bit_slicer_3 (vbi_bit_slicer * d, uint8_t * raw, uint8_t * buf)
+{
+  return bit_slicer_tmpl (d, raw, buf, 3, 0);
+}
+
+static vbi_bool
+bit_slicer_4 (vbi_bit_slicer * d, uint8_t * raw, uint8_t * buf)
+{
+  return bit_slicer_tmpl (d, raw, buf, 4, 0);
+}
+
+static vbi_bool
+bit_slicer_1555_le (vbi_bit_slicer * d, uint8_t * raw, uint8_t * buf)
+{
+  return bit_slicer_tmpl (d, raw, buf, 14, 0);
+}
+
+static vbi_bool
+bit_slicer_5551_le (vbi_bit_slicer * d, uint8_t * raw, uint8_t * buf)
+{
+  return bit_slicer_tmpl (d, raw, buf, 15, 0);
+}
+
+static vbi_bool
+bit_slicer_565_le (vbi_bit_slicer * d, uint8_t * raw, uint8_t * buf)
+{
+  return bit_slicer_tmpl (d, raw, buf, 16, 0);
+}
+
+static vbi_bool
+bit_slicer_1555_be (vbi_bit_slicer * d, uint8_t * raw, uint8_t * buf)
+{
+  return bit_slicer_tmpl (d, raw, buf, 14, 1);
+}
+
+static vbi_bool
+bit_slicer_5551_be (vbi_bit_slicer * d, uint8_t * raw, uint8_t * buf)
+{
+  return bit_slicer_tmpl (d, raw, buf, 15, 1);
+}
+
+static vbi_bool
+bit_slicer_565_be (vbi_bit_slicer * d, uint8_t * raw, uint8_t * buf)
+{
+  return bit_slicer_tmpl (d, raw, buf, 16, 1);
+}
+
+/**
+ * @param slicer Pointer to vbi_bit_slicer object to be initialized. 
+ * @param raw_samples Number of samples or pixels in one raw vbi line
+ *   later passed to vbi_bit_slice(). This limits the number of
+ *   bytes read from the sample buffer.
+ * @param sampling_rate Raw vbi sampling rate in Hz, that is the number of
+ *   samples or pixels sampled per second by the hardware. 
+ * @param cri_rate The Clock Run In is a NRZ modulated
+ *   sequence of '0' and '1' bits prepending most data transmissions to
+ *   synchronize data acquisition circuits. This parameter gives the CRI bit
+ *   rate in Hz, that is the number of CRI bits transmitted per second.
+ * @param bit_rate The transmission bit rate of all data bits following the CRI
+ *   in Hz.
+ * @param cri_frc The FRaming Code usually following the CRI is a bit sequence
+ *   identifying the data service, and per libzvbi definition modulated
+ *   and transmitted at the same bit rate as the payload (however nothing
+ *   stops you from counting all nominal CRI and FRC bits as CRI).
+ *   The bit slicer compares the bits in this word, lsb last transmitted,
+ *   against the transmitted CRI and FRC. Decoding of payload starts
+ *   with the next bit after a match.
+ * @param cri_mask Of the CRI bits in @c cri_frc, only these bits are
+ *   actually significant for a match. For instance it is wise
+ *   not to rely on the very first CRI bits transmitted. Note this
+ *   mask is not shifted left by @a frc_bits.
+ * @param cri_bits 
+ * @param frc_bits Number of CRI and FRC bits in @a cri_frc, respectively.
+ *   Their sum is limited to 32.
+ * @param payload Number of payload <em>bits</em>. Only this data
+ *   will be stored in the vbi_bit_slice() output. If this number
+ *   is no multiple of eight, the most significant bits of the
+ *   last byte are undefined.
+ * @param modulation Modulation of the vbi data, see vbi_modulation.
+ * @param fmt Format of the raw data, see vbi_pixfmt.
+ * 
+ * Initializes vbi_bit_slicer object. Usually you will not use this
+ * function but vbi_raw_decode(), the vbi image decoder which handles
+ * all these details.
+ */
+void
+vbi_bit_slicer_init (vbi_bit_slicer * slicer,
+    int raw_samples, int sampling_rate,
+    int cri_rate, int bit_rate,
+    unsigned int cri_frc, unsigned int cri_mask,
+    int cri_bits, int frc_bits, int payload,
+    vbi_modulation modulation, vbi_pixfmt fmt)
+{
+  unsigned int c_mask = (unsigned int) (-(cri_bits > 0)) >> (32 - cri_bits);
+  unsigned int f_mask = (unsigned int) (-(frc_bits > 0)) >> (32 - frc_bits);
+  int gsh = 0;
+
+  slicer->func = bit_slicer_1;
+
+  switch (fmt) {
+    case VBI_PIXFMT_RGB24:
+    case VBI_PIXFMT_BGR24:
+      slicer->func = bit_slicer_3;
+      slicer->skip = 1;
+      break;
+
+    case VBI_PIXFMT_RGBA32_LE:
+    case VBI_PIXFMT_BGRA32_LE:
+      slicer->func = bit_slicer_4;
+      slicer->skip = 1;
+      break;
+
+    case VBI_PIXFMT_RGBA32_BE:
+    case VBI_PIXFMT_BGRA32_BE:
+      slicer->func = bit_slicer_4;
+      slicer->skip = 2;
+      break;
+
+    case VBI_PIXFMT_RGB16_LE:
+    case VBI_PIXFMT_BGR16_LE:
+      slicer->func = bit_slicer_565_le;
+      gsh = 3;                  /* (green << 3) & 0x07E0 */
+      slicer->skip = 0;
+      break;
+
+    case VBI_PIXFMT_RGBA15_LE:
+    case VBI_PIXFMT_BGRA15_LE:
+      slicer->func = bit_slicer_5551_le;
+      gsh = 2;                  /* (green << 2) & 0x03E0 */
+      slicer->skip = 0;
+      break;
+
+    case VBI_PIXFMT_ARGB15_LE:
+    case VBI_PIXFMT_ABGR15_LE:
+      slicer->func = bit_slicer_1555_le;
+      gsh = 3;                  /* (green << 2) & 0x07C0 */
+      slicer->skip = 0;
+      break;
+
+    case VBI_PIXFMT_RGB16_BE:
+    case VBI_PIXFMT_BGR16_BE:
+      slicer->func = bit_slicer_565_be;
+      gsh = 3;                  /* (green << 3) & 0x07E0 */
+      slicer->skip = 0;
+      break;
+
+    case VBI_PIXFMT_RGBA15_BE:
+    case VBI_PIXFMT_BGRA15_BE:
+      slicer->func = bit_slicer_5551_be;
+      gsh = 2;                  /* (green << 2) & 0x03E0 */
+      slicer->skip = 0;
+      break;
+
+    case VBI_PIXFMT_ARGB15_BE:
+    case VBI_PIXFMT_ABGR15_BE:
+      slicer->func = bit_slicer_1555_be;
+      gsh = 3;                  /* (green << 2) & 0x07C0 */
+      slicer->skip = 0;
+      break;
+
+    case VBI_PIXFMT_YUV420:
+      slicer->func = bit_slicer_1;
+      slicer->skip = 0;
+      break;
+
+    case VBI_PIXFMT_YUYV:
+    case VBI_PIXFMT_YVYU:
+      slicer->func = bit_slicer_2;
+      slicer->skip = 0;
+      break;
+
+    case VBI_PIXFMT_UYVY:
+    case VBI_PIXFMT_VYUY:
+      slicer->func = bit_slicer_2;
+      slicer->skip = 1;
+      break;
+
+    default:
+      fprintf (stderr, "vbi_bit_slicer_init: unknown pixfmt %d\n", fmt);
+      exit (EXIT_FAILURE);
+  }
+
+  slicer->cri_mask = cri_mask & c_mask;
+  slicer->cri = (cri_frc >> frc_bits) & slicer->cri_mask;
+  /* We stop searching for CRI/FRC when the payload
+     cannot possibly fit anymore. */
+  slicer->cri_bytes = raw_samples
+      - ((long long) sampling_rate * (payload + frc_bits)) / bit_rate;
+  slicer->cri_rate = cri_rate;
+  /* Raw vbi data is oversampled to account for low sampling rates. */
+  slicer->oversampling_rate = sampling_rate * OVERSAMPLING;
+  /* 0/1 threshold */
+  slicer->thresh = 105 << (THRESH_FRAC + gsh);
+  slicer->frc = cri_frc & f_mask;
+  slicer->frc_bits = frc_bits;
+  /* Payload bit distance in 1/256 raw samples. */
+  slicer->step = (int) (sampling_rate * 256.0 / bit_rate);
+
+  if (payload & 7) {
+    slicer->payload = payload;
+    slicer->endian = 3;
+  } else {
+    slicer->payload = payload >> 3;
+    slicer->endian = 1;
+  }
+
+  switch (modulation) {
+    case VBI_MODULATION_NRZ_MSB:
+      slicer->endian--;
+    case VBI_MODULATION_NRZ_LSB:
+      slicer->phase_shift = (int)
+          (sampling_rate * 256.0 / cri_rate * .5
+          + sampling_rate * 256.0 / bit_rate * .5 + 128);
+      break;
+
+    case VBI_MODULATION_BIPHASE_MSB:
+      slicer->endian--;
+    case VBI_MODULATION_BIPHASE_LSB:
+      /* Phase shift between the NRZ modulated CRI and the rest */
+      slicer->phase_shift = (int)
+          (sampling_rate * 256.0 / cri_rate * .5
+          + sampling_rate * 256.0 / bit_rate * .25 + 128);
+      break;
+  }
+}
+
+/**
+ * @example examples/wss.c
+ * WSS capture example.
+ */
+
+/**
+ * @param rd Initialized vbi_raw_decoder structure.
+ * @param raw A raw vbi image as defined in the vbi_raw_decoder structure
+ *   (rd->sampling_format, rd->bytes_per_line, rd->count[0] + rd->count[1]
+ *    scan lines).
+ * @param out Buffer to store the decoded vbi_sliced data. Since every
+ *   vbi scan line may contain data, this must be an array of vbi_sliced
+ *   with the same number of entries as scan lines in the raw image
+ *   (rd->count[0] + rd->count[1]).
+ * 
+ * Decode a raw vbi image, consisting of several scan lines of raw vbi data,
+ * into sliced vbi data. The output is sorted by line number.
+ * 
+ * Note this function attempts to learn which lines carry which data
+ * service, or none, to speed up decoding. You should avoid using the same
+ * vbi_raw_decoder structure for different sources.
+ *
+ * @return
+ * The number of lines decoded, i. e. the number of vbi_sliced records
+ * written.
+ */
+int
+vbi_raw_decode (vbi_raw_decoder * rd, uint8_t * raw, vbi_sliced * out)
+{
+  vbi3_raw_decoder *rd3;
+  unsigned int n_lines;
+
+  assert (NULL != rd);
+  assert (NULL != raw);
+  assert (NULL != out);
+
+  rd3 = (vbi3_raw_decoder *) rd->pattern;
+  n_lines = rd->count[0] + rd->count[1];
+
+  pthread_mutex_lock (&rd->mutex);
+
+  {
+    n_lines = vbi3_raw_decoder_decode (rd3, out, n_lines, raw);
+  }
+
+  pthread_mutex_unlock (&rd->mutex);
+
+  return n_lines;
+}
+
+/**
+ * @param rd Initialized vbi_raw_decoder structure.
+ * @param start Array of start line indices for both fields
+ * @param count Array of line counts for both fields
+ * 
+ * Grows or shrinks the internal state arrays for VBI geometry changes
+ */
+void
+vbi_raw_decoder_resize (vbi_raw_decoder * rd, int *start, unsigned int *count)
+{
+#if 0                           /* Set but unused */
+  vbi_service_set service_set;
+#endif
+  vbi3_raw_decoder *rd3;
+
+  assert (NULL != rd);
+  assert (NULL != start);
+  assert (NULL != count);
+
+  rd3 = (vbi3_raw_decoder *) rd->pattern;
+
+  pthread_mutex_lock (&rd->mutex);
+
+  {
+    if ((rd->start[0] == start[0])
+        && (rd->start[1] == start[1])
+        && (rd->count[0] == (int) count[0])
+        && (rd->count[1] == (int) count[1])) {
+      pthread_mutex_unlock (&rd->mutex);
+      return;
+    }
+
+    rd->start[0] = start[0];
+    rd->start[1] = start[1];
+    rd->count[0] = count[0];
+    rd->count[1] = count[1];
+
+#if 0                           /* Set but unused */
+    service_set = vbi3_raw_decoder_set_sampling_par
+        (rd3, (vbi_sampling_par *) rd, /* strict */ 0);
+#else
+    vbi3_raw_decoder_set_sampling_par
+        (rd3, (vbi_sampling_par *) rd, /* strict */ 0);
+#endif
+  }
+
+  pthread_mutex_unlock (&rd->mutex);
+}
+
+/**
+ * @param rd Initialized vbi_raw_decoder structure.
+ * @param services Set of @ref VBI_SLICED_ symbols.
+ * 
+ * Removes one or more data services to be decoded from the
+ * vbi_raw_decoder structure. This function can be called at any
+ * time and does not touch sampling parameters. 
+ * 
+ * @return 
+ * Set of @ref VBI_SLICED_ symbols describing the remaining data
+ * services that will be decoded.
+ */
+unsigned int
+vbi_raw_decoder_remove_services (vbi_raw_decoder * rd, unsigned int services)
+{
+  vbi_service_set service_set;
+  vbi3_raw_decoder *rd3;
+
+  assert (NULL != rd);
+
+  rd3 = (vbi3_raw_decoder *) rd->pattern;
+  service_set = services;
+
+  pthread_mutex_lock (&rd->mutex);
+
+  {
+    service_set = vbi3_raw_decoder_remove_services (rd3, service_set);
+  }
+
+  pthread_mutex_unlock (&rd->mutex);
+
+  return service_set;
+}
+
+/**
+ * @param rd Initialized vbi_raw_decoder structure.
+ * @param services Set of @ref VBI_SLICED_ symbols.
+ * @param strict See description of vbi_raw_decoder_add_services()
+ *
+ * Check which of the given services can be decoded with current capture
+ * parameters at a given strictness level.
+ *
+ * @return
+ * Subset of services actually decodable.
+ */
+unsigned int
+vbi_raw_decoder_check_services (vbi_raw_decoder * rd,
+    unsigned int services, int strict)
+{
+  vbi_service_set service_set;
+
+  assert (NULL != rd);
+
+  service_set = services;
+
+  pthread_mutex_lock (&rd->mutex);
+
+  {
+    service_set = vbi_sampling_par_check_services
+        ((vbi_sampling_par *) rd, service_set, strict);
+  }
+
+  pthread_mutex_unlock (&rd->mutex);
+
+  return (unsigned int) service_set;
+}
+
+/**
+ * @param rd Initialized vbi_raw_decoder structure.
+ * @param services Set of @ref VBI_SLICED_ symbols.
+ * @param strict A value of 0, 1 or 2 requests loose, reliable or strict
+ *  matching of sampling parameters. For example if the data service
+ *  requires knowledge of line numbers while they are not known, @c 0
+ *  will accept the service (which may work if the scan lines are
+ *  populated in a non-confusing way) but @c 1 or @c 2 will not. If the
+ *  data service <i>may</i> use more lines than are sampled, @c 1 will
+ *  accept but @c 2 will not. If unsure, set to @c 1.
+ * 
+ * After you initialized the sampling parameters in @a rd (according to
+ * the abilities of your raw vbi source), this function adds one or more
+ * data services to be decoded. The libzvbi raw vbi decoder can decode up
+ * to eight data services in parallel. You can call this function while
+ * already decoding, it does not change sampling parameters and you must
+ * not change them either after calling this.
+ * 
+ * @return
+ * Set of @ref VBI_SLICED_ symbols describing the data services that actually
+ * will be decoded. This excludes those services not decodable given
+ * the sampling parameters in @a rd.
+ */
+unsigned int
+vbi_raw_decoder_add_services (vbi_raw_decoder * rd,
+    unsigned int services, int strict)
+{
+  vbi_service_set service_set;
+  vbi3_raw_decoder *rd3;
+
+  assert (NULL != rd);
+
+  rd3 = (vbi3_raw_decoder *) rd->pattern;
+  service_set = services;
+
+  pthread_mutex_lock (&rd->mutex);
+
+  {
+    vbi3_raw_decoder_set_sampling_par (rd3, (vbi_sampling_par *) rd, strict);
+
+    service_set = vbi3_raw_decoder_add_services (rd3, service_set, strict);
+  }
+
+  pthread_mutex_unlock (&rd->mutex);
+
+  return service_set;
+}
+
+/**
+ * @param rd Initialized vbi_raw_decoder structure.
+ * @param services Set of VBI_SLICED_ symbols. Here (and only here) you
+ *   can add @c VBI_SLICED_VBI_625 or @c VBI_SLICED_VBI_525 to include all
+ *   vbi scan lines in the calculated sampling parameters.
+ * @param scanning When 525 accept only NTSC services, when 625
+ *   only PAL/SECAM services. When scanning is 0, determine the scanning
+ *   from the requested services, an ambiguous set will pick
+ *   a 525 or 625 line system at random.
+ * @param max_rate If given, the highest data bit rate in Hz of all
+ *   services requested is stored here. (The sampling rate
+ *   should be at least twice as high; rd->sampling_rate will
+ *   be set to a more reasonable value of 27 MHz derived
+ *   from ITU-R Rec. 601.)
+ * 
+ * Calculate the sampling parameters in @a rd required to receive and
+ * decode the requested data @a services. rd->sampling_format will be
+ * @c VBI_PIXFMT_YUV420, rd->bytes_per_line set accordingly to a
+ * reasonable minimum. This function can be used to initialize hardware
+ * prior to calling vbi_raw_decoder_add_service().
+ * 
+ * @return
+ * Set of @ref VBI_SLICED_ symbols describing the data services covered
+ * by the calculated sampling parameters. This excludes services the libzvbi
+ * raw decoder cannot decode.
+ */
+unsigned int
+vbi_raw_decoder_parameters (vbi_raw_decoder * rd,
+    unsigned int services, int scanning, int *max_rate)
+{
+  vbi_videostd_set videostd_set;
+  vbi_service_set service_set;
+
+  switch (scanning) {
+    case 525:
+      videostd_set = VBI_VIDEOSTD_SET_525_60;
+      break;
+
+    case 625:
+      videostd_set = VBI_VIDEOSTD_SET_625_50;
+      break;
+
+    default:
+      videostd_set = 0;
+      break;
+  }
+
+  service_set = services;
+
+  pthread_mutex_lock (&rd->mutex);
+
+  {
+    service_set = vbi_sampling_par_from_services
+        ((vbi_sampling_par *) rd,
+        (unsigned int *) max_rate, videostd_set, service_set);
+  }
+
+  pthread_mutex_unlock (&rd->mutex);
+
+  return (unsigned int) service_set;
+}
+
+/**
+ * @param rd Initialized vbi_raw_decoder structure.
+ * 
+ * Reset a vbi_raw_decoder structure. This removes
+ * all previously added services to be decoded (if any)
+ * but does not touch the sampling parameters. You are
+ * free to change the sampling parameters after calling this.
+ */
+void
+vbi_raw_decoder_reset (vbi_raw_decoder * rd)
+{
+  vbi3_raw_decoder *rd3;
+
+  if (!rd)
+    return;                     /* compatibility */
+
+  assert (NULL != rd);
+
+  rd3 = (vbi3_raw_decoder *) rd->pattern;
+
+  pthread_mutex_lock (&rd->mutex);
+
+  {
+    vbi3_raw_decoder_reset (rd3);
+  }
+
+  pthread_mutex_unlock (&rd->mutex);
+}
+
+/**
+ * @param rd Pointer to initialized vbi_raw_decoder
+ *  structure, can be @c NULL.
+ * 
+ * Free all resources associated with @a rd.
+ */
+void
+vbi_raw_decoder_destroy (vbi_raw_decoder * rd)
+{
+  vbi3_raw_decoder *rd3;
+
+  assert (NULL != rd);
+
+  rd3 = (vbi3_raw_decoder *) rd->pattern;
+
+  vbi3_raw_decoder_delete (rd3);
+
+  pthread_mutex_destroy (&rd->mutex);
+
+  CLEAR (*rd);
+}
+
+/**
+ * @param rd Pointer to a vbi_raw_decoder structure.
+ * 
+ * Initializes a vbi_raw_decoder structure.
+ */
+void
+vbi_raw_decoder_init (vbi_raw_decoder * rd)
+{
+  vbi3_raw_decoder *rd3;
+
+  assert (NULL != rd);
+
+  CLEAR (*rd);
+
+  pthread_mutex_init (&rd->mutex, NULL);
+
+  rd3 = vbi3_raw_decoder_new ( /* sampling_par */ NULL);
+  assert (NULL != rd3);
+
+  rd->pattern = (int8_t *) rd3;
+}
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/ext/closedcaption/decoder.h b/ext/closedcaption/decoder.h
new file mode 100644 (file)
index 0000000..f12147d
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ *  libzvbi -- Old raw VBI decoder
+ *
+ *  Copyright (C) 2000, 2001, 2002 Michael H. Schimek
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the 
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA  02110-1301  USA.
+ */
+
+/* $Id: decoder.h,v 1.11 2008-02-19 00:35:15 mschimek Exp $ */
+
+#ifndef DECODER_H
+#define DECODER_H
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "bcd.h"
+#include "sliced.h"
+
+/* Public */
+
+#include <pthread.h>
+
+/* Bit slicer */
+
+/**
+ * @ingroup Rawdec
+ * @brief Image format used as source to vbi_bit_slice() and vbi_raw_decode().
+ *
+ * @htmlonly
+<table border=1>
+<tr><th>Symbol</th><th>Byte&nbsp;0</th><th>Byte&nbsp;1</th><th>Byte&nbsp;2</th><th>Byte&nbsp;3</th></tr>
+<tr><td colspan=5>Planar YUV 4:2:0 data.</td></tr>
+<tr><td>VBI_PIXFMT_YUV420</td><td colspan=4>
+ <table>
+  <tr><th>Y plane</th><th>U plane</th><th>V plane</th></tr>
+  <tr><td><table border=1>
+   <tr><td>Y00</td><td>Y01</td><td>Y02</td><td>Y03</td></tr>
+   <tr><td>Y10</td><td>Y11</td><td>Y12</td><td>Y13</td></tr>
+   <tr><td>Y20</td><td>Y21</td><td>Y22</td><td>Y23</td></tr>
+   <tr><td>Y30</td><td>Y31</td><td>Y32</td><td>Y33</td></tr>
+  </table></td>
+  <td><table border=1>
+   <tr><td>Cb00</td><td>Cb01</td></tr>
+   <tr><td>Cb10</td><td>Cb11</td></tr>
+  </table></td>
+  <td><table border=1>
+   <tr><td>Cr00</td><td>Cr01</td></tr>
+   <tr><td>Cr10</td><td>Cr11</td></tr>
+  </table></td>
+ </tr></table></td>
+</tr>
+<tr><td colspan=5>Packed YUV 4:2:2 data.</td></tr>
+<tr><td>VBI_PIXFMT_YUYV</td><td>Y0</td><td>Cb</td><td>Y1</td><td>Cr</td></tr>
+<tr><td>VBI_PIXFMT_YVYU</td><td>Y0</td><td>Cr</td><td>Y1</td><td>Cb</td></tr>
+<tr><td>VBI_PIXFMT_UYVY</td><td>Cb</td><td>Y0</td><td>Cr</td><td>Y1</td></tr>
+<tr><td>VBI_PIXFMT_VYUY</td><td>Cr</td><td>Y0</td><td>Cb</td><td>Y1</td></tr>
+<tr><td colspan=5>Packed 32 bit RGB data.</td></tr>
+<tr><td>VBI_PIXFMT_RGBA32_LE VBI_PIXFMT_ARGB32_BE</td>
+<td>r7&nbsp;...&nbsp;r0</td><td>g7&nbsp;...&nbsp;g0</td>
+<td>b7&nbsp;...&nbsp;b0</td><td>a7&nbsp;...&nbsp;a0</td></tr>
+<tr><td>VBI_PIXFMT_BGRA32_LE VBI_PIXFMT_ARGB32_BE</td>
+<td>b7&nbsp;...&nbsp;b0</td><td>g7&nbsp;...&nbsp;g0</td>
+<td>r7&nbsp;...&nbsp;r0</td><td>a7&nbsp;...&nbsp;a0</td></tr>
+<tr><td>VBI_PIXFMT_ARGB32_LE VBI_PIXFMT_BGRA32_BE</td>
+<td>a7&nbsp;...&nbsp;a0</td><td>r7&nbsp;...&nbsp;r0</td>
+<td>g7&nbsp;...&nbsp;g0</td><td>b7&nbsp;...&nbsp;b0</td></tr>
+<tr><td>VBI_PIXFMT_ABGR32_LE VBI_PIXFMT_RGBA32_BE</td>
+<td>a7&nbsp;...&nbsp;a0</td><td>b7&nbsp;...&nbsp;b0</td>
+<td>g7&nbsp;...&nbsp;g0</td><td>r7&nbsp;...&nbsp;r0</td></tr>
+<tr><td colspan=5>Packed 24 bit RGB data.</td></tr>
+<tr><td>VBI_PIXFMT_RGBA24</td>
+<td>r7&nbsp;...&nbsp;r0</td><td>g7&nbsp;...&nbsp;g0</td>
+<td>b7&nbsp;...&nbsp;b0</td><td>&nbsp;</td></tr>
+<tr><td>VBI_PIXFMT_BGRA24</td>
+<td>b7&nbsp;...&nbsp;b0</td><td>g7&nbsp;...&nbsp;g0</td>
+<td>r7&nbsp;...&nbsp;r0</td><td>&nbsp;</td></tr>
+<tr><td colspan=5>Packed 16 bit RGB data.</td></tr>
+<tr><td>VBI_PIXFMT_RGB16_LE</td>
+<td>g2&nbsp;g1&nbsp;g0&nbsp;r4&nbsp;r3&nbsp;r2&nbsp;r1&nbsp;r0</td>
+<td>b4&nbsp;b3&nbsp;b2&nbsp;b1&nbsp;b0&nbsp;g5&nbsp;g4&nbsp;g3</td>
+<td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>VBI_PIXFMT_BGR16_LE</td>
+<td>g2&nbsp;g1&nbsp;g0&nbsp;b4&nbsp;b3&nbsp;b2&nbsp;b1&nbsp;b0</td>
+<td>r4&nbsp;r3&nbsp;r2&nbsp;r1&nbsp;r0&nbsp;g5&nbsp;g4&nbsp;g3</td>
+<td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>VBI_PIXFMT_RGB16_BE</td>
+<td>b4&nbsp;b3&nbsp;b2&nbsp;b1&nbsp;b0&nbsp;g5&nbsp;g4&nbsp;g3</td>
+<td>g2&nbsp;g1&nbsp;g0&nbsp;r4&nbsp;r3&nbsp;r2&nbsp;r1&nbsp;r0</td>
+<td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>VBI_PIXFMT_BGR16_BE</td>
+<td>r4&nbsp;r3&nbsp;r2&nbsp;r1&nbsp;r0&nbsp;g5&nbsp;g4&nbsp;g3</td>
+<td>g2&nbsp;g1&nbsp;g0&nbsp;b4&nbsp;b3&nbsp;b2&nbsp;b1&nbsp;b0</td>
+<td>&nbsp;</td><td>&nbsp;</td></tr>
+<tr><td colspan=5>Packed 15 bit RGB data.</td></tr>
+<tr><td>VBI_PIXFMT_RGBA15_LE</td>
+<td>g2&nbsp;g1&nbsp;g0&nbsp;r4&nbsp;r3&nbsp;r2&nbsp;r1&nbsp;r0</td>
+<td>a0&nbsp;b4&nbsp;b3&nbsp;b2&nbsp;b1&nbsp;b0&nbsp;g4&nbsp;g3</td>
+<td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>VBI_PIXFMT_BGRA15_LE</td>
+<td>g2&nbsp;g1&nbsp;g0&nbsp;b4&nbsp;b3&nbsp;b2&nbsp;b1&nbsp;b0</td>
+<td>a0&nbsp;r4&nbsp;r3&nbsp;r2&nbsp;r1&nbsp;r0&nbsp;g4&nbsp;g3</td>
+<td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>VBI_PIXFMT_ARGB15_LE</td>
+<td>g1&nbsp;g0&nbsp;r4&nbsp;r3&nbsp;r2&nbsp;r1&nbsp;r0&nbsp;a0</td>
+<td>b4&nbsp;b3&nbsp;b2&nbsp;b1&nbsp;b0&nbsp;g4&nbsp;g3&nbsp;g2</td>
+<td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>VBI_PIXFMT_ABGR15_LE</td>
+<td>g1&nbsp;g0&nbsp;b4&nbsp;b3&nbsp;b2&nbsp;b1&nbsp;b0&nbsp;a0</td>
+<td>r4&nbsp;r3&nbsp;r2&nbsp;r1&nbsp;r0&nbsp;g4&nbsp;g3&nbsp;g2</td>
+<td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>VBI_PIXFMT_RGBA15_BE</td>
+<td>a0&nbsp;b4&nbsp;b3&nbsp;b2&nbsp;b1&nbsp;b0&nbsp;g4&nbsp;g3</td>
+<td>g2&nbsp;g1&nbsp;g0&nbsp;r4&nbsp;r3&nbsp;r2&nbsp;r1&nbsp;r0</td>
+<td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>VBI_PIXFMT_BGRA15_BE</td>
+<td>a0&nbsp;r4&nbsp;r3&nbsp;r2&nbsp;r1&nbsp;r0&nbsp;g4&nbsp;g3</td>
+<td>g2&nbsp;g1&nbsp;g0&nbsp;b4&nbsp;b3&nbsp;b2&nbsp;b1&nbsp;b0</td>
+<td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>VBI_PIXFMT_ARGB15_BE</td>
+<td>b4&nbsp;b3&nbsp;b2&nbsp;b1&nbsp;b0&nbsp;g4&nbsp;g3&nbsp;g2</td>
+<td>g1&nbsp;g0&nbsp;r4&nbsp;r3&nbsp;r2&nbsp;r1&nbsp;r0&nbsp;a0</td>
+<td>&nbsp;</td><td>&nbsp;</td></tr><tr><td>VBI_PIXFMT_ABGR15_BE</td>
+<td>r4&nbsp;r3&nbsp;r2&nbsp;r1&nbsp;r0&nbsp;g4&nbsp;g3&nbsp;g2</td>
+<td>g1&nbsp;g0&nbsp;b4&nbsp;b3&nbsp;b2&nbsp;b1&nbsp;b0&nbsp;a0</td>
+<td>&nbsp;</td><td>&nbsp;</td></tr>
+</table>
+@endhtmlonly */
+/* Attn: keep this in sync with rte, don't change order */
+typedef enum {
+       VBI_PIXFMT_YUV420 = 1,
+       VBI_PIXFMT_YUYV,
+       VBI_PIXFMT_YVYU,
+       VBI_PIXFMT_UYVY,
+       VBI_PIXFMT_VYUY,
+        VBI_PIXFMT_PAL8,
+       VBI_PIXFMT_RGBA32_LE = 32,
+       VBI_PIXFMT_RGBA32_BE,
+       VBI_PIXFMT_BGRA32_LE,
+       VBI_PIXFMT_BGRA32_BE,
+       VBI_PIXFMT_ABGR32_BE = 32, /* synonyms */
+       VBI_PIXFMT_ABGR32_LE,
+       VBI_PIXFMT_ARGB32_BE,
+       VBI_PIXFMT_ARGB32_LE,
+       VBI_PIXFMT_RGB24,
+       VBI_PIXFMT_BGR24,
+       VBI_PIXFMT_RGB16_LE,
+       VBI_PIXFMT_RGB16_BE,
+       VBI_PIXFMT_BGR16_LE,
+       VBI_PIXFMT_BGR16_BE,
+       VBI_PIXFMT_RGBA15_LE,
+       VBI_PIXFMT_RGBA15_BE,
+       VBI_PIXFMT_BGRA15_LE,
+       VBI_PIXFMT_BGRA15_BE,
+       VBI_PIXFMT_ARGB15_LE,
+       VBI_PIXFMT_ARGB15_BE,
+       VBI_PIXFMT_ABGR15_LE,
+       VBI_PIXFMT_ABGR15_BE
+} vbi_pixfmt;
+
+/* Private */
+
+typedef uint64_t vbi_pixfmt_set;
+
+#define VBI_MAX_PIXFMTS 64
+#define VBI_PIXFMT_SET(pixfmt) (((vbi_pixfmt_set) 1) << (pixfmt))
+#define VBI_PIXFMT_SET_YUV (VBI_PIXFMT_SET (VBI_PIXFMT_YUV420) |       \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_YUYV) |          \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_YVYU) |          \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_UYVY) |          \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_VYUY))
+#define VBI_PIXFMT_SET_RGB (VBI_PIXFMT_SET (VBI_PIXFMT_RGBA32_LE) |    \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_RGBA32_BE) |     \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_BGRA32_LE) |     \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_BGRA32_BE) |     \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_RGB24) |         \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_BGR24) |         \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_RGB16_LE) |      \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_RGB16_BE) |      \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_BGR16_LE) |      \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_BGR16_BE) |      \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_RGBA15_LE) |     \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_RGBA15_BE) |     \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_BGRA15_LE) |     \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_BGRA15_BE) |     \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_ARGB15_LE) |     \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_ARGB15_BE) |     \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_ABGR15_LE) |     \
+                           VBI_PIXFMT_SET (VBI_PIXFMT_ABGR15_BE))
+#define VBI_PIXFMT_SET_ALL (VBI_PIXFMT_SET_YUV |                       \
+                           VBI_PIXFMT_SET_RGB)
+
+#define VBI_PIXFMT_BPP(fmt)                                            \
+       (((fmt) == VBI_PIXFMT_YUV420) ? 1 :                             \
+        (((fmt) >= VBI_PIXFMT_RGBA32_LE                                \
+          && (fmt) <= VBI_PIXFMT_BGRA32_BE) ? 4 :                      \
+         (((fmt) == VBI_PIXFMT_RGB24                                   \
+           || (fmt) == VBI_PIXFMT_BGR24) ? 3 : 2)))
+
+/* Public */
+
+/**
+ * @ingroup Rawdec
+ * @brief Modulation used for VBI data transmission.
+ */
+typedef enum {
+       /**
+        * The data is 'non-return to zero' coded, logical '1' bits
+        * are described by high sample values, logical '0' bits by
+        * low values. The data is last significant bit first transmitted.
+        */
+       VBI_MODULATION_NRZ_LSB,
+       /**
+        * 'Non-return to zero' coded, most significant bit first
+        * transmitted.
+        */
+       VBI_MODULATION_NRZ_MSB,
+       /**
+        * The data is 'bi-phase' coded. Each data bit is described
+        * by two complementary signalling elements, a logical '1'
+        * by a sequence of '10' elements, a logical '0' by a '01'
+        * sequence. The data is last significant bit first transmitted.
+        */
+       VBI_MODULATION_BIPHASE_LSB,
+       /**
+        * 'Bi-phase' coded, most significant bit first transmitted.
+        */
+       VBI_MODULATION_BIPHASE_MSB
+} vbi_modulation;
+
+/**
+ * @ingroup Rawdec
+ * @brief Bit slicer context.
+ *
+ * The contents of this structure are private,
+ * use vbi_bit_slicer_init() to initialize.
+ */
+typedef struct vbi_bit_slicer {
+       vbi_bool        (* func)(struct vbi_bit_slicer *slicer,
+                                uint8_t *raw, uint8_t *buf);
+       unsigned int    cri;
+       unsigned int    cri_mask;
+       int             thresh;
+       int             cri_bytes;
+       int             cri_rate;
+       int             oversampling_rate;
+       int             phase_shift;
+       int             step;
+       unsigned int    frc;
+       int             frc_bits;
+       int             payload;
+       int             endian;
+       int             skip;
+} vbi_bit_slicer;
+
+/**
+ * @addtogroup Rawdec
+ * @{
+ */
+extern void            vbi_bit_slicer_init(vbi_bit_slicer *slicer,
+                                           int raw_samples, int sampling_rate,
+                                           int cri_rate, int bit_rate,
+                                           unsigned int cri_frc, unsigned int cri_mask,
+                                           int cri_bits, int frc_bits, int payload,
+                                           vbi_modulation modulation, vbi_pixfmt fmt);
+/**
+ * @param slicer Pointer to initialized vbi_bit_slicer object.
+ * @param raw Input data. At least the number of pixels or samples
+ *  given as @a raw_samples to vbi_bit_slicer_init().
+ * @param buf Output data. The buffer must be large enough to store
+ *   the number of bits given as @a payload to vbi_bit_slicer_init().
+ * 
+ * Decode one scan line of raw vbi data. Note the bit slicer tries
+ * to adapt to the average signal amplitude, you should avoid
+ * using the same vbi_bit_slicer object for data from different
+ * devices.
+ *
+ * @note As a matter of speed this function does not lock the
+ * @a slicer. When you want to share a vbi_bit_slicer object between
+ * multiple threads you must implement your own locking mechanism.
+ * 
+ * @return
+ * @c FALSE if the raw data does not contain the expected
+ * information, i. e. the CRI/FRC has not been found. This may also
+ * result from a too weak or noisy signal. Error correction must be
+ * implemented at a higher layer.
+ */
+_vbi_inline vbi_bool
+vbi_bit_slice(vbi_bit_slicer *slicer, uint8_t *raw, uint8_t *buf)
+{
+       return slicer->func(slicer, raw, buf);
+}
+/** @} */
+
+/**
+ * @ingroup Rawdec
+ * @brief Raw vbi decoder context.
+ *
+ * Only the sampling parameters are public. See
+ * vbi_raw_decoder_parameters() and vbi_raw_decoder_add_services()
+ * for usage.
+ */
+typedef struct vbi_raw_decoder {
+       /* Sampling parameters */
+
+       /**
+        * Either 525 (M/NTSC, M/PAL) or 625 (PAL, SECAM), describing the
+        * scan line system all line numbers refer to.
+        */
+       int                     scanning;
+       /**
+        * Format of the raw vbi data.
+        */
+       vbi_pixfmt              sampling_format;
+       /**
+        * Sampling rate in Hz, the number of samples or pixels
+        * captured per second.
+        */
+       int                     sampling_rate;          /* Hz */
+       /**
+        * Number of samples or pixels captured per scan line,
+        * in bytes. This determines the raw vbi image width and you
+        * want it large enough to cover all data transmitted in the line (with
+        * headroom).
+        */
+       int                     bytes_per_line;
+       /**
+        * The distance from 0H (leading edge hsync, half amplitude point)
+        * to the first sample (pixel) captured, in samples (pixels). You want
+        * an offset small enough not to miss the start of the data
+        * transmitted.
+        */
+       int                     offset;                 /* 0H, samples */
+       /**
+        * First scan line to be captured, first and second field
+        * respectively, according to the ITU-R line numbering scheme
+        * (see vbi_sliced). Set to zero if the exact line number isn't
+        * known.
+        */
+       int                     start[2];               /* ITU-R numbering */
+       /**
+        * Number of scan lines captured, first and second
+        * field respectively. This can be zero if only data from one
+        * field is required. The sum @a count[0] + @a count[1] determines the
+        * raw vbi image height.
+        */
+       int                     count[2];               /* field lines */
+       /**
+        * In the raw vbi image, normally all lines of the second
+        * field are supposed to follow all lines of the first field. When
+        * this flag is set, the scan lines of first and second field
+        * will be interleaved in memory. This implies @a count[0] and @a count[1]
+        * are equal.
+        */
+       vbi_bool                interlaced;
+       /**
+        * Fields must be stored in temporal order, i. e. as the
+        * lines have been captured. It is assumed that the first field is
+        * also stored first in memory, however if the hardware cannot reliable
+        * distinguish fields this flag shall be cleared, which disables
+        * decoding of data services depending on the field number.
+        */
+       vbi_bool                synchronous;
+
+       /*< private >*/
+
+       pthread_mutex_t         mutex;
+
+       unsigned int            services;
+       int                     num_jobs;
+
+       int8_t *                pattern;
+       struct _vbi_raw_decoder_job {
+               unsigned int            id;
+               int                     offset;
+               vbi_bit_slicer          slicer;
+       }                       jobs[8];
+} vbi_raw_decoder;
+
+/**
+ * @addtogroup Rawdec
+ * @{
+ */
+extern void            vbi_raw_decoder_init(vbi_raw_decoder *rd);
+extern void            vbi_raw_decoder_reset(vbi_raw_decoder *rd);
+extern void            vbi_raw_decoder_destroy(vbi_raw_decoder *rd);
+extern unsigned int    vbi_raw_decoder_add_services(vbi_raw_decoder *rd,
+                                                    unsigned int services,
+                                                    int strict);
+extern unsigned int     vbi_raw_decoder_check_services(vbi_raw_decoder *rd,
+                                                    unsigned int services, int strict);
+extern unsigned int    vbi_raw_decoder_remove_services(vbi_raw_decoder *rd,
+                                                       unsigned int services);
+extern void             vbi_raw_decoder_resize( vbi_raw_decoder *rd,
+                                               int * start, unsigned int * count );
+extern unsigned int    vbi_raw_decoder_parameters(vbi_raw_decoder *rd, unsigned int services,
+                                                  int scanning, int *max_rate);
+extern int             vbi_raw_decode(vbi_raw_decoder *rd, uint8_t *raw, vbi_sliced *out);
+/** @} */
+
+/* Private */
+
+#endif /* DECODER_H */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/ext/closedcaption/macros.h b/ext/closedcaption/macros.h
new file mode 100644 (file)
index 0000000..2e9bdfb
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ *  libzvbi -- Useful macros
+ *
+ *  Copyright (C) 2002, 2003, 2004, 2007 Michael H. Schimek
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the 
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA  02110-1301  USA.
+ */
+
+/* $Id: macros.h,v 1.12 2013-07-10 23:11:18 mschimek Exp $ */
+
+#ifndef __ZVBI_MACROS_H__
+#define __ZVBI_MACROS_H__
+
+#ifdef __cplusplus
+#  define VBI_BEGIN_DECLS extern "C" {
+#  define VBI_END_DECLS }
+#else
+#  define VBI_BEGIN_DECLS
+#  define VBI_END_DECLS
+#endif
+
+VBI_BEGIN_DECLS
+
+/* Public */
+
+#if __GNUC__ >= 4
+#  define _vbi_sentinel __attribute__ ((__sentinel__(0)))
+#  define _vbi_deprecated __attribute__ ((__deprecated__))
+#else
+#  define _vbi_sentinel
+#  define _vbi_deprecated
+#  define __restrict__
+#endif
+
+#if (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) || __GNUC__ >= 4
+#  define _vbi_nonnull(params) __attribute__ ((__nonnull__ params))
+#  define _vbi_format(params) __attribute__ ((__format__ params))
+#else
+#  define _vbi_nonnull(params)
+#  define _vbi_format(params)
+#endif
+
+#if __GNUC__ >= 3
+#  define _vbi_pure __attribute__ ((__pure__))
+#  define _vbi_alloc __attribute__ ((__malloc__))
+#else
+#  define _vbi_pure
+#  define _vbi_alloc
+#endif
+
+#if __GNUC__ >= 2
+#  define _vbi_unused __attribute__ ((__unused__))
+#  define _vbi_const __attribute__ ((__const__))
+#  define _vbi_inline static __inline__
+#else
+#  define _vbi_unused
+#  define _vbi_const
+#  define _vbi_inline static
+#endif
+
+/**
+ * @ingroup Basic
+ * @name Boolean type
+ * @{
+ */
+#ifndef TRUE
+#  define TRUE 1
+#endif
+#ifndef FALSE
+#  define FALSE 0
+#endif
+
+typedef int vbi_bool;
+/** @} */
+
+#ifndef NULL
+#  ifdef __cplusplus
+#    define NULL (0L)
+#  else
+#    define NULL ((void *) 0)
+#  endif
+#endif
+
+/* XXX Document me - for variadic funcs. */
+#define VBI_END ((void *) 0)
+
+#if 0
+typedef void
+vbi_lock_fn                    (void *                 user_data);
+typedef void
+vbi_unlock_fn                  (void *                 user_data);
+#endif
+
+/**
+ * @ingroup Basic
+ * @{
+ */
+typedef enum {
+       /** External error causes, for example lack of memory. */
+       VBI_LOG_ERROR           = 1 << 3,
+
+       /**
+        * Invalid parameters and similar problems which suggest
+        * a bug in the application using the library.
+        */
+       VBI_LOG_WARNING         = 1 << 4,
+
+       /**
+        * Causes of possibly undesired results, for example when a
+        * data service cannot be decoded with the current video
+        * standard setting.
+        */
+       VBI_LOG_NOTICE          = 1 << 5,
+
+       /** Progress messages. */
+       VBI_LOG_INFO            = 1 << 6,
+
+       /** Information useful to debug the library. */
+       VBI_LOG_DEBUG           = 1 << 7,
+
+       /** Driver responses (strace). Not implemented yet. */
+       VBI_LOG_DRIVER          = 1 << 8,
+
+       /** More detailed debugging information. */
+       VBI_LOG_DEBUG2          = 1 << 9,
+       VBI_LOG_DEBUG3          = 1 << 10
+} vbi_log_mask;
+
+typedef void
+vbi_log_fn                     (vbi_log_mask           level,
+                                const char *           context,
+                                const char *           message,
+                                void *                 user_data);
+
+extern vbi_log_fn              vbi_log_on_stderr;
+/** @} */
+
+/* Private */
+
+typedef struct {
+       vbi_log_fn *            fn;
+       void *                  user_data;
+       vbi_log_mask            mask;
+} _vbi_log_hook;
+
+VBI_END_DECLS
+
+#endif /* __ZVBI_MACROS_H__ */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/ext/closedcaption/misc.h b/ext/closedcaption/misc.h
new file mode 100644 (file)
index 0000000..f978a51
--- /dev/null
@@ -0,0 +1,525 @@
+/*
+ *  libzvbi -- Miscellaneous cows and chickens
+ *
+ *  Copyright (C) 2000-2003 Iñaki García Etxebarria
+ *  Copyright (C) 2002-2007 Michael H. Schimek
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the 
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA  02110-1301  USA.
+ */
+
+/* $Id: misc.h,v 1.24 2013-07-02 02:32:31 mschimek Exp $ */
+
+#ifndef MISC_H
+#define MISC_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>          /* (u)intXX_t */
+#include <sys/types.h>         /* (s)size_t */
+#include <float.h>             /* DBL_MAX */
+#include <limits.h>            /* (S)SIZE_MAX */
+#include <assert.h>
+#include <glib.h>
+#include <gst/gst.h>
+
+#include "macros.h"
+
+#define N_ELEMENTS(array) (sizeof (array) / sizeof (*(array)))
+
+#ifdef __GNUC__
+
+#if __GNUC__ < 3
+/* Expect expression usually true/false, schedule accordingly. */
+#  define likely(expr) (expr)
+#  define unlikely(expr) (expr)
+#else
+#  define likely(expr) __builtin_expect(expr, 1)
+#  define unlikely(expr) __builtin_expect(expr, 0)
+#endif
+
+#undef __i386__
+#undef __i686__
+/* FIXME #cpu is deprecated
+#if #cpu (i386)
+#  define __i386__ 1
+#endif
+#if #cpu (i686)
+#  define __i686__ 1
+#endif
+*/
+
+/* &x == PARENT (&x.tm_min, struct tm, tm_min),
+   safer than &x == (struct tm *) &x.tm_min. A NULL _ptr is safe and
+   will return NULL, not -offsetof(_member). */
+#undef PARENT
+#define PARENT(_ptr, _type, _member) ({                                        \
+       __typeof__ (&((_type *) 0)->_member) _p = (_ptr);               \
+       (_p != 0) ? (_type *)(((char *) _p) - offsetof (_type,          \
+         _member)) : (_type *) 0;                                      \
+})
+
+/* Like PARENT(), to be used with const _ptr. */
+#define CONST_PARENT(_ptr, _type, _member) ({                          \
+       __typeof__ (&((const _type *) 0)->_member) _p = (_ptr);         \
+       (_p != 0) ? (const _type *)(((const char *) _p) - offsetof      \
+        (const _type, _member)) : (const _type *) 0;                   \
+})
+
+/* Note the following macros have no side effects only when you
+   compile with GCC, so don't expect this. */
+
+/* Absolute value of int, long or long long without a branch.
+   Note ABS (INT_MIN) -> INT_MAX + 1. */
+#undef ABS
+#define ABS(n) ({                                                      \
+       register __typeof__ (n) _n = (n), _t = _n;                      \
+       if (-1 == (-1 >> 1)) { /* do we have signed shifts? */          \
+               _t >>= sizeof (_t) * 8 - 1;                             \
+               _n ^= _t;                                               \
+               _n -= _t;                                               \
+       } else if (_n < 0) { /* also warns if n is unsigned type */     \
+               _n = -_n;                                               \
+       }                                                               \
+       /* return */ _n;                                                \
+})
+
+#undef MIN
+#define MIN(x, y) ({                                                   \
+       __typeof__ (x) _x = (x);                                        \
+       __typeof__ (y) _y = (y);                                        \
+       (void)(&_x == &_y); /* warn if types do not match */            \
+       /* return */ (_x < _y) ? _x : _y;                               \
+})
+
+#undef MAX
+#define MAX(x, y) ({                                                   \
+       __typeof__ (x) _x = (x);                                        \
+       __typeof__ (y) _y = (y);                                        \
+       (void)(&_x == &_y); /* warn if types do not match */            \
+       /* return */ (_x > _y) ? _x : _y;                               \
+})
+
+/* Note other compilers may swap only int, long or pointer. */
+#undef SWAP
+#define SWAP(x, y)                                                     \
+do {                                                                   \
+       __typeof__ (x) _x = x;                                          \
+       x = y;                                                          \
+       y = _x;                                                         \
+} while (0)
+
+#undef SATURATE
+#ifdef __i686__ /* has conditional move */
+#define SATURATE(n, min, max) ({                                       \
+       __typeof__ (n) _n = (n);                                        \
+       __typeof__ (n) _min = (min);                                    \
+       __typeof__ (n) _max = (max);                                    \
+       (void)(&_n == &_min); /* warn if types do not match */          \
+       (void)(&_n == &_max);                                           \
+       if (_n < _min)                                                  \
+               _n = _min;                                              \
+       if (_n > _max)                                                  \
+               _n = _max;                                              \
+       /* return */ _n;                                                \
+})
+#else
+#define SATURATE(n, min, max) ({                                       \
+       __typeof__ (n) _n = (n);                                        \
+       __typeof__ (n) _min = (min);                                    \
+       __typeof__ (n) _max = (max);                                    \
+       (void)(&_n == &_min); /* warn if types do not match */          \
+       (void)(&_n == &_max);                                           \
+       if (_n < _min)                                                  \
+               _n = _min;                                              \
+       else if (_n > _max)                                             \
+               _n = _max;                                              \
+       /* return */ _n;                                                \
+})
+#endif
+
+#else /* !__GNUC__ */
+
+#define likely(expr) (expr)
+#define unlikely(expr) (expr)
+#undef __i386__
+#undef __i686__
+
+static char *
+PARENT_HELPER (char *p, unsigned int offset)
+{ return (0 == p) ? ((char *) 0) : p - offset; }
+
+static const char *
+CONST_PARENT_HELPER (const char *p, unsigned int offset)
+{ return (0 == p) ? ((char *) 0) : p - offset; }
+
+#define PARENT(_ptr, _type, _member)                                   \
+       ((0 == offsetof (_type, _member)) ? (_type *)(_ptr)             \
+        : (_type *) PARENT_HELPER ((char *)(_ptr), offsetof (_type, _member)))
+#define CONST_PARENT(_ptr, _type, _member)                             \
+       ((0 == offsetof (const _type, _member)) ? (const _type *)(_ptr) \
+        : (const _type *) CONST_PARENT_HELPER ((const char *)(_ptr),   \
+         offsetof (const _type, _member)))
+
+#undef ABS
+#define ABS(n) (((n) < 0) ? -(n) : (n))
+
+#undef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+
+#undef MAX
+#define MAX(x, y) (((x) > (y)) ? (x) : (y))
+
+#undef SWAP
+#define SWAP(x, y)                                                     \
+do {                                                                   \
+       long _x = x;                                                    \
+       x = y;                                                          \
+       y = _x;                                                         \
+} while (0)
+
+#undef SATURATE
+#define SATURATE(n, min, max) MIN (MAX (min, n), max)
+
+#endif /* !__GNUC__ */
+
+/* 32 bit constant byte reverse, e.g. 0xAABBCCDD -> 0xDDCCBBAA. */
+#define SWAB32(m)                                                      \
+       (+ (((m) & 0xFF000000) >> 24)                                   \
+        + (((m) & 0xFF0000) >> 8)                                      \
+        + (((m) & 0xFF00) << 8)                                        \
+        + (((m) & 0xFF) << 24))
+
+#ifdef HAVE_BUILTIN_POPCOUNT
+#  define popcnt(x) __builtin_popcount ((uint32_t)(x))
+#else
+#  define popcnt(x) _vbi_popcnt (x)
+#endif
+
+extern unsigned int
+_vbi_popcnt                    (uint32_t               x);
+
+/* NB GCC inlines and optimizes these functions when size is const. */
+#define SET(var) memset (&(var), ~0, sizeof (var))
+
+#define CLEAR(var) memset (&(var), 0, sizeof (var))
+
+/* Useful to copy arrays, otherwise use assignment. */
+#define COPY(d, s)                                                     \
+       (assert (sizeof (d) == sizeof (s)), memcpy (d, s, sizeof (d)))
+
+/* Copy string const into char array. */
+#define STRACPY(array, s)                                              \
+do {                                                                   \
+       /* Complain if s is no string const or won't fit. */            \
+       const char t_[sizeof (array) - 1] _vbi_unused = s;              \
+                                                                       \
+       memcpy (array, s, sizeof (s));                                  \
+} while (0)
+
+/* Copy bits through mask. */
+#define COPY_SET_MASK(dest, from, mask)                                        \
+       (dest ^= (from) ^ (dest & (mask)))
+
+/* Set bits if cond is TRUE, clear if FALSE. */
+#define COPY_SET_COND(dest, bits, cond)                                        \
+        ((cond) ? (dest |= (bits)) : (dest &= ~(bits)))
+
+/* Set and clear bits. */
+#define COPY_SET_CLEAR(dest, set, clear)                               \
+       (dest = (dest & ~(clear)) | (set))
+
+/* For applications, debugging and fault injection during unit tests. */
+
+#define vbi_malloc malloc
+#define vbi_realloc realloc
+#define vbi_strdup strdup
+#define vbi_free free
+
+#define vbi_cache_malloc vbi_malloc
+#define vbi_cache_free vbi_free
+
+/* Helper functions. */
+
+_vbi_inline int
+_vbi_to_ascii                  (int                    c)
+{
+       if (c < 0)
+               return '?';
+
+       c &= 0x7F;
+
+       if (c < 0x20 || c >= 0x7F)
+               return '.';
+
+       return c;
+}
+
+typedef struct {
+       const char *            key;
+       int                     value;
+} _vbi_key_value_pair;
+
+extern vbi_bool
+_vbi_keyword_lookup            (int *                  value,
+                                const char **          inout_s,
+                                const _vbi_key_value_pair * table,
+                                unsigned int           n_pairs)
+  _vbi_nonnull ((1, 2, 3));
+
+extern void
+_vbi_shrink_vector_capacity    (void **                vector,
+                                size_t *               capacity,
+                                size_t                 min_capacity,
+                                size_t                 element_size)
+  _vbi_nonnull ((1, 2));
+extern vbi_bool
+_vbi_grow_vector_capacity      (void **                vector,
+                                size_t *               capacity,
+                                size_t                 min_capacity,
+                                size_t                 element_size)
+  _vbi_nonnull ((1, 2));
+
+/* Logging stuff. */
+#ifdef G_HAVE_ISO_VARARGS
+#define VBI_CAT_LEVEL_LOG(cat,level,object,...) G_STMT_START{          \
+  if (G_UNLIKELY ((level) <= GST_LEVEL_MAX && (level) <= _gst_debug_min)) {                                            \
+    gst_debug_log ((cat), (level), __FILE__, GST_FUNCTION, __LINE__,   \
+        (GObject *) (object), __VA_ARGS__);                            \
+  }                                                                    \
+}G_STMT_END
+#else /* G_HAVE_GNUC_VARARGS */
+#ifdef G_HAVE_GNUC_VARARGS
+#define VBI_CAT_LEVEL_LOG(cat,level,object,args...) G_STMT_START{      \
+  if (G_UNLIKELY ((level) <= GST_LEVEL_MAX && (level) <= _gst_debug_min)) {                                            \
+    gst_debug_log ((cat), (level), __FILE__, GST_FUNCTION, __LINE__,   \
+        (GObject *) (object), ##args );                                        \
+  }                                                                    \
+}G_STMT_END
+#else /* no variadic macros, use inline */
+static inline void
+VBI_CAT_LEVEL_LOG_valist (GstDebugCategory * cat,
+    GstDebugLevel level, gpointer object, const char *format, va_list varargs)
+{
+  if (G_UNLIKELY ((level) <= GST_LEVEL_MAX && (level) <= _gst_debug_min)) {
+    gst_debug_log_valist (cat, level, "", "", 0, (GObject *) object, format,
+        varargs);
+  }
+}
+
+static inline void
+VBI_CAT_LEVEL_LOG (GstDebugCategory * cat, GstDebugLevel level,
+    gpointer object, const char *format, ...)
+{
+  va_list varargs;
+
+  va_start (varargs, format);
+  GST_CAT_LEVEL_LOG_valist (cat, level, object, format, varargs);
+  va_end (varargs);
+}
+#endif
+#endif /* G_HAVE_ISO_VARARGS */
+
+#define error(hook, templ, args...)                                    \
+       VBI_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_ERROR, NULL, templ , ##args)
+#define warning(hook, templ, args...)                                  \
+       VBI_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_WARNING, NULL, templ , ##args)
+#define notice(hook, templ, args...)                                   \
+       VBI_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_INFO, NULL, templ , ##args)
+#define info(hook, templ, args...)                                     \
+       VBI_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_INFO, NULL, templ , ##args)
+#define debug1(hook, templ, args...)                                   \
+       VBI_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_DEBUG, NULL, templ , ##args)
+#define debug2(hook, templ, args...)                                   \
+       VBI_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_LOG, NULL, templ , ##args)
+#define debug3(hook, templ, args...)                                   \
+       VBI_CAT_LEVEL_LOG (GST_CAT_DEFAULT, GST_LEVEL_TRACE, NULL, templ , ##args)
+
+#if 0                          /* Replaced logging with GStreamer logging system */
+extern _vbi_log_hook           _vbi_global_log;
+
+extern void
+_vbi_log_vprintf               (vbi_log_fn *           log_fn,
+                                void *                 user_data,
+                                vbi_log_mask           level,
+                                const char *           source_file,
+                                const char *           context,
+                                const char *           templ,
+                                va_list                ap)
+  _vbi_nonnull ((1, 4, 5, 6));
+extern void
+_vbi_log_printf                (vbi_log_fn *           log_fn,
+                                void *                 user_data,
+                                vbi_log_mask           level,
+                                const char *           source_file,
+                                const char *           context,
+                                const char *           templ,
+                                ...)
+  _vbi_nonnull ((1, 4, 5, 6)) _vbi_format ((printf, 6, 7));
+
+#define _vbi_log(hook, level, templ, args...)                          \
+do {                                                                   \
+       _vbi_log_hook *_h = hook;                                       \
+                                                                       \
+       if ((NULL != _h && 0 != (_h->mask & level))                     \
+           || (_h = &_vbi_global_log, 0 != (_h->mask & level)))        \
+               _vbi_log_printf (_h->fn, _h->user_data,         \
+                                 level, __FILE__, __FUNCTION__,        \
+                                 templ , ##args);                      \
+} while (0)
+
+#define _vbi_vlog(hook, level, templ, ap)                              \
+do {                                                                   \
+       _vbi_log_hook *_h = hook;                                       \
+                                                                       \
+       if ((NULL != _h && 0 != (_h->mask & level))                     \
+           || (_h = &_vbi_global_log, 0 != (_h->mask & level)))        \
+               _vbi_log_vprintf (_h->fn, _h->user_data,                \
+                                 level, __FILE__, __FUNCTION__,        \
+                                 templ, ap);                           \
+} while (0)
+#define error(hook, templ, args...)                                    \
+       _vbi_log (hook, VBI_LOG_ERROR, templ , ##args)
+#define warning(hook, templ, args...)                                  \
+       _vbi_log (hook, VBI_LOG_ERROR, templ , ##args)
+#define notice(hook, templ, args...)                                   \
+       _vbi_log (hook, VBI_LOG_NOTICE, templ , ##args)
+#define info(hook, templ, args...)                                     \
+       _vbi_log (hook, VBI_LOG_INFO, templ , ##args)
+#define debug1(hook, templ, args...)                                   \
+       _vbi_log (hook, VBI_LOG_DEBUG, templ , ##args)
+#define debug2(hook, templ, args...)                                   \
+       _vbi_log (hook, VBI_LOG_DEBUG2, templ , ##args)
+#define debug3(hook, templ, args...)                                   \
+       _vbi_log (hook, VBI_LOG_DEBUG3, templ , ##args)
+#endif
+
+/* Portability stuff. */
+
+/* These should be defined in inttypes.h. */
+#ifndef PRId64
+#  define PRId64 "lld"
+#endif
+#ifndef PRIu64
+#  define PRIu64 "llu"
+#endif
+#ifndef PRIx64
+#  define PRIx64 "llx"
+#endif
+
+/* Should be defined in C99 limits.h? */
+#ifndef SIZE_MAX
+#  define SIZE_MAX ((size_t) -1)
+#endif
+
+#ifndef TIME_MIN
+#  define TIME_MIN (_vbi_time_min ())
+_vbi_inline time_t
+_vbi_time_min                  (void)
+{
+       const time_t t = (time_t) -1.25;
+
+       if (t < -1) {
+               return (time_t)((sizeof (time_t) > 4) ? DBL_MIN : FLT_MIN);
+       } else if (t < 0) {
+               return ((uint64_t) 1) << (sizeof (time_t) * 8 - 1);
+       } else {
+               return 0;
+       }
+}
+#endif
+
+#ifndef TIME_MAX
+#  define TIME_MAX (_vbi_time_max ())
+_vbi_inline time_t
+_vbi_time_max                  (void)
+{
+       const time_t t = (time_t) -1.25;
+
+       if (t < -1) {
+               return (time_t)((sizeof (time_t) > 4) ? DBL_MAX : FLT_MAX);
+       } else if (t < 0) {
+               /* Most likely signed 32 or 64 bit. */
+               return (((uint64_t) 1) << (sizeof (time_t) * 8 - 1)) - 1;
+       } else {
+               return -1;
+       }
+}
+#endif
+
+/* __va_copy is a GNU extension. */
+#ifndef __va_copy
+#  define __va_copy(ap1, ap2) do { ap1 = ap2; } while (0)
+#endif
+
+/* Use this instead of strncpy(). strlcpy() is a BSD extension. */
+#ifndef HAVE_STRLCPY
+#  define strlcpy _vbi_strlcpy
+#endif
+#undef strncpy
+#define strncpy use_strlcpy_instead
+
+extern size_t
+_vbi_strlcpy                   (char *                 dst,
+                                const char *           src,
+                                size_t                 size)
+  _vbi_nonnull ((1, 2));
+
+/* strndup() is a BSD/GNU extension. */
+#ifndef HAVE_STRNDUP
+#  define strndup _vbi_strndup
+#endif
+
+extern char *
+_vbi_strndup                   (const char *           s,
+                                size_t                 len)
+  _vbi_nonnull ((1));
+
+/* vasprintf() is a GNU extension. */
+#ifndef HAVE_VASPRINTF
+#  define vasprintf _vbi_vasprintf
+#endif
+
+extern int
+_vbi_vasprintf                 (char **                dstp,
+                                const char *           templ,
+                                va_list                ap)
+  _vbi_nonnull ((1, 2));
+
+/* asprintf() is a GNU extension. */
+#ifndef HAVE_ASPRINTF
+#  define asprintf _vbi_asprintf
+#endif
+
+extern int
+_vbi_asprintf                  (char **                dstp,
+                                const char *           templ,
+                                ...)
+  _vbi_nonnull ((1, 2)) _vbi_format ((printf, 2, 3));
+
+#undef sprintf
+#define sprintf use_snprintf_or_asprintf_instead
+
+#endif /* MISC_H */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/ext/closedcaption/raw_decoder.c b/ext/closedcaption/raw_decoder.c
new file mode 100644 (file)
index 0000000..0ff956a
--- /dev/null
@@ -0,0 +1,1279 @@
+/*
+ *  libzvbi -- Raw VBI decoder
+ *
+ *  Copyright (C) 2000-2004 Michael H. Schimek
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the 
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA  02110-1301  USA.
+ */
+
+/* $Id: raw_decoder.c,v 1.24 2008-08-19 10:04:46 mschimek Exp $ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "misc.h"
+#include "raw_decoder.h"
+
+#ifndef RAW_DECODER_PATTERN_DUMP
+#  define RAW_DECODER_PATTERN_DUMP 0
+#endif
+
+#  define sp_sample_format sampling_format
+
+/**
+ * $addtogroup RawDecoder Raw VBI decoder
+ * $ingroup Raw
+ * $brief Converting a raw VBI image to sliced VBI data.
+ */
+
+/* Missing:
+   VITC PAL 6-22 11.2us 1.8125 Mbit NRZ two start bits + CRC
+   VITC NTSC 10-21 ditto
+   CGMS NTSC 20 11us .450450 Mbit NRZ ?
+   MOJI
+*/
+const _vbi_service_par _vbi_service_table[] = {
+  {
+        VBI_SLICED_TELETEXT_A,  /* UNTESTED */
+        "Teletext System A",
+        VBI_VIDEOSTD_SET_625_50,
+        {6, 318},
+        {22, 335},
+        10500, 6203125, 6203125,        /* 397 x FH */
+        0x00AAAAE7, 0xFFFF, 18, 6, 37 * 8, VBI_MODULATION_NRZ_LSB,
+        0,                      /* probably */
+      }, {
+        VBI_SLICED_TELETEXT_B_L10_625,
+        "Teletext System B 625 Level 1.5",
+        VBI_VIDEOSTD_SET_625_50,
+        {7, 320},
+        {22, 335},
+        10300, 6937500, 6937500,        /* 444 x FH */
+        0x00AAAAE4, 0xFFFF, 18, 6, 42 * 8, VBI_MODULATION_NRZ_LSB,
+        0,
+      }, {
+        VBI_SLICED_TELETEXT_B,
+        "Teletext System B, 625",
+        VBI_VIDEOSTD_SET_625_50,
+        {6, 318},
+        {22, 335},
+        10300, 6937500, 6937500,        /* 444 x FH */
+        0x00AAAAE4, 0xFFFF, 18, 6, 42 * 8, VBI_MODULATION_NRZ_LSB,
+        0,
+      }, {
+        VBI_SLICED_TELETEXT_C_625,      /* UNTESTED */
+        "Teletext System C 625",
+        VBI_VIDEOSTD_SET_625_50,
+        {6, 318},
+        {22, 335},
+        10480, 5734375, 5734375,        /* 367 x FH */
+        0x00AAAAE7, 0xFFFF, 18, 6, 33 * 8, VBI_MODULATION_NRZ_LSB,
+        0,
+      }, {
+        VBI_SLICED_TELETEXT_D_625,      /* UNTESTED */
+        "Teletext System D 625",
+        VBI_VIDEOSTD_SET_625_50,
+        {6, 318},
+        {22, 335},
+        10500,                  /* or 10970 depending on field order */
+        5642787, 5642787,       /* 14/11 x FSC (color subcarrier) */
+        0x00AAAAE5, 0xFFFF, 18, 6, 34 * 8, VBI_MODULATION_NRZ_LSB,
+        0,
+      }, {
+        VBI_SLICED_VPS, "Video Program System",
+        VBI_VIDEOSTD_SET_PAL_BG,
+        {16, 0},
+        {16, 0},
+        12500, 5000000, 2500000,        /* 160 x FH */
+        0xAAAA8A99, 0xFFFFFF, 32, 0, 13 * 8,
+        VBI_MODULATION_BIPHASE_MSB,
+        _VBI_SP_FIELD_NUM,
+      }, {
+        VBI_SLICED_VPS_F2, "Pseudo-VPS on field 2",
+        VBI_VIDEOSTD_SET_PAL_BG,
+        {0, 329},
+        {0, 329},
+        12500, 5000000, 2500000,        /* 160 x FH */
+        0xAAAA8A99, 0xFFFFFF, 32, 0, 13 * 8,
+        VBI_MODULATION_BIPHASE_MSB,
+        _VBI_SP_FIELD_NUM,
+      }, {
+        VBI_SLICED_WSS_625, "Wide Screen Signalling 625",
+        VBI_VIDEOSTD_SET_625_50,
+        {23, 0},
+        {23, 0},
+        11000, 5000000, 833333, /* 160/3 x FH */
+        /* ...1000 111 / 0 0011 1100 0111 1000 0011 111x */
+        /* ...0010 010 / 0 1001 1001 0011 0011 1001 110x */
+        0x8E3C783E, 0x2499339C, 32, 0, 14 * 1,
+        VBI_MODULATION_BIPHASE_LSB,
+        /* Hm. Too easily confused with caption?? */
+        _VBI_SP_FIELD_NUM | _VBI_SP_LINE_NUM,
+      }, {
+        VBI_SLICED_CAPTION_625_F1, "Closed Caption 625, field 1",
+        VBI_VIDEOSTD_SET_625_50,
+        {22, 0},
+        {22, 0},
+        10500, 1000000, 500000, /* 32 x FH */
+        0x00005551, 0x7FF, 14, 2, 2 * 8, VBI_MODULATION_NRZ_LSB,
+        _VBI_SP_FIELD_NUM,
+      }, {
+        VBI_SLICED_CAPTION_625_F2, "Closed Caption 625, field 2",
+        VBI_VIDEOSTD_SET_625_50,
+        {0, 335},
+        {0, 335},
+        10500, 1000000, 500000, /* 32 x FH */
+        0x00005551, 0x7FF, 14, 2, 2 * 8, VBI_MODULATION_NRZ_LSB,
+        _VBI_SP_FIELD_NUM,
+      }, {
+        VBI_SLICED_VBI_625, "VBI 625",  /* Blank VBI */
+        VBI_VIDEOSTD_SET_625_50,
+        {6, 318},
+        {22, 335},
+        10000, 1510000, 1510000,
+        0, 0, 0, 0, 10 * 8, 0,  /* 10.0-2 ... 62.9+1 us */
+        0,
+      }, {
+        VBI_SLICED_TELETEXT_B_525,      /* UNTESTED */
+        "Teletext System B 525",
+        VBI_VIDEOSTD_SET_525_60,
+        {10, 272},
+        {21, 284},
+        10500, 5727272, 5727272,        /* 364 x FH */
+        0x00AAAAE4, 0xFFFF, 18, 6, 34 * 8, VBI_MODULATION_NRZ_LSB,
+        0,
+      }, {
+        VBI_SLICED_TELETEXT_C_525,      /* UNTESTED */
+        "Teletext System C 525",
+        VBI_VIDEOSTD_SET_525_60,
+        {10, 272},
+        {21, 284},
+        10480, 5727272, 5727272,        /* 364 x FH */
+        0x00AAAAE7, 0xFFFF, 18, 6, 33 * 8, VBI_MODULATION_NRZ_LSB,
+        0,
+      }, {
+        VBI_SLICED_TELETEXT_D_525,      /* UNTESTED */
+        "Teletext System D 525",
+        VBI_VIDEOSTD_SET_525_60,
+        {10, 272},
+        {21, 284},
+        9780, 5727272, 5727272, /* 364 x FH */
+        0x00AAAAE5, 0xFFFF, 18, 6, 34 * 8, VBI_MODULATION_NRZ_LSB,
+        0,
+      }, {
+#if 0                           /* FIXME probably wrong */
+        VBI_SLICED_WSS_CPR1204, /* NOT CONFIRMED (EIA-J CPR-1204) */
+        "Wide Screen Signalling 525",
+        VBI_VIDEOSTD_SET_NTSC_M_JP,
+        {20, 283},
+        {20, 283},
+        11200, 1789773, 447443, /* 1/8 x FSC */
+        0x000000F0, 0xFF, 8, 0, 20 * 1, VBI_MODULATION_NRZ_MSB,
+        /* No useful FRC, but a six bit CRC */
+        0,
+      }, {
+#endif
+        VBI_SLICED_CAPTION_525_F1,
+        "Closed Caption 525, field 1",
+        VBI_VIDEOSTD_SET_525_60,
+        {21, 0},
+        {21, 0},
+        10500, 1006976, 503488, /* 32 x FH */
+        /* Test of CRI bits has been removed to handle the
+           incorrect signal observed by Rich Kandel (see
+           _VBI_RAW_SHIFT_CC_CRI). */
+        0x03, 0x0F, 4, 0, 2 * 8, VBI_MODULATION_NRZ_LSB,
+        /* 0x00005551, 0x7FF, 14, 2, 2 * 8, VBI_MODULATION_NRZ_LSB, */
+        /* I've seen CC signals on other lines and there's no
+           way to distinguish from the transmitted data. */
+        _VBI_SP_FIELD_NUM | _VBI_SP_LINE_NUM,
+      }, {
+        VBI_SLICED_CAPTION_525_F2,
+        "Closed Caption 525, field 2",
+        VBI_VIDEOSTD_SET_525_60,
+        {0, 284},
+        {0, 284},
+        10500, 1006976, 503488, /* 32 x FH */
+        0x03, 0x0F, 4, 0, 2 * 8, VBI_MODULATION_NRZ_LSB,
+        /* 0x00005551, 0x7FF, 14, 2, 2 * 8, VBI_MODULATION_NRZ_LSB, */
+        _VBI_SP_FIELD_NUM | _VBI_SP_LINE_NUM,
+      }, {
+        VBI_SLICED_2xCAPTION_525,       /* NOT CONFIRMED */
+        "2xCaption 525",
+        VBI_VIDEOSTD_SET_525_60,
+        {10, 0},
+        {21, 0},
+        10500, 1006976, 1006976,        /* 64 x FH */
+        0x000554ED, 0xFFFF, 12, 8, 4 * 8,
+        VBI_MODULATION_NRZ_LSB, /* Tb. */
+        _VBI_SP_FIELD_NUM,
+      }, {
+        VBI_SLICED_VBI_525, "VBI 525",  /* Blank VBI */
+        VBI_VIDEOSTD_SET_525_60,
+        {10, 272},
+        {21, 284},
+        9500, 1510000, 1510000,
+        0, 0, 0, 0, 10 * 8, 0,  /* 9.5-1 ... 62.4+1 us */
+        0,
+      }, {
+        0, NULL,
+        VBI_VIDEOSTD_SET_EMPTY,
+        {0, 0},
+        {0, 0},
+        0, 0, 0,
+        0, 0, 0, 0, 0, 0,
+        0,
+      }
+};
+
+_vbi_inline const _vbi_service_par *
+find_service_par (unsigned int service)
+{
+  unsigned int i;
+
+  for (i = 0; _vbi_service_table[i].id; ++i)
+    if (service == _vbi_service_table[i].id)
+      return _vbi_service_table + i;
+
+  return NULL;
+}
+
+/**
+ * $ingroup Sliced
+ * $param service A data service identifier, for example from a
+ *   vbi_sliced structure.
+ *
+ * $return
+ * Name of the $a service, in ASCII, or $c NULL if unknown.
+ */
+const char *
+vbi_sliced_name (vbi_service_set service)
+{
+  const _vbi_service_par *par;
+
+  /* These are ambiguous */
+  if (service == VBI_SLICED_CAPTION_525)
+    return "Closed Caption 525";
+  if (service == VBI_SLICED_CAPTION_625)
+    return "Closed Caption 625";
+  if (service == (VBI_SLICED_VPS | VBI_SLICED_VPS_F2))
+    return "Video Program System";
+  if (service == VBI_SLICED_TELETEXT_B_L25_625)
+    return "Teletext System B 625 Level 2.5";
+
+  /* Incorrect, no longer in table */
+  if (service == VBI_SLICED_TELETEXT_BD_525)
+    return "Teletext System B/D";
+
+  if ((par = find_service_par (service)))
+    return par->label;
+
+  return NULL;
+}
+
+/**
+ * @ingroup Sliced
+ * @param service A data service identifier, for example from a
+ *   vbi_sliced structure.
+ *
+ * @return
+ * Number of payload bits, @c 0 if the service is unknown.
+ */
+unsigned int
+vbi_sliced_payload_bits (unsigned int service)
+{
+  const _vbi_service_par *par;
+
+  /* These are ambiguous */
+  if (service == VBI_SLICED_CAPTION_525)
+    return 16;
+  if (service == VBI_SLICED_CAPTION_625)
+    return 16;
+  if (service == (VBI_SLICED_VPS | VBI_SLICED_VPS_F2))
+    return 13 * 8;
+  if (service == VBI_SLICED_TELETEXT_B_L25_625)
+    return 42 * 8;
+
+  /* Incorrect, no longer in table */
+  if (service == VBI_SLICED_TELETEXT_BD_525)
+    return 34 * 8;
+
+  if ((par = find_service_par (service)))
+    return par->payload;
+
+  return 0;
+}
+
+static void
+dump_pattern_line (const vbi3_raw_decoder * rd, unsigned int row, FILE * fp)
+{
+  const vbi_sampling_par *sp;
+  unsigned int line;
+  unsigned int i;
+
+  sp = &rd->sampling;
+
+  if (sp->interlaced) {
+    unsigned int field = row & 1;
+
+    if (0 == sp->start[field])
+      line = 0;
+    else
+      line = sp->start[field] + (row >> 1);
+  } else {
+    if (row >= (unsigned int) sp->count[0]) {
+      if (0 == sp->start[1])
+        line = 0;
+      else
+        line = sp->start[1] + row - sp->count[0];
+    } else {
+      if (0 == sp->start[0])
+        line = 0;
+      else
+        line = sp->start[0] + row;
+    }
+  }
+
+  fprintf (fp, "scan line %3u: ", line);
+
+  for (i = 0; i < _VBI3_RAW_DECODER_MAX_WAYS; ++i) {
+    unsigned int pos;
+
+    pos = row * _VBI3_RAW_DECODER_MAX_WAYS;
+    fprintf (fp, "%02x ", (uint8_t) rd->pattern[pos + i]);
+  }
+
+  fputc ('\n', fp);
+}
+
+void
+_vbi3_raw_decoder_dump (const vbi3_raw_decoder * rd, FILE * fp)
+{
+  const vbi_sampling_par *sp;
+  unsigned int i;
+
+  assert (NULL != fp);
+
+  fprintf (fp, "vbi3_raw_decoder %p\n", rd);
+
+  if (NULL == rd)
+    return;
+
+  fprintf (fp, "  services 0x%08x\n", rd->services);
+
+  for (i = 0; i < rd->n_jobs; ++i)
+    fprintf (fp, "  job %u: 0x%08x (%s)\n",
+        i + 1, rd->jobs[i].id, vbi_sliced_name (rd->jobs[i].id));
+
+  if (!rd->pattern) {
+    fprintf (fp, "  no pattern\n");
+    return;
+  }
+
+  sp = &rd->sampling;
+
+  for (i = 0; i < ((unsigned int) sp->count[0]
+          + (unsigned int) sp->count[1]); ++i) {
+    fputs ("  ", fp);
+    dump_pattern_line (rd, i, fp);
+  }
+}
+
+_vbi_inline int
+cpr1204_crc (const vbi_sliced * sliced)
+{
+  const int poly = (1 << 6) + (1 << 1) + 1;
+  int crc, i;
+
+  crc = (+(sliced->data[0] << 12)
+      + (sliced->data[1] << 4)
+      + (sliced->data[2]));
+
+  crc |= (((1 << 6) - 1) << (14 + 6));
+
+  for (i = 14 + 6 - 1; i >= 0; i--) {
+    if (crc & ((1 << 6) << i))
+      crc ^= poly << i;
+  }
+
+  return crc;
+}
+
+static vbi_bool
+slice (vbi3_raw_decoder * rd,
+    vbi_sliced * sliced,
+    _vbi3_raw_decoder_job * job, unsigned int i, const uint8_t * raw)
+{
+  if (rd->debug && NULL != rd->sp_lines) {
+    return vbi3_bit_slicer_slice_with_points
+        (&job->slicer,
+        sliced->data,
+        sizeof (sliced->data),
+        rd->sp_lines[i].points,
+        &rd->sp_lines[i].n_points, N_ELEMENTS (rd->sp_lines[i].points), raw);
+  } else {
+    return vbi3_bit_slicer_slice
+        (&job->slicer, sliced->data, sizeof (sliced->data), raw);
+  }
+}
+
+_vbi_inline vbi_sliced *
+decode_pattern (vbi3_raw_decoder * rd,
+    vbi_sliced * sliced, int8_t * pattern, unsigned int i, const uint8_t * raw)
+{
+  vbi_sampling_par *sp;
+  int8_t *pat;
+
+  sp = &rd->sampling;
+
+  for (pat = pattern;; ++pat) {
+    int j;
+
+    j = *pat;                   /* data service n, blank 0, or counter -n */
+
+    if (j > 0) {
+      _vbi3_raw_decoder_job *job;
+
+      job = rd->jobs + j - 1;
+
+      if (!slice (rd, sliced, job, i, raw)) {
+        continue;               /* no match, try next data service */
+      }
+
+      /* FIXME probably wrong */
+      if (0 && VBI_SLICED_WSS_CPR1204 == job->id) {
+        const int poly = (1 << 6) + (1 << 1) + 1;
+        int crc, j;
+
+        crc = (sliced->data[0] << 12)
+            + (sliced->data[1] << 4)
+            + sliced->data[2];
+        crc |= (((1 << 6) - 1) << (14 + 6));
+
+        for (j = 14 + 6 - 1; j >= 0; j--) {
+          if (crc & ((1 << 6) << j))
+            crc ^= poly << j;
+        }
+
+        if (crc)
+          continue;             /* no match */
+      }
+
+      /* Positive match, output decoded line. */
+
+      /* FIXME: if we have a field number we should
+         really only set the service id of one field. */
+      sliced->id = job->id;
+      sliced->line = 0;
+
+      if (i >= (unsigned int) sp->count[0]) {
+        if (sp->synchronous && 0 != sp->start[1])
+          sliced->line = sp->start[1]
+              + i - sp->count[0];
+      } else {
+        if (sp->synchronous && 0 != sp->start[0])
+          sliced->line = sp->start[0] + i;
+      }
+
+      if (0)
+        fprintf (stderr, "%2d %s\n",
+            sliced->line, vbi_sliced_name (sliced->id));
+
+      ++sliced;
+
+      /* Predict line as non-blank, force testing for
+         all data services in the next 128 frames. */
+      pattern[_VBI3_RAW_DECODER_MAX_WAYS - 1] = -128;
+    } else if (pat == pattern) {
+      /* Line was predicted as blank, once in 16
+         frames look for data services. */
+      if (0 == rd->readjust) {
+        unsigned int size;
+
+        size = sizeof (*pattern)
+            * (_VBI3_RAW_DECODER_MAX_WAYS - 1);
+
+        j = pattern[0];
+        memmove (&pattern[0], &pattern[1], size);
+        pattern[_VBI3_RAW_DECODER_MAX_WAYS - 1] = j;
+      }
+
+      break;
+    } else if ((j = pattern[_VBI3_RAW_DECODER_MAX_WAYS - 1]) < 0) {
+      /* Increment counter, when zero predict line as
+         blank and stop looking for data services until
+         0 == rd->readjust. */
+      /* Disabled because we may miss caption/subtitles
+         when the signal inserter is disabled during silent
+         periods for more than 4-5 seconds. */
+      /* pattern[_VBI3_RAW_DECODER_MAX_WAYS - 1] = j + 1; */
+      break;
+    } else {
+      /* found nothing, j = 0 */
+    }
+
+    /* Try the found data service first next time. */
+    *pat = pattern[0];
+    pattern[0] = j;
+
+    break;                      /* line done */
+  }
+
+  return sliced;
+}
+
+/**
+ * $param rd Pointer to vbi3_raw_decoder object allocated with
+ *   vbi3_raw_decoder_new().
+ * $param sliced Buffer to store the decoded vbi_sliced data. Since every
+ *   vbi scan line may contain data, this should be an array of vbi_sliced
+ *   with the same number of elements as scan lines in the raw image
+ *   (vbi_sampling_parameters.count[0] + .count[1]).
+ * $param max_lines Size of $a sliced data array, in lines, not bytes.
+ * $param raw A raw vbi image as described by the vbi_sampling_par
+ *   associated with $a rd.
+ * 
+ * Decodes a raw vbi image, consisting of several scan lines of raw vbi data,
+ * to sliced vbi data. The output is sorted by ascending line number.
+ * 
+ * Note this function attempts to learn which lines carry which data
+ * service, or if any, to speed up decoding. You should avoid using the same
+ * vbi3_raw_decoder object for different sources.
+ *
+ * $return
+ * The number of lines decoded, i. e. the number of vbi_sliced records
+ * written.
+ */
+unsigned int
+vbi3_raw_decoder_decode (vbi3_raw_decoder * rd,
+    vbi_sliced * sliced, unsigned int max_lines, const uint8_t * raw)
+{
+  vbi_sampling_par *sp;
+  unsigned int scan_lines;
+  unsigned int pitch;
+  int8_t *pattern;
+  const uint8_t *raw1;
+  vbi_sliced *sliced_begin;
+  vbi_sliced *sliced_end;
+  unsigned int i;
+
+  if (!rd->services)
+    return 0;
+
+  sp = &rd->sampling;
+
+  scan_lines = sp->count[0] + sp->count[1];
+  pitch = sp->bytes_per_line << sp->interlaced;
+
+  pattern = rd->pattern;
+
+  raw1 = raw;
+
+  sliced_begin = sliced;
+  sliced_end = sliced + max_lines;
+
+  if (RAW_DECODER_PATTERN_DUMP)
+    _vbi3_raw_decoder_dump (rd, stderr);
+
+  for (i = 0; i < scan_lines; ++i) {
+    if (sliced >= sliced_end)
+      break;
+
+    if (sp->interlaced && i == (unsigned int) sp->count[0])
+      raw = raw1 + sp->bytes_per_line;
+
+    sliced = decode_pattern (rd, sliced, pattern, i, raw);
+
+    pattern += _VBI3_RAW_DECODER_MAX_WAYS;
+    raw += pitch;
+  }
+
+  rd->readjust = (rd->readjust + 1) & 15;
+
+  return sliced - sliced_begin;
+}
+
+/**
+ * $param rd Pointer to vbi3_raw_decoder object allocated with
+ *   vbi3_raw_decoder_new().
+ * 
+ * Resets a vbi3_raw_decoder object, removing all services added
+ * with vbi3_raw_decoder_add_services().
+ */
+void
+vbi3_raw_decoder_reset (vbi3_raw_decoder * rd)
+{
+  assert (NULL != rd);
+
+  if (rd->pattern) {
+    vbi_free (rd->pattern);
+    rd->pattern = NULL;
+  }
+
+  rd->services = 0;
+  rd->n_jobs = 0;
+
+  rd->readjust = 1;
+
+  CLEAR (rd->jobs);
+}
+
+static void
+remove_job_from_pattern (vbi3_raw_decoder * rd, int job_num)
+{
+  int8_t *pattern;
+  unsigned int scan_lines;
+
+  job_num += 1;                 /* index into rd->jobs, 0 means no job */
+
+  pattern = rd->pattern;
+  scan_lines = rd->sampling.count[0] + rd->sampling.count[1];
+
+  /* For each scan line. */
+  while (scan_lines-- > 0) {
+    int8_t *dst;
+    int8_t *src;
+    int8_t *end;
+
+    dst = pattern;
+    end = pattern + _VBI3_RAW_DECODER_MAX_WAYS;
+
+    /* Remove jobs with job_num, fill up pattern with 0.
+       Jobs above job_num move down in rd->jobs. */
+    for (src = dst; src < end; ++src) {
+      int8_t num = *src;
+
+      if (num > job_num)
+        *dst++ = num - 1;
+      else if (num != job_num)
+        *dst++ = num;
+    }
+
+    while (dst < end)
+      *dst++ = 0;
+
+    pattern = end;
+  }
+}
+
+/**
+ * $param rd Pointer to vbi3_raw_decoder object allocated with
+ *   vbi3_raw_decoder_new().
+ * $param services Set of data services.
+ * 
+ * Removes one or more data services to be decoded from the
+ * vbi3_raw_decoder object.
+ * 
+ * $return 
+ * Set describing the remaining data services $a rd will decode.
+ */
+vbi_service_set
+    vbi3_raw_decoder_remove_services
+    (vbi3_raw_decoder * rd, vbi_service_set services) {
+  _vbi3_raw_decoder_job *job;
+  unsigned int job_num;
+
+  assert (NULL != rd);
+
+  job = rd->jobs;
+  job_num = 0;
+
+  while (job_num < rd->n_jobs) {
+    if (job->id & services) {
+      if (rd->pattern)
+        remove_job_from_pattern (rd, job_num);
+
+      memmove (job, job + 1, (rd->n_jobs - job_num - 1) * sizeof (*job));
+
+      --rd->n_jobs;
+
+      CLEAR (rd->jobs[rd->n_jobs]);
+    } else {
+      ++job_num;
+    }
+  }
+
+  rd->services &= ~services;
+
+  return rd->services;
+}
+
+static vbi_bool
+add_job_to_pattern (vbi3_raw_decoder * rd,
+    int job_num, unsigned int *start, unsigned int *count)
+{
+  int8_t *pattern_end;
+  unsigned int scan_lines;
+  unsigned int field;
+
+  job_num += 1;                 /* index into rd->jobs, 0 means no job */
+
+  scan_lines = rd->sampling.count[0]
+      + rd->sampling.count[1];
+
+  pattern_end = rd->pattern + scan_lines * _VBI3_RAW_DECODER_MAX_WAYS;
+
+  for (field = 0; field < 2; ++field) {
+    int8_t *pattern;
+    unsigned int i;
+
+    pattern = rd->pattern + start[field] * _VBI3_RAW_DECODER_MAX_WAYS;
+
+    /* For each line where we may find the data. */
+    for (i = 0; i < count[field]; ++i) {
+      unsigned int free;
+      int8_t *dst;
+      int8_t *src;
+      int8_t *end;
+
+      assert (pattern < pattern_end);
+
+      dst = pattern;
+      end = pattern + _VBI3_RAW_DECODER_MAX_WAYS;
+
+      free = 0;
+
+      for (src = dst; src < end; ++src) {
+        int8_t num = *src;
+
+        if (num <= 0) {
+          ++free;
+          continue;
+        } else {
+          free += (num == job_num);
+          *dst++ = num;
+        }
+      }
+
+      while (dst < end)
+        *dst++ = 0;
+
+      if (free <= 1)            /* reserve a NULL way */
+        return FALSE;
+
+      pattern = end;
+    }
+  }
+
+  for (field = 0; field < 2; ++field) {
+    int8_t *pattern;
+    unsigned int i;
+
+    pattern = rd->pattern + start[field] * _VBI3_RAW_DECODER_MAX_WAYS;
+
+    /* For each line where we may find the data. */
+    for (i = 0; i < count[field]; ++i) {
+      unsigned int way;
+
+      for (way = 0; pattern[way] > 0; ++way)
+        if (pattern[way] == job_num)
+          break;
+
+      pattern[way] = job_num;
+      pattern[_VBI3_RAW_DECODER_MAX_WAYS - 1] = -128;
+
+      pattern += _VBI3_RAW_DECODER_MAX_WAYS;
+    }
+  }
+
+  return TRUE;
+}
+
+static void
+lines_containing_data (unsigned int start[2],
+    unsigned int count[2],
+    const vbi_sampling_par * sp, const _vbi_service_par * par)
+{
+  unsigned int field;
+
+  start[0] = 0;
+  start[1] = sp->count[0];
+
+  count[0] = sp->count[0];
+  count[1] = sp->count[1];
+
+  if (!sp->synchronous) {
+    /* XXX Scanning all lines isn't always necessary. */
+    return;
+  }
+
+  for (field = 0; field < 2; ++field) {
+    unsigned int first;
+    unsigned int last;
+
+    if (0 == par->first[field]
+        || 0 == par->last[field]) {
+      /* No data on this field. */
+      count[field] = 0;
+      continue;
+    }
+
+    first = sp->start[field];
+    last = first + sp->count[field] - 1;
+
+    if (first > 0 && sp->count[field] > 0) {
+      assert (par->first[field] <= par->last[field]);
+
+      if ((unsigned int) par->first[field] > last
+          || (unsigned int) par->last[field] < first)
+        continue;
+
+      first = MAX (first, (unsigned int) par->first[field]);
+      last = MIN ((unsigned int) par->last[field], last);
+
+      start[field] += first - sp->start[field];
+      count[field] = last + 1 - first;
+    }
+  }
+}
+
+/**
+ * $param rd Pointer to vbi3_raw_decoder object allocated with
+ *   vbi3_raw_decoder_new().
+ * $param services Set of data services.
+ * $param strict A value of 0, 1 or 2 requests loose, reliable or strict
+ *  matching of sampling parameters. For example if the data service
+ *  requires knowledge of line numbers, $c 0 will always accept the
+ *  service (which may work if the scan lines are populated in a
+ *  non-confusing way) but $c 1 or $c 2 will not. If the data service
+ *  might use more lines than are sampled, $c 1 will accept but $c 2
+ *  will not. If unsure, set to $c 1.
+ * 
+ * Adds one or more data services to be decoded. Currently the libzvbi
+ * raw vbi decoder can decode up to eight data services in parallel.
+ * 
+ * $return
+ * Set describing the data services $a rd will decode. The function
+ * eliminates services which cannot be decoded with the current
+ * sampling parameters, or when they exceed the decoder capacity.
+ */
+/* Attn: strict must be int for compatibility with libzvbi 0.2 (-1 == 0) */
+vbi_service_set
+vbi3_raw_decoder_add_services (vbi3_raw_decoder * rd,
+    vbi_service_set services, int strict)
+{
+  const _vbi_service_par *par;
+  double min_offset;
+
+  assert (NULL != rd);
+
+  services &= ~(VBI_SLICED_VBI_525 | VBI_SLICED_VBI_625);
+
+  if (rd->services & services) {
+    info (&rd->log,
+        "Already decoding services 0x%08x.", rd->services & services);
+    services &= ~rd->services;
+  }
+
+  if (0 == services) {
+    info (&rd->log, "No services to add.");
+    return rd->services;
+  }
+
+  if (!rd->pattern) {
+    unsigned int scan_lines;
+    unsigned int scan_ways;
+    unsigned int size;
+
+    scan_lines = rd->sampling.count[0] + rd->sampling.count[1];
+    scan_ways = scan_lines * _VBI3_RAW_DECODER_MAX_WAYS;
+
+    size = scan_ways * sizeof (rd->pattern[0]);
+    rd->pattern = (int8_t *) vbi_malloc (size);
+    if (NULL == rd->pattern) {
+      error (&rd->log, "Out of memory.");
+      return rd->services;
+    }
+
+    memset (rd->pattern, 0, scan_ways * sizeof (rd->pattern[0]));
+  }
+
+  if (525 == rd->sampling.scanning) {
+    min_offset = 7.9e-6;
+  } else {
+    min_offset = 8.0e-6;
+  }
+
+  for (par = _vbi_service_table; par->id; ++par) {
+    vbi_sampling_par *sp;
+    _vbi3_raw_decoder_job *job;
+    unsigned int start[2];
+    unsigned int count[2];
+    unsigned int sample_offset;
+    unsigned int samples_per_line;
+    unsigned int cri_end;
+    unsigned int j;
+
+    if (0 == (par->id & services))
+      continue;
+
+    job = rd->jobs;
+
+    /* Some jobs can be merged, otherwise we add a new job. */
+    for (j = 0; j < rd->n_jobs; ++j) {
+      unsigned int id = job->id | par->id;
+
+      /* Level 1.0 and 2.5 */
+      if (0 == (id & ~VBI_SLICED_TELETEXT_B)
+          /* Field 1 and 2 */
+          || 0 == (id & ~VBI_SLICED_CAPTION_525)
+          || 0 == (id & ~VBI_SLICED_CAPTION_625)
+          || 0 == (id & ~(VBI_SLICED_VPS | VBI_SLICED_VPS_F2)))
+        break;
+
+      ++job;
+    }
+
+    if (j >= _VBI3_RAW_DECODER_MAX_JOBS) {
+      error (&rd->log,
+          "Set 0x%08x exceeds number of "
+          "simultaneously decodable "
+          "services (%u).", services, _VBI3_RAW_DECODER_MAX_WAYS);
+      break;
+    } else if (j >= rd->n_jobs) {
+      job->id = 0;
+    }
+
+
+    sp = &rd->sampling;
+
+    if (!_vbi_sampling_par_check_services_log (sp, par->id, strict, &rd->log))
+      continue;
+
+
+    sample_offset = 0;
+
+    /* Skip color burst. */
+    /* Offsets aren't that reliable, sigh. */
+    if (0 && sp->offset > 0 && strict > 0) {
+      double offset;
+
+      offset = sp->offset / (double) sp->sampling_rate;
+      if (offset < min_offset)
+        sample_offset = (int) (min_offset * sp->sampling_rate);
+    }
+
+    if (VBI_SLICED_WSS_625 & par->id) {
+      /* TODO: WSS 625 occupies only first half of line,
+         we can abort earlier. */
+      cri_end = ~0;
+    } else {
+      cri_end = ~0;
+    }
+
+    samples_per_line = sp->bytes_per_line
+        / VBI_PIXFMT_BPP (sp->sp_sample_format);
+
+    if (!_vbi3_bit_slicer_init (&job->slicer)) {
+      assert (!"bit_slicer_init");
+    }
+
+    if (!vbi3_bit_slicer_set_params
+        (&job->slicer,
+            sp->sp_sample_format,
+            sp->sampling_rate,
+            sample_offset,
+            samples_per_line,
+            par->cri_frc >> par->frc_bits,
+            par->cri_frc_mask >> par->frc_bits,
+            par->cri_bits,
+            par->cri_rate,
+            cri_end,
+            (par->cri_frc & ((1U << par->frc_bits) - 1)),
+            par->frc_bits, par->payload, par->bit_rate, par->modulation)) {
+      assert (!"bit_slicer_set_params");
+    }
+
+    vbi3_bit_slicer_set_log_fn (&job->slicer,
+        rd->log.mask, rd->log.fn, rd->log.user_data);
+
+    lines_containing_data (start, count, sp, par);
+
+    if (!add_job_to_pattern (rd, job - rd->jobs, start, count)) {
+      error (&rd->log,
+          "Out of decoder pattern space for "
+          "service 0x%08x (%s).", par->id, par->label);
+      continue;
+    }
+
+    job->id |= par->id;
+
+    if (job >= rd->jobs + rd->n_jobs)
+      ++rd->n_jobs;
+
+    rd->services |= par->id;
+  }
+
+  return rd->services;
+}
+
+vbi_bool
+vbi3_raw_decoder_sampling_point (vbi3_raw_decoder * rd,
+    vbi3_bit_slicer_point * point, unsigned int row, unsigned int nth_bit)
+{
+  assert (NULL != rd);
+  assert (NULL != point);
+
+  if (row >= rd->n_sp_lines)
+    return FALSE;
+
+  if (nth_bit >= rd->sp_lines[row].n_points)
+    return FALSE;
+
+  *point = rd->sp_lines[row].points[nth_bit];
+
+  return TRUE;
+}
+
+vbi_bool
+vbi3_raw_decoder_debug (vbi3_raw_decoder * rd, vbi_bool enable)
+{
+#if 0                           /* Set but unused */
+  _vbi3_raw_decoder_sp_line *sp_lines;
+#endif
+  unsigned int n_lines;
+  vbi_bool r;
+
+  assert (NULL != rd);
+
+#if 0                           /* Set but unused */
+  sp_lines = NULL;
+#endif
+  r = TRUE;
+
+  rd->debug = ! !enable;
+
+  n_lines = 0;
+  if (enable) {
+    n_lines = rd->sampling.count[0] + rd->sampling.count[1];
+  }
+
+  switch (rd->sampling.sp_sample_format) {
+    case VBI_PIXFMT_YUV420:
+      break;
+
+    default:
+      /* Not implemented. */
+      n_lines = 0;
+      r = FALSE;
+      break;
+  }
+
+  if (rd->n_sp_lines == n_lines)
+    return r;
+
+  vbi_free (rd->sp_lines);
+  rd->sp_lines = NULL;
+  rd->n_sp_lines = 0;
+
+  if (n_lines > 0) {
+    rd->sp_lines = calloc (n_lines, sizeof (*rd->sp_lines));
+    if (NULL == rd->sp_lines)
+      return FALSE;
+
+    rd->n_sp_lines = n_lines;
+  }
+
+  return r;
+}
+
+vbi_service_set
+vbi3_raw_decoder_services (vbi3_raw_decoder * rd)
+{
+  assert (NULL != rd);
+
+  return rd->services;
+}
+
+/**
+ * $param rd Pointer to a vbi3_raw_decoder object allocated with
+ *   vbi3_raw_decoder_new().
+ * $param sp New sampling parameters.
+ * $param strict See vbi3_raw_decoder_add_services().
+ *
+ * Changes the sampling parameters used by $a rd. This will
+ * remove all services which have been added with
+ * vbi3_raw_decoder_add_services() but cannot be decoded with
+ * the new sampling parameters.
+ *
+ * $return
+ * Set of data services $rd will be decode after the change.
+ * Can be zero if the sampling parameters are invalid or some
+ * other error occured.
+ */
+/* Attn: strict must be int for compatibility with libzvbi 0.2 (-1 == 0) */
+vbi_service_set
+    vbi3_raw_decoder_set_sampling_par
+    (vbi3_raw_decoder * rd, const vbi_sampling_par * sp, int strict) {
+  unsigned int services;
+
+  assert (NULL != rd);
+  assert (NULL != sp);
+
+  services = rd->services;
+
+  vbi3_raw_decoder_reset (rd);
+
+  if (!_vbi_sampling_par_valid_log (sp, &rd->log)) {
+    CLEAR (rd->sampling);
+    return 0;
+  }
+
+  rd->sampling = *sp;
+
+  /* Error ignored. */
+  vbi3_raw_decoder_debug (rd, rd->debug);
+
+  return vbi3_raw_decoder_add_services (rd, services, strict);
+}
+
+/**
+ * $param rd Pointer to a vbi3_raw_decoder object allocated with
+ *   vbi3_raw_decoder_new().
+ * $param sp Sampling parameters will be stored here.
+ *
+ * Returns sampling parameters used by $a rd.
+ */
+void vbi3_raw_decoder_get_sampling_par
+    (const vbi3_raw_decoder * rd, vbi_sampling_par * sp)
+{
+  assert (NULL != rd);
+  assert (NULL != sp);
+
+  *sp = rd->sampling;
+}
+
+void
+vbi3_raw_decoder_set_log_fn (vbi3_raw_decoder * rd,
+    vbi_log_fn * log_fn, void *user_data, vbi_log_mask mask)
+{
+  unsigned int i;
+
+  assert (NULL != rd);
+
+  if (NULL == log_fn)
+    mask = 0;
+
+  rd->log.mask = mask;
+  rd->log.fn = log_fn;
+  rd->log.user_data = user_data;
+
+  for (i = 0; i < _VBI3_RAW_DECODER_MAX_JOBS; ++i) {
+    vbi3_bit_slicer_set_log_fn (&rd->jobs[i].slicer, mask, log_fn, user_data);
+  }
+}
+
+/**
+ * @internal
+ *
+ * Free all resources associated with @a rd.
+ */
+void
+_vbi3_raw_decoder_destroy (vbi3_raw_decoder * rd)
+{
+  vbi3_raw_decoder_reset (rd);
+
+  vbi3_raw_decoder_debug (rd, FALSE);
+
+  /* Make unusable. */
+  CLEAR (*rd);
+}
+
+/**
+ * @internal
+ * 
+ * See vbi3_raw_decoder_new().
+ */
+vbi_bool
+_vbi3_raw_decoder_init (vbi3_raw_decoder * rd, const vbi_sampling_par * sp)
+{
+  CLEAR (*rd);
+
+  vbi3_raw_decoder_reset (rd);
+
+  if (NULL != sp) {
+    if (!_vbi_sampling_par_valid_log (sp, &rd->log))
+      return FALSE;
+
+    rd->sampling = *sp;
+  }
+
+  return TRUE;
+}
+
+/**
+ * $param rd Pointer to a vbi3_raw_decoder object allocated with
+ *   vbi3_raw_decoder_new(), can be NULL
+ *
+ * Deletes a vbi3_raw_decoder object.
+ */
+void
+vbi3_raw_decoder_delete (vbi3_raw_decoder * rd)
+{
+  if (NULL == rd)
+    return;
+
+  _vbi3_raw_decoder_destroy (rd);
+
+  vbi_free (rd);
+}
+
+/**
+ * $param sp VBI sampling parameters describing the raw VBI image
+ *   to decode, can be $c NULL. If they are negotiatable you can determine
+ *   suitable parameters with vbi_sampling_par_from_services(). You can
+ *   change the sampling parameters later with
+ *   vbi3_raw_decoder_set_sampling_par().
+ *
+ * Allocates a vbi3_raw_decoder object. To actually decode data
+ * services you must request the data with vbi3_raw_decoder_add_services().
+ *
+ * $returns
+ * NULL when out of memory or the sampling parameters are invalid,
+ * Otherwise a pointer to an opaque vbi3_raw_decoder object which must
+ * be deleted with vbi3_raw_decoder_delete() when done.
+ */
+vbi3_raw_decoder *
+vbi3_raw_decoder_new (const vbi_sampling_par * sp)
+{
+  vbi3_raw_decoder *rd;
+
+  rd = vbi_malloc (sizeof (*rd));
+  if (NULL == rd) {
+    errno = ENOMEM;
+    return NULL;
+  }
+
+  if (!_vbi3_raw_decoder_init (rd, sp)) {
+    vbi_free (rd);
+    rd = NULL;
+  }
+
+  return rd;
+}
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/ext/closedcaption/raw_decoder.h b/ext/closedcaption/raw_decoder.h
new file mode 100644 (file)
index 0000000..8f7382c
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ *  libzvbi -- Raw VBI decoder
+ *
+ *  Copyright (C) 2000-2004 Michael H. Schimek
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the 
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA  02110-1301  USA.
+ */
+
+/* $Id: raw_decoder.h,v 1.12 2008-02-19 00:35:21 mschimek Exp $ */
+
+#ifndef __ZVBI_RAW_DECODER_H__
+#define __ZVBI_RAW_DECODER_H__
+
+#include <stdio.h>
+
+#include "decoder.h"
+#include "sampling_par.h"
+#include "bit_slicer.h"
+
+VBI_BEGIN_DECLS
+
+/*
+ * $ingroup RawDecoder
+ * $brief Raw VBI decoder.
+ *
+ * The contents of this structure are private.
+ * Call vbi3_raw_decoder_new() to allocate a raw VBI decoder.
+ */
+typedef struct _vbi3_raw_decoder vbi3_raw_decoder;
+
+/*
+ * $addtogroup RawDecoder
+ * ${
+ */
+extern vbi_bool
+vbi3_raw_decoder_sampling_point        (vbi3_raw_decoder *     rd,
+                                vbi3_bit_slicer_point *point,
+                                unsigned int           row,
+                                unsigned int           nth_bit);
+extern unsigned int
+vbi3_raw_decoder_decode                (vbi3_raw_decoder *     rd,
+                                vbi_sliced *           sliced,
+                                unsigned int           sliced_lines,
+                                const uint8_t *        raw);
+extern void
+vbi3_raw_decoder_reset         (vbi3_raw_decoder *     rd);
+extern vbi_service_set
+vbi3_raw_decoder_services      (vbi3_raw_decoder *     rd);
+extern vbi_service_set
+vbi3_raw_decoder_remove_services
+                               (vbi3_raw_decoder *     rd,
+                                vbi_service_set        services);
+extern vbi_service_set
+vbi3_raw_decoder_add_services  (vbi3_raw_decoder *     rd,
+                                vbi_service_set        services,
+                                int                    strict);
+extern vbi_bool
+vbi3_raw_decoder_debug         (vbi3_raw_decoder *     rd,
+                                vbi_bool               enable);
+extern vbi_service_set
+vbi3_raw_decoder_set_sampling_par
+                               (vbi3_raw_decoder *     rd,
+                                const vbi_sampling_par *sp,
+                                int                    strict);
+extern void
+vbi3_raw_decoder_get_sampling_par
+                               (const vbi3_raw_decoder *rd,
+                                vbi_sampling_par *     sp);
+extern void
+vbi3_raw_decoder_set_log_fn    (vbi3_raw_decoder *     rd,
+                                vbi_log_fn *           log_fn,
+                                void *                 user_data,
+                                vbi_log_mask           mask);
+extern void
+vbi3_raw_decoder_delete                (vbi3_raw_decoder *     rd);
+extern vbi3_raw_decoder *
+vbi3_raw_decoder_new           (const vbi_sampling_par *sp);
+
+/* $} */
+
+/* Private */
+
+/** @internal */
+#define _VBI3_RAW_DECODER_MAX_JOBS 8
+/** @internal */
+#define _VBI3_RAW_DECODER_MAX_WAYS 8
+
+/** @internal */
+typedef struct {
+       vbi_service_set         id;
+       vbi3_bit_slicer         slicer;
+} _vbi3_raw_decoder_job;
+
+/** @internal */
+typedef struct {
+       vbi3_bit_slicer_point   points[512];
+       unsigned int            n_points;
+} _vbi3_raw_decoder_sp_line;
+
+/**
+ * @internal
+ * Don't dereference pointers to this structure.
+ * I guarantee it will change.
+ */
+struct _vbi3_raw_decoder {
+       vbi_sampling_par        sampling;
+
+       vbi_service_set         services;
+
+       _vbi_log_hook           log;
+       vbi_bool                debug;
+
+       unsigned int            n_jobs;
+       unsigned int            n_sp_lines;
+       int                     readjust;
+       int8_t *                pattern;        /* n scan lines * MAX_WAYS */
+       _vbi3_raw_decoder_job   jobs[_VBI3_RAW_DECODER_MAX_JOBS];
+       _vbi3_raw_decoder_sp_line *sp_lines;
+};
+
+/** @internal */
+typedef enum {
+       /** Requires field line numbers. */
+       _VBI_SP_LINE_NUM        = (1 << 0),
+       /** Requires field numbers. */
+       _VBI_SP_FIELD_NUM       = (1 << 1),
+} _vbi_service_par_flag;
+
+typedef struct _vbi_service_par _vbi_service_par;
+
+/** @internal */
+struct _vbi_service_par {
+       vbi_service_set         id;
+       const char *            label;
+
+       /**
+        * Video standard
+        * - 525 lines, FV = 59.94 Hz, FH = 15734 Hz
+        * - 625 lines, FV = 50 Hz, FH = 15625 Hz
+        */
+       vbi_videostd_set        videostd_set;
+
+       /**
+        * Most scan lines used by the data service, first and last
+        * line of first and second field. ITU-R numbering scheme.
+        * Zero if no data from this field, requires field sync.
+        */
+       unsigned int            first[2];
+        unsigned int           last[2];
+
+       /**
+        * Leading edge hsync to leading edge first CRI one bit,
+        * half amplitude points, in nanoseconds.
+        */
+       unsigned int            offset;
+
+       unsigned int            cri_rate;       /**< Hz */
+       unsigned int            bit_rate;       /**< Hz */
+
+       /** Clock Run In and FRaming Code, LSB last txed bit of FRC. */
+       unsigned int            cri_frc;
+
+       /** CRI and FRC bits significant for identification. */
+       unsigned int            cri_frc_mask;
+
+       /**
+        * Number of significat cri_bits (at cri_rate),
+        * frc_bits (at bit_rate).
+        */
+       unsigned int            cri_bits;
+       unsigned int            frc_bits;
+
+       unsigned int            payload;        /**< bits */
+       vbi_modulation          modulation;
+
+       _vbi_service_par_flag   flags;
+};
+
+extern const _vbi_service_par _vbi_service_table [];
+
+extern void
+_vbi3_raw_decoder_dump         (const vbi3_raw_decoder *rd,
+                                FILE *                 fp);
+extern void
+_vbi3_raw_decoder_destroy      (vbi3_raw_decoder *     rd);
+extern vbi_bool
+_vbi3_raw_decoder_init         (vbi3_raw_decoder *     rd,
+                                const vbi_sampling_par *sp);
+
+VBI_END_DECLS
+
+#endif /* __ZVBI_RAW_DECODER_H__ */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/ext/closedcaption/sampling_par.c b/ext/closedcaption/sampling_par.c
new file mode 100644 (file)
index 0000000..b08c63a
--- /dev/null
@@ -0,0 +1,551 @@
+/*
+ *  libzvbi -- Raw VBI sampling parameters
+ *
+ *  Copyright (C) 2000-2004 Michael H. Schimek
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the 
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA  02110-1301  USA.
+ */
+
+/* $Id: sampling_par.c,v 1.12 2013-08-28 14:45:00 mschimek Exp $ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <errno.h>
+
+#include "misc.h"
+#include "raw_decoder.h"
+#include "sampling_par.h"
+#include "sliced.h"
+
+#  define vbi_pixfmt_bytes_per_pixel VBI_PIXFMT_BPP
+#  define sp_sample_format sampling_format
+
+/**
+ * @addtogroup Sampling Raw VBI sampling
+ * @ingroup Raw
+ * @brief Raw VBI data sampling interface.
+ */
+
+/**
+ * @internal
+ * Compatibility.
+ */
+vbi_videostd_set
+_vbi_videostd_set_from_scanning (int scanning)
+{
+  switch (scanning) {
+    case 525:
+      return VBI_VIDEOSTD_SET_525_60;
+
+    case 625:
+      return VBI_VIDEOSTD_SET_625_50;
+
+    default:
+      break;
+  }
+
+  return 0;
+}
+
+_vbi_inline vbi_bool
+range_check (unsigned int start,
+    unsigned int count, unsigned int min, unsigned int max)
+{
+  /* Check bounds and overflow. */
+  return (start >= min && (start + count) <= max && (start + count) >= start);
+}
+
+/**
+ * @internal
+ * @param sp Sampling parameters to verify.
+ * 
+ * @return
+ * TRUE if the sampling parameters are valid (as far as we can tell).
+ */
+vbi_bool
+_vbi_sampling_par_valid_log (const vbi_sampling_par * sp, _vbi_log_hook * log)
+{
+  vbi_videostd_set videostd_set;
+  unsigned int bpp;
+
+  assert (NULL != sp);
+
+  switch (sp->sp_sample_format) {
+    case VBI_PIXFMT_YUV420:
+      /* This conflicts with the ivtv driver, which returns an
+         odd number of bytes per line.  The driver format is
+         _GREY but libzvbi 0.2 has no VBI_PIXFMT_Y8. */
+      break;
+
+    default:
+      bpp = vbi_pixfmt_bytes_per_pixel (sp->sp_sample_format);
+      if (0 != (sp->bytes_per_line % bpp))
+        goto bad_samples;
+      break;
+  }
+
+  if (0 == sp->bytes_per_line)
+    goto no_samples;
+
+  if (0 == sp->count[0]
+      && 0 == sp->count[1])
+    goto bad_range;
+
+  videostd_set = _vbi_videostd_set_from_scanning (sp->scanning);
+
+  if (VBI_VIDEOSTD_SET_525_60 & videostd_set) {
+    if (VBI_VIDEOSTD_SET_625_50 & videostd_set)
+      goto ambiguous;
+
+    if (0 != sp->start[0]
+        && !range_check (sp->start[0], sp->count[0], 1, 262))
+      goto bad_range;
+
+    if (0 != sp->start[1]
+        && !range_check (sp->start[1], sp->count[1], 263, 525))
+      goto bad_range;
+  } else if (VBI_VIDEOSTD_SET_625_50 & videostd_set) {
+    if (0 != sp->start[0]
+        && !range_check (sp->start[0], sp->count[0], 1, 311))
+      goto bad_range;
+
+    if (0 != sp->start[1]
+        && !range_check (sp->start[1], sp->count[1], 312, 625))
+      goto bad_range;
+  } else {
+  ambiguous:
+    info (log, "Ambiguous videostd_set 0x%lx.", (unsigned long) videostd_set);
+    return FALSE;
+  }
+
+  if (sp->interlaced && (sp->count[0] != sp->count[1]
+          || 0 == sp->count[0])) {
+    info (log,
+        "Line counts %u, %u must be equal and "
+        "non-zero when raw VBI data is interlaced.",
+        sp->count[0], sp->count[1]);
+    return FALSE;
+  }
+
+  return TRUE;
+
+no_samples:
+  info (log, "samples_per_line is zero.");
+  return FALSE;
+
+
+bad_samples:
+  info (log,
+      "bytes_per_line value %u is no multiple of "
+      "the sample size %u.",
+      sp->bytes_per_line, vbi_pixfmt_bytes_per_pixel (sp->sp_sample_format));
+  return FALSE;
+
+bad_range:
+  info (log,
+      "Invalid VBI scan range %u-%u (%u lines), "
+      "%u-%u (%u lines).",
+      sp->start[0], sp->start[0] + sp->count[0] - 1,
+      sp->count[0],
+      sp->start[1], sp->start[1] + sp->count[1] - 1, sp->count[1]);
+  return FALSE;
+}
+
+static vbi_bool
+    _vbi_sampling_par_permit_service
+    (const vbi_sampling_par * sp,
+    const _vbi_service_par * par, unsigned int strict, _vbi_log_hook * log)
+{
+  const unsigned int unknown = 0;
+  double signal;
+  unsigned int field;
+  unsigned int samples_per_line;
+  vbi_videostd_set videostd_set;
+
+  assert (NULL != sp);
+  assert (NULL != par);
+
+  videostd_set = _vbi_videostd_set_from_scanning (sp->scanning);
+  if (0 == (par->videostd_set & videostd_set)) {
+    info (log,
+        "Service 0x%08x (%s) requires "
+        "videostd_set 0x%lx, "
+        "have 0x%lx.",
+        par->id, par->label,
+        (unsigned long) par->videostd_set, (unsigned long) videostd_set);
+    return FALSE;
+  }
+
+  if (par->flags & _VBI_SP_LINE_NUM) {
+    if ((par->first[0] > 0 && unknown == (unsigned int) sp->start[0])
+        || (par->first[1] > 0 && unknown == (unsigned int) sp->start[1])) {
+      info (log,
+          "Service 0x%08x (%s) requires known "
+          "line numbers.", par->id, par->label);
+      return FALSE;
+    }
+  }
+
+  {
+    unsigned int rate;
+
+    rate = MAX (par->cri_rate, par->bit_rate);
+
+    switch (par->id) {
+      case VBI_SLICED_WSS_625:
+        /* Effective bit rate is just 1/3 max_rate,
+           so 1 * max_rate should suffice. */
+        break;
+
+      default:
+        rate = (rate * 3) >> 1;
+        break;
+    }
+
+    if (rate > (unsigned int) sp->sampling_rate) {
+      info (log,
+          "Sampling rate %f MHz too low "
+          "for service 0x%08x (%s).",
+          sp->sampling_rate / 1e6, par->id, par->label);
+      return FALSE;
+    }
+  }
+
+  signal = par->cri_bits / (double) par->cri_rate
+      + (par->frc_bits + par->payload) / (double) par->bit_rate;
+
+  samples_per_line = sp->bytes_per_line / VBI_PIXFMT_BPP (sp->sampling_format);
+
+  if (0 && sp->offset > 0 && strict > 0) {
+    double sampling_rate;
+    double offset;
+    double end;
+
+    sampling_rate = (double) sp->sampling_rate;
+
+    offset = sp->offset / sampling_rate;
+    end = (sp->offset + samples_per_line) / sampling_rate;
+
+    if (offset > (par->offset / 1e3 - 0.5e-6)) {
+      info (log,
+          "Sampling starts at 0H + %f us, too "
+          "late for service 0x%08x (%s) at "
+          "%f us.", offset * 1e6, par->id, par->label, par->offset / 1e3);
+      return FALSE;
+    }
+
+    if (end < (par->offset / 1e9 + signal + 0.5e-6)) {
+      info (log,
+          "Sampling ends too early at 0H + "
+          "%f us for service 0x%08x (%s) "
+          "which ends at %f us",
+          end * 1e6,
+          par->id, par->label, par->offset / 1e3 + signal * 1e6 + 0.5);
+      return FALSE;
+    }
+  } else {
+    double samples;
+
+    samples = samples_per_line / (double) sp->sampling_rate;
+
+    if (strict > 0)
+      samples -= 1e-6;          /* headroom */
+
+    if (samples < signal) {
+      info (log,
+          "Service 0x%08x (%s) signal length "
+          "%f us exceeds %f us sampling length.",
+          par->id, par->label, signal * 1e6, samples * 1e6);
+      return FALSE;
+    }
+  }
+
+  if ((par->flags & _VBI_SP_FIELD_NUM)
+      && !sp->synchronous) {
+    info (log,
+        "Service 0x%08x (%s) requires "
+        "synchronous field order.", par->id, par->label);
+    return FALSE;
+  }
+
+  for (field = 0; field < 2; ++field) {
+    unsigned int start;
+    unsigned int end;
+
+    start = sp->start[field];
+    end = start + sp->count[field] - 1;
+
+    if (0 == par->first[field]
+        || 0 == par->last[field]) {
+      /* No data on this field. */
+      continue;
+    }
+
+    if (0 == sp->count[field]) {
+      info (log,
+          "Service 0x%08x (%s) requires "
+          "data from field %u", par->id, par->label, field + 1);
+      return FALSE;
+    }
+
+    /* (int) <= 0 for compatibility with libzvbi 0.2.x */
+    if ((int) strict <= 0 || 0 == sp->start[field])
+      continue;
+
+    if (1 == strict && par->first[field] > par->last[field]) {
+      /* May succeed if not all scanning lines
+         available for the service are actually used. */
+      continue;
+    }
+
+    if (start > par->first[field]
+        || end < par->last[field]) {
+      info (log,
+          "Service 0x%08x (%s) requires "
+          "lines %u-%u, have %u-%u.",
+          par->id, par->label, par->first[field], par->last[field], start, end);
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+/**
+ * @internal
+ */
+vbi_service_set
+    _vbi_sampling_par_check_services_log
+    (const vbi_sampling_par * sp,
+    vbi_service_set services, unsigned int strict, _vbi_log_hook * log) {
+  const _vbi_service_par *par;
+  vbi_service_set rservices;
+
+  assert (NULL != sp);
+
+  rservices = 0;
+
+  for (par = _vbi_service_table; par->id; ++par) {
+    if (0 == (par->id & services))
+      continue;
+
+    if (_vbi_sampling_par_permit_service (sp, par, strict, log))
+      rservices |= par->id;
+  }
+
+  return rservices;
+}
+
+/**
+ * @internal
+ */
+vbi_service_set
+    _vbi_sampling_par_from_services_log
+    (vbi_sampling_par * sp,
+    unsigned int *max_rate,
+    vbi_videostd_set videostd_set_req,
+    vbi_service_set services, _vbi_log_hook * log) {
+  const _vbi_service_par *par;
+  vbi_service_set rservices;
+  vbi_videostd_set videostd_set;
+  unsigned int rate;
+  unsigned int samples_per_line;
+
+  assert (NULL != sp);
+
+  videostd_set = 0;
+
+  if (0 != videostd_set_req) {
+    if (0 == (VBI_VIDEOSTD_SET_ALL & videostd_set_req)
+        || ((VBI_VIDEOSTD_SET_525_60 & videostd_set_req)
+            && (VBI_VIDEOSTD_SET_625_50 & videostd_set_req))) {
+      warning (log,
+          "Ambiguous videostd_set 0x%lx.", (unsigned long) videostd_set_req);
+      CLEAR (*sp);
+      return 0;
+    }
+
+    videostd_set = videostd_set_req;
+  }
+
+  samples_per_line = 0;
+  sp->sampling_rate = 27000000; /* ITU-R BT.601 */
+  sp->offset = (int) (64e-6 * sp->sampling_rate);
+  sp->start[0] = 30000;
+  sp->count[0] = 0;
+  sp->start[1] = 30000;
+  sp->count[1] = 0;
+  sp->interlaced = FALSE;
+  sp->synchronous = TRUE;
+
+  rservices = 0;
+  rate = 0;
+
+  for (par = _vbi_service_table; par->id; ++par) {
+#if 0                           /* Set but unused */
+    double margin;
+#endif
+    double signal;
+    int offset;
+    unsigned int samples;
+    unsigned int i;
+
+    if (0 == (par->id & services))
+      continue;
+
+    if (0 == videostd_set_req) {
+      vbi_videostd_set set;
+
+      set = par->videostd_set | videostd_set;
+
+      if (0 == (set & ~VBI_VIDEOSTD_SET_525_60)
+          || 0 == (set & ~VBI_VIDEOSTD_SET_625_50))
+        videostd_set |= par->videostd_set;
+    }
+#if 0                           /* Set but unused */
+    if (VBI_VIDEOSTD_SET_525_60 & videostd_set)
+      margin = 1.0e-6;
+    else
+      margin = 2.0e-6;
+#endif
+
+    if (0 == (par->videostd_set & videostd_set)) {
+      info (log,
+          "Service 0x%08x (%s) requires "
+          "videostd_set 0x%lx, "
+          "have 0x%lx.",
+          par->id, par->label,
+          (unsigned long) par->videostd_set, (unsigned long) videostd_set);
+      continue;
+    }
+
+    rate = MAX (rate, par->cri_rate);
+    rate = MAX (rate, par->bit_rate);
+
+    signal = par->cri_bits / (double) par->cri_rate
+        + ((par->frc_bits + par->payload) / (double) par->bit_rate);
+
+    offset = (int) ((par->offset / 1e9) * sp->sampling_rate);
+    samples = (int) ((signal + 1.0e-6) * sp->sampling_rate);
+
+    sp->offset = MIN (sp->offset, offset);
+
+    samples_per_line = MAX (samples_per_line + sp->offset,
+        samples + offset) - sp->offset;
+
+    for (i = 0; i < 2; ++i)
+      if (par->first[i] > 0 && par->last[i] > 0) {
+        sp->start[i] = MIN
+            ((unsigned int) sp->start[i], (unsigned int) par->first[i]);
+        sp->count[i] = MAX ((unsigned int) sp->start[i]
+            + sp->count[i], (unsigned int) par->last[i] + 1)
+            - sp->start[i];
+      }
+
+    rservices |= par->id;
+  }
+
+  if (0 == rservices) {
+    CLEAR (*sp);
+    return 0;
+  }
+
+  if (0 == sp->count[1]) {
+    sp->start[1] = 0;
+
+    if (0 == sp->count[0]) {
+      sp->start[0] = 0;
+      sp->offset = 0;
+    }
+  } else if (0 == sp->count[0]) {
+    sp->start[0] = 0;
+  }
+
+  sp->scanning = (videostd_set & VBI_VIDEOSTD_SET_525_60)
+      ? 525 : 625;
+  sp->sp_sample_format = VBI_PIXFMT_YUV420;
+
+  /* Note bpp is 1. */
+  sp->bytes_per_line = MAX (1440U, samples_per_line);
+
+  if (max_rate)
+    *max_rate = rate;
+
+  return rservices;
+}
+
+/**
+ * @param sp Sampling parameters to check against.
+ * @param services Set of data services.
+ * @param strict See description of vbi_raw_decoder_add_services().
+ *
+ * Check which of the given services can be decoded with the given
+ * sampling parameters at the given strictness level.
+ *
+ * @return
+ * Subset of @a services decodable with the given sampling parameters.
+ */
+vbi_service_set
+    vbi_sampling_par_check_services
+    (const vbi_sampling_par * sp,
+    vbi_service_set services, unsigned int strict) {
+  return _vbi_sampling_par_check_services_log (sp, services, strict,
+      /* log_hook */ NULL);
+}
+
+/**
+ * @param sp Sampling parameters calculated by this function
+ *   will be stored here.
+ * @param max_rate If not NULL, the highest data bit rate in Hz of
+ *   all services requested will be stored here. The sampling rate
+ *   should be at least twice as high; @sp sampling_rate will
+ *   be set to a more reasonable value of 27 MHz, which is twice
+ *   the video sampling rate defined by ITU-R Rec. BT.601.
+ * @param videostd_set Create sampling parameters matching these
+ *   video standards. When 0 determine video standard from requested
+ *   services.
+ * @param services Set of VBI_SLICED_ symbols. Here (and only here) you
+ *   can add @c VBI_SLICED_VBI_625 or @c VBI_SLICED_VBI_525 to include all
+ *   vbi scan lines in the calculated sampling parameters.
+ *
+ * Calculate the sampling parameters required to receive and decode the
+ * requested data @a services. The @a sp sampling_format will be
+ * @c VBI_PIXFMT_Y8, offset and bytes_per_line will be set to
+ * reasonable minimums. This function can be used to initialize hardware
+ * prior to creating a vbi_raw_decoder object.
+ * 
+ * @return
+ * Subset of @a services covered by the calculated sampling parameters.
+ */
+vbi_service_set
+vbi_sampling_par_from_services (vbi_sampling_par * sp,
+    unsigned int *max_rate,
+    vbi_videostd_set videostd_set, vbi_service_set services)
+{
+  return _vbi_sampling_par_from_services_log (sp, max_rate,
+      videostd_set, services,
+      /* log_hook */ NULL);
+}
+
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/ext/closedcaption/sampling_par.h b/ext/closedcaption/sampling_par.h
new file mode 100644 (file)
index 0000000..50c01b8
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *  libzvbi -- Raw VBI sampling parameters
+ *
+ *  Copyright (C) 2000-2004 Michael H. Schimek
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the 
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA  02110-1301  USA.
+ */
+
+/* $Id: sampling_par.h,v 1.9 2008-02-24 14:17:06 mschimek Exp $ */
+
+#ifndef __SAMPLING_PAR_H__
+#define __SAMPLING_PAR_H__
+
+#include "decoder.h"
+
+VBI_BEGIN_DECLS
+
+/* Public */
+
+typedef vbi_raw_decoder vbi_sampling_par;
+
+#define VBI_VIDEOSTD_SET_EMPTY 0
+#define VBI_VIDEOSTD_SET_PAL_BG 1
+#define VBI_VIDEOSTD_SET_625_50 1
+#define VBI_VIDEOSTD_SET_525_60 2
+#define VBI_VIDEOSTD_SET_ALL 3
+typedef uint64_t vbi_videostd_set;
+
+/* Private */
+
+extern vbi_service_set
+vbi_sampling_par_from_services (vbi_sampling_par *    sp,
+                               unsigned int *         max_rate,
+                               vbi_videostd_set      videostd_set,
+                               vbi_service_set       services);
+extern vbi_service_set
+vbi_sampling_par_check_services
+                                (const vbi_sampling_par *sp,
+                                 vbi_service_set       services,
+                                 unsigned int           strict)
+  _vbi_pure;
+
+extern vbi_videostd_set
+_vbi_videostd_set_from_scanning        (int                    scanning);
+
+extern vbi_service_set
+_vbi_sampling_par_from_services_log
+                                (vbi_sampling_par *    sp,
+                                 unsigned int *         max_rate,
+                                 vbi_videostd_set      videostd_set,
+                                 vbi_service_set       services,
+                                 _vbi_log_hook *       log);
+extern vbi_service_set
+_vbi_sampling_par_check_services_log
+                                (const vbi_sampling_par *sp,
+                                 vbi_service_set       services,
+                                 unsigned int           strict,
+                                 _vbi_log_hook *       log)
+  _vbi_pure;
+extern vbi_bool
+_vbi_sampling_par_valid_log    (const vbi_sampling_par *sp,
+                                 _vbi_log_hook *       log)
+  _vbi_pure;
+
+VBI_END_DECLS
+
+#endif /* __SAMPLING_PAR_H__ */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/
diff --git a/ext/closedcaption/sliced.h b/ext/closedcaption/sliced.h
new file mode 100644 (file)
index 0000000..1b209d4
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ *  libzvbi -- Sliced VBI data
+ *
+ *  Copyright (C) 2000, 2001 Michael H. Schimek
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the 
+ *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ *  Boston, MA  02110-1301  USA.
+ */
+
+/* $Id: sliced.h,v 1.11 2008-02-24 14:17:02 mschimek Exp $ */
+
+#ifndef SLICED_H
+#define SLICED_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Public */
+
+#include <inttypes.h>
+
+/**
+ * @addtogroup Sliced Sliced VBI data
+ * @ingroup Raw
+ * @brief Definition of sliced VBI data.
+ *
+ * The output of the libzvbi raw VBI decoder, and input to the data
+ * service decoder, is VBI data in binary format as defined in this
+ * section. It is similar to the output of hardware VBI decoders
+ * and VBI data transmitted in digital TV streams.
+ */
+
+/**
+ * @name Data service symbols
+ * @ingroup Sliced
+ * @{
+ */
+
+/**
+ * @anchor VBI_SLICED_
+ * No data service, blank vbi_sliced structure.
+ */
+#define VBI_SLICED_NONE                        0
+
+/**
+ * Unknown data service (vbi_dvb_demux).
+ * @since 0.2.10
+ */
+#define VBI_SLICED_UNKNOWN              0
+
+/**
+ * Antiope a.k.a. Teletext System A
+ *
+ * Reference: <a href="http://www.itu.ch">ITU-R BT.653
+ * "Teletext Systems"</a>
+ *
+ * vbi_sliced payload: Last 37 bytes, without clock run-in and
+ * framing code, lsb first transmitted.
+ *
+ * @since 0.2.10
+ */
+#define VBI_SLICED_ANTIOPE              0x00002000
+/**
+ * Synonym of VBI_SLICED_ANTIOPE.
+ * @since 0.2.10
+ */
+#define VBI_SLICED_TELETEXT_A           0x00002000
+
+#define VBI_SLICED_TELETEXT_B_L10_625  0x00000001
+#define VBI_SLICED_TELETEXT_B_L25_625  0x00000002
+/**
+ * Teletext System B for 625 line systems
+ *
+ * Note this is separated into Level 1.0 and Level 2.5+ since the latter
+ * permits occupation of scan line 6 which is frequently out of
+ * range of raw VBI capture drivers. Clients should request decoding of both,
+ * may then verify Level 2.5 is covered. vbi_sliced id can be
+ * VBI_SLICED_TELETEXT_B, _B_L10_625 or _B_L25_625 regardless of line number.
+ *
+ * Reference: <a href="http://www.etsi.org">EN 300 706
+ * "Enhanced Teletext specification"</a>, <a href="http://www.itu.ch">
+ * ITU-R BT.653 "Teletext Systems"</a>
+ *
+ * vbi_sliced payload: Last 42 of the 45 byte Teletext packet, that is
+ * without clock run-in and framing code, lsb first transmitted.
+ */
+#define VBI_SLICED_TELETEXT_B          (VBI_SLICED_TELETEXT_B_L10_625 | \
+                                        VBI_SLICED_TELETEXT_B_L25_625)
+/**
+ * Synonym of VBI_SLICED_TELETEXT_B.
+ * @since 0.2.10
+ */
+#define VBI_SLICED_TELETEXT_B_625      VBI_SLICED_TELETEXT_B
+
+/**
+ * Teletext System C for 625 line systems
+ *
+ * Reference: <a href="http://www.itu.ch">ITU-R BT.653
+ * "Teletext Systems"</a>
+ *
+ * vbi_sliced payload: Last 33 bytes, without clock run-in and
+ * framing code, lsb first transmitted.
+ *
+ * @since 0.2.10
+ */
+#define VBI_SLICED_TELETEXT_C_625       0x00004000
+
+/**
+ * Teletext System D for 625 line systems
+ *
+ * Reference: <a href="http://www.itu.ch">ITU-R BT.653
+ * "Teletext Systems"</a>
+ *
+ * vbi_sliced payload: Last 34 bytes, without clock run-in and
+ * framing code, lsb first transmitted.
+ *
+ * @since 0.2.10
+ */
+#define VBI_SLICED_TELETEXT_D_625       0x00008000
+
+/**
+ * Video Program System
+ *
+ * Reference: <a href="http://www.etsi.org">ETS 300 231
+ * "Specification of the domestic video Programme
+ * Delivery Control system (PDC)"</a>, <a href="http://www.irt.de">
+ * IRT 8R2 "Video-Programm-System (VPS)"</a>.
+ *
+ * vbi_sliced payload: Byte number 3 to 15 according to ETS 300 231
+ * Figure 9, lsb first transmitted.
+ */
+#define VBI_SLICED_VPS                  0x00000004
+
+/**
+ * Pseudo-VPS signal transmitted on field 2
+ *
+ * vbi_sliced payload: 13 bytes.
+ *
+ * @since 0.2.10
+ */
+#define VBI_SLICED_VPS_F2               0x00001000
+
+#define VBI_SLICED_CAPTION_625_F1       0x00000008
+#define VBI_SLICED_CAPTION_625_F2       0x00000010
+/**
+ * Closed Caption for 625 line systems
+ *
+ * Note this is split into field one and two services since for basic
+ * caption decoding only field one is required. vbi_sliced id can be
+ * VBI_SLICED_CAPTION_625, _625_F1 or _625_F2 regardless of line number.
+ *
+ * Reference: <a href="http://global.ihs.com">EIA 608
+ * "Recommended Practice for Line 21 Data Service"</a>.
+ *
+ * vbi_sliced payload: First and second byte including parity,
+ * lsb first transmitted.
+ */
+#define VBI_SLICED_CAPTION_625         (VBI_SLICED_CAPTION_625_F1 | \
+                                         VBI_SLICED_CAPTION_625_F2)
+
+/**
+ * Wide Screen Signalling for 625 line systems
+ *
+ * Reference: <a href="http://www.etsi.org">EN 300 294
+ * "625-line television Wide Screen Signalling (WSS)"</a>.
+ *
+ * vbi_sliced payload:
+ * <pre>
+ * Byte         0                  1
+ *       msb         lsb  msb             lsb
+ * bit   7 6 5 4 3 2 1 0  x x 13 12 11 10 9 8<br></pre>
+ * according to EN 300 294, Table 1, lsb first transmitted.
+ */
+#define VBI_SLICED_WSS_625              0x00000400
+
+#define VBI_SLICED_CAPTION_525_F1      0x00000020
+#define VBI_SLICED_CAPTION_525_F2      0x00000040
+/**
+ * Closed Caption for 525 line systems (NTSC).
+ *
+ * Note this is split into field one and two services since for basic
+ * caption decoding only field one is required. vbi_sliced id can be
+ * VBI_SLICED_CAPTION_525, _525_F1 or _525_F2 regardless of line number.
+ *
+ * VBI_SLICED_CAPTION_525 also covers XDS (Extended Data Service),
+ * V-Chip data and ITV / WebTV data.
+ *
+ * Reference: <a href="http://global.ihs.com">EIA 608
+ * "Recommended Practice for Line 21 Data Service"</a>.
+ *
+ * vbi_sliced payload: First and second byte including parity,
+ * lsb first transmitted.
+ */
+#define VBI_SLICED_CAPTION_525         (VBI_SLICED_CAPTION_525_F1 | \
+                                        VBI_SLICED_CAPTION_525_F2)
+/**
+ * Closed Caption at double bit rate for 525 line systems.
+ *
+ * Reference: ?
+ *
+ * vbi_sliced payload: First to fourth byte including parity bit,
+ * lsb first transmitted.
+ */
+#define VBI_SLICED_2xCAPTION_525       0x00000080
+
+/**
+ * Teletext System B for 525 line systems
+ *
+ * Reference: <a href="http://www.itu.ch">ITU-R BT.653
+ * "Teletext Systems"</a>
+ *
+ * vbi_sliced payload: Last 34 bytes, without clock run-in and
+ * framing code, lsb first transmitted.
+ *
+ * @since 0.2.10
+ */
+#define VBI_SLICED_TELETEXT_B_525       0x00010000
+
+/**
+ * North American Basic Teletext Specification
+ * a.k.a. Teletext System C for 525 line systems
+ *
+ * Reference: <a href="http://global.ihs.com">EIA-516
+ * "North American Basic Teletext Specification (NABTS)"</a>,
+ * <a href="http://www.itu.ch">ITU-R BT.653 "Teletext Systems"</a>
+ *
+ * vbi_sliced payload: Last 33 bytes, without clock run-in and
+ * framing code, lsb first transmitted.
+ *
+ * @since 0.2.10
+ */
+#define VBI_SLICED_NABTS                0x00000100
+
+/**
+ * Synonym of VBI_SLICED_NABTS.
+ * @since 0.2.10
+ */
+#define VBI_SLICED_TELETEXT_C_525       0x00000100
+
+/**
+ * Misdefined.
+ *
+ * vbi_sliced payload: 34 bytes.
+ *
+ * @deprecated
+ * This service was misdefined.
+ * Use VBI_SLICED_TELETEXT_B_525 or VBI_SLICED_TELETEXT_D_525 in new code.
+ */
+#define VBI_SLICED_TELETEXT_BD_525     0x00000200
+
+/**
+ * Teletext System D for 525 line systems
+ *
+ * Reference: <a href="http://www.itu.ch">ITU-R BT.653
+ * "Teletext Systems"</a>
+ *
+ * vbi_sliced payload: Last 34 bytes, without clock run-in and
+ * framing code, lsb first transmitted.
+ *
+ * @since 0.2.10
+ */
+#define VBI_SLICED_TELETEXT_D_525       0x00020000
+
+/**
+ * Wide Screen Signalling for NTSC Japan
+ *
+ * Reference: <a href="http://www.jeita.or.jp">EIA-J CPR-1204</a>
+ *
+ * vbi_sliced payload:
+ * <pre>
+ * Byte         0                    1                  2
+ *       msb         lsb  msb               lsb  msb             lsb
+ * bit   7 6 5 4 3 2 1 0  15 14 13 12 11 10 9 8  x x x x 19 18 17 16
+ * </pre>
+ */
+
+#define VBI_SLICED_WSS_CPR1204         0x00000800
+
+/**
+ * No actual data service. This symbol is used to request capturing
+ * of all PAL/SECAM VBI data lines from the libzvbi driver interface,
+ * as opposed to just those lines used to transmit the requested
+ * data services.
+ */
+#define VBI_SLICED_VBI_625             0x20000000
+
+/**
+ * No actual data service. This symbol is used to request capturing
+ * of all NTSC VBI data lines from the libzvbi driver interface,
+ * as opposed to just those lines used to transmit the requested
+ * data services.
+ */
+#define VBI_SLICED_VBI_525             0x40000000
+
+/** @} */
+
+typedef unsigned int vbi_service_set;
+
+/**
+ * @ingroup Sliced
+ * @brief This structure holds one scan line of sliced vbi data.
+ *
+ * For example the contents of NTSC line 21, two bytes of Closed Caption
+ * data. Usually an array of vbi_sliced is used, covering all
+ * VBI lines of the two fields of a video frame.
+ */
+typedef struct {
+       /**
+        * A @ref VBI_SLICED_ symbol identifying the data service. Under cirumstances
+        * (see VBI_SLICED_TELETEXT_B) this can be a set of VBI_SLICED_ symbols.
+        */
+       uint32_t                id;
+       /**
+        * Source line number according to the ITU-R line numbering scheme,
+        * a value of @c 0 if the exact line number is unknown. Note that some
+        * data services cannot be reliable decoded without line number.
+        *
+        * @image html zvbi_625.gif "ITU-R PAL/SECAM line numbering scheme"
+        * @image html zvbi_525.gif "ITU-R NTSC line numbering scheme"
+        */
+       uint32_t                line;
+       /**
+        * The actual payload. See the documentation of @ref VBI_SLICED_ symbols
+        * for details.
+        */
+       uint8_t                 data[56];
+} vbi_sliced;
+
+/**
+ * @addtogroup Sliced
+ * @{
+ */
+extern const char *
+vbi_sliced_name                        (vbi_service_set        service)
+  _vbi_const;
+extern unsigned int
+vbi_sliced_payload_bits                (vbi_service_set        service)
+  _vbi_const;
+/** @} */
+
+/* Private */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SLICED_H */
+
+/*
+Local variables:
+c-set-style: K&R
+c-basic-offset: 8
+End:
+*/