mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
1010 lines
28 KiB
C
1010 lines
28 KiB
C
/*
|
|
* 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_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_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_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_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:
|
|
*/
|