summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/lib/util/vbiparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/lib/util/vbiparse.c')
-rw-r--r--trunk/src/lib/util/vbiparse.c406
1 files changed, 406 insertions, 0 deletions
diff --git a/trunk/src/lib/util/vbiparse.c b/trunk/src/lib/util/vbiparse.c
new file mode 100644
index 00000000000..89a800fb683
--- /dev/null
+++ b/trunk/src/lib/util/vbiparse.c
@@ -0,0 +1,406 @@
+/***************************************************************************
+
+ vbiparse.c
+
+ Parse Philips codes and other data from VBI lines.
+
+****************************************************************************
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#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
+-------------------------------------------------*/
+
+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
+-------------------------------------------------*/
+
+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
+-------------------------------------------------*/
+
+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];
+}
+