| /* | |
| * 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; */ | |
| warn (&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) { | |
| warn (&bs->log, | |
| "buffer_size %u < %u bits of payload.", buffer_size * 8, bs->payload); | |
| return FALSE; | |
| } | |
| if (bs->total_bits > max_points) { | |
| warn (&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) { | |
| warn (&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) { | |
| warn (&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) { | |
| warn (&bs->log, "cri_rate %u > sampling_rate %u.", cri_rate, sampling_rate); | |
| goto failure; | |
| } | |
| if (payload_rate > sampling_rate) { | |
| warn (&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: | |
| warn (&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))) { | |
| warn (&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: | |
| */ |