/*
 *  libzvbi - Raw VBI decoder
 *
 *  Copyright (C) 2000-2004 Michael H. Schimek
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* $Id: raw_decoder.c,v 1.16 2006/09/24 03:08:06 mschimek Exp $ */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "misc.h"
#include "raw_decoder.h"

#define vbi_inline static __inline__

#ifndef RAW_DECODER_PATTERN_DUMP
#  define DECODER_PATTERN_DUMP 0
#endif

#ifndef RAW_DECODER_LOG
#  define RAW_DECODER_LOG 0
#endif

#define sp_log(level, templ, args...)					\
do {									\
	_vbi_log_printf (log_fn, log_user_data,				\
			level, __FUNCTION__, templ , ##args);		\
} while (0)

/**
 * $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 ?
*/
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,
		/* 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,
		_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";

	/* 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;

	/* 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;
}

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 (!job->slicer.func (&job->slicer,
					       sliced->data, 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. */

			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 sliced_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		sliced_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 + sliced_lines;

	if (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)
		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 vbi_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;

		scan_lines = rd->sampling.count[0] + rd->sampling.count[1];
		scan_ways = scan_lines * _VBI3_RAW_DECODER_MAX_WAYS;

		rd->pattern = (int8_t *)
			malloc (scan_ways * sizeof (rd->pattern[0]));
		if (!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. */
		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;
		}

		/* XXX use sp->samples_per_line if available */
		samples_per_line = sp->bytes_per_line
			/ VBI_PIXFMT_BPP (sp->sampling_format);

		if (!_vbi3_bit_slicer_init (&job->slicer)) {
			assert (!"bit_slicer_init");
		}

		if (!vbi3_bit_slicer_set_params
		    (&job->slicer,
		     sp->sampling_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");
		}

		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_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;

	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)
{
	assert (NULL != rd);

	if (NULL == log_fn)
		mask = 0;

	rd->log.fn = log_fn;
	rd->log.user_data = user_data;
	rd->log.mask = mask;
}

/**
 * $internal
 *
 * Free all resources associated with $a rd.
 */
void
_vbi3_raw_decoder_destroy	(vbi3_raw_decoder *	rd)
{
	vbi3_raw_decoder_reset (rd);

	/* 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 (RAW_DECODER_LOG) {
		vbi3_raw_decoder_set_log_fn (rd,
					     vbi_log_on_stderr,
					     /* user_data */ NULL,
					     /* mask */ VBI_LOG_INFO * 2 - 1);
	}

	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);

	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 vbi_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 = malloc (sizeof (*rd));
	if (NULL == rd)
		return NULL;

	if (!_vbi3_raw_decoder_init (rd, sp)) {
		free (rd);
		rd = NULL;
	}

	return rd;
}


syntax highlighted by Code2HTML, v. 0.9.1