diff options
author | 2015-11-08 12:56:12 +0100 | |
---|---|---|
committer | 2015-11-08 12:56:12 +0100 | |
commit | 7c19aac60e12d6f5ea301bdb34d7826a01e0b06f (patch) | |
tree | f310d86aa2c6bfc19d115307dedde4eb0cd52dad /src/lib/util/vbiparse.cpp | |
parent | a57b46ae933badd7441ce1644711dbb851e2b504 (diff) |
Rename *.c -> *.cpp in our source (nw)
Diffstat (limited to 'src/lib/util/vbiparse.cpp')
-rw-r--r-- | src/lib/util/vbiparse.cpp | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/src/lib/util/vbiparse.cpp b/src/lib/util/vbiparse.cpp new file mode 100644 index 00000000000..9c6c39fc4ea --- /dev/null +++ b/src/lib/util/vbiparse.cpp @@ -0,0 +1,408 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + vbiparse.c + + Parse Philips codes and other data from VBI lines. + +***************************************************************************/ + +#include "osdcore.h" +#include "vbiparse.h" +#include <math.h> + + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define PRINTF_WHITE_FLAG 0 + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define MAX_SOURCE_WIDTH 1024 +#define MAX_CLOCK_DIFF 3 + + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + vbi_parse_manchester_code - parse a Manchester + code from a line of video data +-------------------------------------------------*/ + +int vbi_parse_manchester_code(const UINT16 *source, int sourcewidth, int sourceshift, int expectedbits, UINT32 *result) +{ + UINT8 srcabs[MAX_SOURCE_WIDTH]; + UINT8 min, max, mid, srcabsval; + double clock, bestclock; + int x, firstedge; + int besterr; + + /* fail if the width is too large */ + if (sourcewidth > MAX_SOURCE_WIDTH) + return 0; + + /* find highs and lows in the line */ + min = 0xff; + max = 0x00; + for (x = 0; x < sourcewidth; x++) + { + UINT8 rawsrc = source[x] >> sourceshift; + min = MIN(min, rawsrc); + max = MAX(max, rawsrc); + } + + /* bail if the line is all black or all white */ + if (max < 0x80 || min > 0x80) + return 0; + + /* determine the midpoint and then set the thresholds to be halfway */ + mid = (min + max) / 2; + min = mid - (mid - min) / 2; + max = mid + (max - mid) / 2; + + /* convert the source into absolute high/low */ + srcabsval = (source[0] > mid); + for (x = 0; x < sourcewidth; x++) + { + UINT8 rawsrc = source[x] >> sourceshift; + if (rawsrc >= max) + srcabsval = 1; + else if (rawsrc <= min) + srcabsval = 0; + srcabs[x] = srcabsval; + } + + /* find the first transition; this is assumed to be the middle of the first bit */ + for (x = 0; x < sourcewidth - 1; x++) + if (srcabs[x] != srcabs[x + 1]) + break; + if (x == sourcewidth - 1) + return 0; + firstedge = x; + + /* now scan to find a clock that has a nearby transition on each beat */ + bestclock = 0; + besterr = 1000; + for (clock = (double)sourcewidth / (double)expectedbits; clock >= 2.0; clock -= 1.0 / (double)expectedbits) + { + int error = 0; + + /* scan for all the expected bits */ + for (x = 1; x < expectedbits; x++) + { + int curbit = firstedge + (double)x * clock; + int offby; + + /* look for a match that is off by an amount up to the maximum */ + for (offby = 0; offby <= MAX_CLOCK_DIFF; offby++) + if (srcabs[curbit + offby + 0] != srcabs[curbit + offby + 1] || srcabs[curbit - offby + 0] != srcabs[curbit - offby + 1]) + break; + + /* if we never found the edge, fail immediately */ + if (offby > MAX_CLOCK_DIFF) + break; + + /* only continue if we're still in the running */ + error += offby; + if (error >= besterr) + break; + } + + /* if we got to the end, this is the best candidate so far */ + if (x == expectedbits) + { + besterr = error; + bestclock = clock; + } + } + + /* if nobody matched, fail */ + if (bestclock == 0) + return 0; + + /* now extract the bits */ + for (x = 0; x < expectedbits; x++) + { + int leftstart = firstedge + ceil(((double)x - 0.5) * bestclock); +// int leftmid = firstedge + ((double)x - 0.25) * bestclock; + int leftend = firstedge + floor(((double)x - 0.0) * bestclock); + int rightstart = firstedge + ceil(((double)x + 0.0) * bestclock); +// int rightmid = firstedge + ((double)x + 0.25) * bestclock; + int rightend = firstedge + floor(((double)x + 0.5) * bestclock); + int leftavg, rightavg, leftabs, rightabs; + int confidence = 0; + int tx; + + /* compute left and right average values */ + leftavg = 0; + for (tx = leftstart; tx <= leftend; tx++) + leftavg += (UINT8)(source[tx] >> sourceshift) - mid; + leftabs = (leftavg >= 0); + leftavg = (leftavg < 0) ? -leftavg : leftavg; + + rightavg = 0; + for (tx = rightstart; tx <= rightend; tx++) + rightavg += (UINT8)(source[tx] >> sourceshift) - mid; + rightabs = (rightavg >= 0); + rightavg = (rightavg < 0) ? -rightavg : rightavg; + + /* all bits should be marked by transitions; fail if we don't get one */ + if (leftabs == rightabs) + return 0; + + /* store the bit and its confidence level */ + confidence = leftavg + rightavg; + result[x] = (leftabs < rightabs) | (confidence << 1); + } + return expectedbits; +} + + +/*------------------------------------------------- + vbi_parse_white_flag - compute the "white + flag" from a line of video data +-------------------------------------------------*/ + +int vbi_parse_white_flag(const UINT16 *source, int sourcewidth, int sourceshift) +{ + int histo[256] = { 0 }; + int minval = 0xff; + int maxval = 0x00; + int subtract; + int peakval; + int result; + int x; + + /* compute a histogram of values */ + for (x = 0; x < sourcewidth; x++) + { + UINT8 yval = source[x] >> sourceshift; + histo[yval]++; + } + + /* remove the lowest 1% of the values to account for noise and determine the minimum */ + subtract = sourcewidth / 100; + for (minval = 0; minval < 255; minval++) + if ((subtract -= histo[minval]) < 0) + break; + + /* remove the highest 1% of the values to account for noise and determine the maximum */ + subtract = sourcewidth / 100; + for (maxval = 255; maxval > 0; maxval--) + if ((subtract -= histo[maxval]) < 0) + break; + + /* this is useful for debugging issues with white flag detection */ + if (PRINTF_WHITE_FLAG) + { + printf("Histo: min=%02X max=%02X\n", minval, maxval); + for (x = 0; x < 256; x++) + if (histo[x] != 0) printf("%dx%02X\n", histo[x], x); + } + + /* ignore if we have no dynamic range */ + if (maxval - minval < 10) + { + if (PRINTF_WHITE_FLAG) + printf("White flag NOT detected; threshold too low\n"); + return FALSE; + } + + /* + At this point, there are two approaches that have been tried: + + 1. Find the peak value and call it white if the peak is above + the 90% line + + 2. Ignore the first and last 20% of the line and count how + many pixels are above some threshold (75% line was used). + Call it white if at least 80% of the pixels are above + the threshold. + + Both approaches agree 99% of the time, but the first tends to + be more correct when there is a discrepancy. + */ + + /* determine where the peak is */ + peakval = 0; + for (x = 1; x < 256; x++) + if (histo[x] > histo[peakval]) + peakval = x; + + /* return TRUE if it is above the 90% mark */ + result = (peakval > minval + 9 * (maxval - minval) / 10); + if (PRINTF_WHITE_FLAG) + printf("White flag %s: peak=%02X thresh=%02X\n", result ? "detected" : "NOT detected", peakval, minval + 9 * (maxval - minval) / 10); + return result; + +#ifdef UNUSED_CODE +{ + int above = 0; + int thresh; + + /* alternate approach: */ + + /* ignore the first 1/5 and last 1/5 of the line for the remaining computations */ + source += sourcewidth / 5; + sourcewidth -= 2 * (sourcewidth / 5); + + /* count how many values were above the 75% mark of the range */ + thresh = minval + 3 * (maxval - minval) / 4; + for (x = 0; x < sourcewidth; x++) + { + UINT8 yval = source[x] >> sourceshift; + above += (yval >= thresh); + } + /* if at least 80% of the pixels are above the threshold, we'll call it white */ + return ((above * 100) / sourcewidth >= 80); +} +#endif +} + + +/*------------------------------------------------- + vbi_parse_all - parse everything from a video + frame +-------------------------------------------------*/ + +/** + * @fn void vbi_parse_all(const UINT16 *source, int sourcerowpixels, int sourcewidth, int sourceshift, vbi_metadata *vbi) + * + * @brief Vbi parse all. + * + * @param source Source for the. + * @param sourcerowpixels The sourcerowpixels. + * @param sourcewidth The sourcewidth. + * @param sourceshift The sourceshift. + * @param [in,out] vbi If non-null, the vbi. + */ + +void vbi_parse_all(const UINT16 *source, int sourcerowpixels, int sourcewidth, int sourceshift, vbi_metadata *vbi) +{ + UINT32 bits[2][24]; + UINT8 bitnum; + + /* first reset it all */ + memset(vbi, 0, sizeof(*vbi)); + + /* get the white flag */ + vbi->white = vbi_parse_white_flag(source + 11 * sourcerowpixels, sourcewidth, sourceshift); + + /* parse line 16 */ + if (vbi_parse_manchester_code(source + 16 * sourcerowpixels, sourcewidth, sourceshift, 24, bits[0]) == 24) + for (bitnum = 0; bitnum < 24; bitnum++) + vbi->line16 = (vbi->line16 << 1) | (bits[0][bitnum] & 1); + + /* parse line 17 */ + if (vbi_parse_manchester_code(source + 17 * sourcerowpixels, sourcewidth, sourceshift, 24, bits[0]) == 24) + for (bitnum = 0; bitnum < 24; bitnum++) + vbi->line17 = (vbi->line17 << 1) | (bits[0][bitnum] & 1); + + /* parse line 18 */ + if (vbi_parse_manchester_code(source + 18 * sourcerowpixels, sourcewidth, sourceshift, 24, bits[1]) == 24) + for (bitnum = 0; bitnum < 24; bitnum++) + vbi->line18 = (vbi->line18 << 1) | (bits[1][bitnum] & 1); + + /* pick the best out of lines 17/18 */ + /* if we only got one or the other, that's all we have */ + if (vbi->line17 == 0) + vbi->line1718 = vbi->line18; + else if (vbi->line18 == 0) + vbi->line1718 = vbi->line17; + + /* if they agree, we're golden */ + else if (vbi->line17 == vbi->line18) + vbi->line1718 = vbi->line17; + + /* if they don't agree, we have to pick one */ + else + { + /* if both are frame numbers, and one is not valid BCD, pick the other */ + if ((vbi->line17 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE && (vbi->line18 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE) + { + if ((vbi->line17 & 0xf000) > 0x9000 || (vbi->line17 & 0xf00) > 0x900 || (vbi->line17 & 0xf0) > 0x90 || (vbi->line17 & 0xf) > 0x9) + vbi->line1718 = vbi->line18; + else if ((vbi->line18 & 0xf000) > 0x9000 || (vbi->line18 & 0xf00) > 0x900 || (vbi->line18 & 0xf0) > 0x90 || (vbi->line18 & 0xf) > 0x9) + vbi->line1718 = vbi->line17; + } + + /* if still nothing, then scan through the bits and pick the ones with the most confidence */ + if (vbi->line1718 == 0) + for (bitnum = 0; bitnum < 24; bitnum++) + vbi->line1718 = (vbi->line1718 << 1) | ((bits[0][bitnum] > bits[1][bitnum]) ? (bits[0][bitnum] & 1) : (bits[1][bitnum] & 1)); + } +} + + +/*------------------------------------------------- + vbi_metadata_pack - pack the VBI data down + into a smaller form for storage +-------------------------------------------------*/ + +/** + * @fn void vbi_metadata_pack(UINT8 *dest, UINT32 framenum, const vbi_metadata *vbi) + * + * @brief Vbi metadata pack. + * + * @param [in,out] dest If non-null, destination for the. + * @param framenum The framenum. + * @param vbi The vbi. + */ + +void vbi_metadata_pack(UINT8 *dest, UINT32 framenum, const vbi_metadata *vbi) +{ + dest[0] = framenum >> 16; + dest[1] = framenum >> 8; + dest[2] = framenum >> 0; + dest[3] = vbi->white; + dest[4] = vbi->line16 >> 16; + dest[5] = vbi->line16 >> 8; + dest[6] = vbi->line16 >> 0; + dest[7] = vbi->line17 >> 16; + dest[8] = vbi->line17 >> 8; + dest[9] = vbi->line17 >> 0; + dest[10] = vbi->line18 >> 16; + dest[11] = vbi->line18 >> 8; + dest[12] = vbi->line18 >> 0; + dest[13] = vbi->line1718 >> 16; + dest[14] = vbi->line1718 >> 8; + dest[15] = vbi->line1718 >> 0; +} + + +/*------------------------------------------------- + vbi_metadata_unpack - unpack the VBI data + from a smaller form into the full structure +-------------------------------------------------*/ + +/** + * @fn void vbi_metadata_unpack(vbi_metadata *vbi, UINT32 *framenum, const UINT8 *source) + * + * @brief Vbi metadata unpack. + * + * @param [in,out] vbi If non-null, the vbi. + * @param [in,out] framenum If non-null, the framenum. + * @param source Source for the. + */ + +void vbi_metadata_unpack(vbi_metadata *vbi, UINT32 *framenum, const UINT8 *source) +{ + if (framenum != NULL) + *framenum = (source[0] << 16) | (source[1] << 8) | source[2]; + vbi->white = source[3]; + vbi->line16 = (source[4] << 16) | (source[5] << 8) | source[6]; + vbi->line17 = (source[7] << 16) | (source[8] << 8) | source[9]; + vbi->line18 = (source[10] << 16) | (source[11] << 8) | source[12]; + vbi->line1718 = (source[13] << 16) | (source[14] << 8) | source[15]; +} |