/*
* libzvbi test
*
* Copyright (C) 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.11 2006/09/24 03:08:17 mschimek Exp $ */
/* Automated test of the vbi_raw_decoder. */
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "src/raw_decoder.h"
#include "src/io-sim.h"
#define N_ELEMENTS(array) (sizeof (array) / sizeof (*(array)))
vbi_bool verbose;
typedef struct {
vbi_service_set service;
/* Scan lines. */
unsigned int first;
unsigned int last;
} block;
#define BLOCK_END { 0, 0, 0 }
static void *
memset_rand (void * d1,
size_t n)
{
uint8_t *d = (uint8_t *) d1;
while (n-- > 0)
*d++ = rand ();
return d1;
}
static void *
xmemdup (const void * s,
size_t n)
{
void *d;
d = malloc (n);
assert (NULL != d);
memcpy (d, s, n);
return d;
}
static void
dump_hex (const uint8_t * p,
unsigned int n)
{
while (n-- > 0)
fprintf (stderr, "%02x ", *p++);
}
static void
sliced_rand_lines (const vbi_sliced * s_start,
const vbi_sliced * s_end,
vbi_sliced * s,
vbi_service_set service,
unsigned int first_line,
unsigned int last_line)
{
unsigned int line;
for (line = first_line; line <= last_line; ++line) {
const vbi_sliced *t;
assert (s < s_end);
for (t = s_start; t < s; ++t)
assert (t->line != line);
s->id = service;
s->line = line;
memset_rand (s->data, sizeof (s->data));
++s;
}
}
static unsigned int
sliced_rand (vbi_sliced * s,
unsigned int s_lines,
const block * b)
{
const vbi_sliced *s_start;
vbi_service_set services;
s_start = s;
services = 0;
while (b->service) {
services |= b->service;
if (b->first > 0) {
sliced_rand_lines (s_start, s_start + s_lines, s,
b->service, b->first, b->last);
s += b->last - b->first + 1;
}
++b;
}
if (0)
fprintf (stderr, "services 0x%08x\n", services);
return s - s_start;
}
static unsigned int
create_raw (uint8_t ** raw,
vbi_sliced ** sliced,
const vbi_sampling_par *sp,
const block * b,
unsigned int pixel_mask,
unsigned int raw_flags)
{
unsigned int scan_lines;
unsigned int sliced_lines;
unsigned int raw_size;
scan_lines = sp->count[0] + sp->count[1];
raw_size = sp->bytes_per_line * scan_lines;
*raw = (uint8_t *) malloc (raw_size);
assert (NULL != *raw);
*sliced = (vbi_sliced *) malloc (sizeof (**sliced) * 50);
assert (NULL != *sliced);
sliced_lines = sliced_rand (*sliced, 50, b);
if (pixel_mask) {
memset_rand (*raw, raw_size);
assert (_vbi_raw_video_image (*raw, raw_size, sp,
/* blank_level: default */ 0,
/* black_level: default */ 0,
/* white_level: default */ 0,
pixel_mask,
raw_flags,
*sliced, sliced_lines));
} else {
assert (_vbi_raw_vbi_image (*raw, raw_size, sp,
/* blank_level: default */ 0,
/* white_level: default */ 0,
raw_flags,
*sliced, sliced_lines));
}
return sliced_lines;
}
static vbi3_raw_decoder *
create_decoder (const vbi_sampling_par *sp,
const block * b,
unsigned int strict)
{
vbi3_raw_decoder *rd;
vbi_service_set in_services;
vbi_service_set out_services;
in_services = 0;
while (b->service) {
in_services |= b->service;
++b;
}
rd = vbi3_raw_decoder_new (sp);
assert (NULL != rd);
if (sp->synchronous) {
vbi3_raw_decoder_set_log_fn
(rd,
vbi_log_on_stderr,
/* user_data */ NULL,
/* max_level */ VBI_LOG_INFO * 2 - 1);
} else {
/* Don't complain about expected failures.
XXX Check for those in a different function. */
}
out_services = vbi3_raw_decoder_add_services (rd, in_services, strict);
if (!sp->synchronous) {
/* Ambiguous. */
in_services &= ~(VBI_SLICED_VPS |
VBI_SLICED_VPS_F2 |
VBI_SLICED_WSS_625 |
VBI_SLICED_CAPTION_625 |
VBI_SLICED_CAPTION_525);
}
assert (in_services == out_services);
return rd;
}
static void
compare_payload (const vbi_sliced * in,
const vbi_sliced * out)
{
unsigned int payload;
payload = vbi_sliced_payload_bits (out->id);
assert (0 == memcmp (in->data, out->data, payload >> 3));
if (payload & 7) {
unsigned int mask = (1 << (payload & 7)) - 1;
payload = (payload >> 3);
/* MSBs zero, rest as sent */
assert (0 == ((in->data[payload] & mask)
^ out->data[payload]));
}
}
static void
compare_sliced (const vbi_sampling_par *sp,
const vbi_sliced * in,
const vbi_sliced * out,
const vbi_sliced * old,
unsigned int in_lines,
unsigned int out_lines,
unsigned int old_lines)
{
unsigned int i;
unsigned int min;
unsigned int id;
vbi_sliced *in1;
vbi_sliced *s;
min = 0;
for (i = 0; i < out_lines; ++i) {
unsigned int payload;
if (sp->synchronous) {
/* Ascending line numbers. */
assert (out[i].line > min);
min = out[i].line;
} else {
/* Could be first or second field,
we don't know. */
assert (0 == out[i].line);
}
/* Valid service id. */
assert (0 != out[i].id);
payload = (vbi_sliced_payload_bits (out[i].id) + 7) >> 3;
assert (payload > 0);
/* vbi_sliced big enough. */
assert (payload <= sizeof (out[i].data));
/* Writes more than payload. */
assert (0 == memcmp (out[i].data + payload,
old[i].data + payload,
sizeof (out[i].data) - payload));
}
/* Respects limits. */
assert (0 == memcmp (out + out_lines,
old + out_lines,
sizeof (*old) * (old_lines - out_lines)));
in1 = xmemdup (in, sizeof (*in) * in_lines);
for (i = 0; i < out_lines; ++i) {
if (sp->synchronous) {
for (s = in1; s < in1 + in_lines; ++s)
if (s->line == out[i].line)
break;
/* Found something we didn't send. */
assert (s < in1 + in_lines);
/* Identified as something else. */
/* fprintf (stderr, "%3u id %08x %08x\n", s->line, s->id, out[i].id); */
assert (s->id == out[i].id);
} else {
/* fprintf (stderr, "%08x ", out[i].id); */
/* No line numbers, but data must be in
same order. */
for (s = in1; s < in1 + in_lines; ++s)
if (s->id == out[i].id)
break;
assert (s < in1 + in_lines);
/* fprintf (stderr, "from line %3u\n", s->line); */
}
compare_payload (s, &out[i]);
s->id = 0;
}
id = 0;
for (s = in1; s < in1 + in_lines; ++s)
id |= s->id;
if (!sp->synchronous) {
/* Ok these are ambiguous. */
id &= ~(VBI_SLICED_VPS |
VBI_SLICED_VPS_F2 |
VBI_SLICED_WSS_625 |
VBI_SLICED_CAPTION_625 |
VBI_SLICED_CAPTION_525);
}
/* Anything missed? */
assert (0 == id);
free (in1);
in1 = NULL;
}
static void
test_cycle (const vbi_sampling_par *sp,
const block * b,
unsigned int pixel_mask,
unsigned int raw_flags,
unsigned int strict)
{
vbi_sliced *in;
vbi_sliced out[50];
vbi_sliced old[50];
uint8_t *raw;
vbi3_raw_decoder *rd;
unsigned int in_lines;
unsigned int out_lines;
in_lines = create_raw (&raw, &in, sp, b, pixel_mask, raw_flags);
if (0 && verbose)
dump_hex (raw + 120, 12);
rd = create_decoder (sp, b, strict);
memset_rand (out, sizeof (out));
memcpy (old, out, sizeof (old));
out_lines = vbi3_raw_decoder_decode (rd, out, 40, raw);
if (verbose)
fprintf (stderr, "%s %d in=%u out=%u\n",
__FUNCTION__,
(sp->sampling_format),
in_lines, out_lines);
if (sp->synchronous)
assert (in_lines == out_lines);
compare_sliced (sp, in, out, old, in_lines, out_lines, 50);
free (in);
free (raw);
}
static vbi_bool
block_contains_service (const block * b,
vbi_service_set services)
{
while (b->service) {
if (b->service & services)
return TRUE;
++b;
}
return FALSE;
}
static void
test_vbi (const vbi_sampling_par *sp,
const block * b,
unsigned int strict)
{
test_cycle (sp, b, /* pixel_mask */ 0,
/* raw_flags */ 0, strict);
/* Tests incorrect signal shape reported by Rich Kadel. */
if (block_contains_service (b, VBI_SLICED_CAPTION_525))
test_cycle (sp, b, /* pixel_mask */ 0,
_VBI_RAW_SHIFT_CC_CRI, strict);
}
static void
test_video (const vbi_sampling_par *sp,
const block * b,
unsigned int strict)
{
vbi_sampling_par sp2;
unsigned int pixel_mask;
vbi_pixfmt pixfmt;
unsigned int samples_per_line;
sp2 = *sp;
samples_per_line = sp->bytes_per_line
/ VBI_PIXFMT_BPP (sp->sampling_format);
for (pixfmt = (vbi_pixfmt) 0; pixfmt < VBI_MAX_PIXFMTS;
pixfmt = (vbi_pixfmt)(pixfmt + 1)) {
if (0 == (VBI_PIXFMT_SET_ALL & VBI_PIXFMT_SET (pixfmt)))
continue;
sp2.sampling_format = pixfmt;
sp2.bytes_per_line = samples_per_line
* VBI_PIXFMT_BPP (pixfmt);
/* Check bit slicer looks at Y/G */
if (VBI_PIXFMT_SET (pixfmt) & VBI_PIXFMT_SET_YUV)
pixel_mask = 0xFF;
else
pixel_mask = 0xFF00;
test_cycle (&sp2, b, pixel_mask,
/* raw_flags */ 0, strict);
if (block_contains_service (b, VBI_SLICED_CAPTION_525))
test_cycle (&sp2, b, pixel_mask,
_VBI_RAW_SHIFT_CC_CRI, strict);
}
}
static const block ttx_a [] = {
{ VBI_SLICED_TELETEXT_A, 6, 22 },
{ VBI_SLICED_TELETEXT_A, 318, 335 },
BLOCK_END,
};
static const block ttx_c_625 [] = {
{ VBI_SLICED_TELETEXT_C_625, 6, 22 },
{ VBI_SLICED_TELETEXT_C_625, 318, 335 },
BLOCK_END,
};
static const block ttx_d_625 [] = {
{ VBI_SLICED_TELETEXT_D_625, 6, 22 },
{ VBI_SLICED_TELETEXT_D_625, 318, 335 },
BLOCK_END,
};
static const block hi_625 [] = {
{ VBI_SLICED_TELETEXT_B_625, 6, 21 },
{ VBI_SLICED_CAPTION_625, 22, 22 },
{ VBI_SLICED_WSS_625, 23, 23 },
{ VBI_SLICED_TELETEXT_B_625, 318, 334 },
{ VBI_SLICED_CAPTION_625, 335, 335 },
BLOCK_END,
};
static const block hi_f1_625 [] = {
{ VBI_SLICED_VPS, 16, 16 },
{ VBI_SLICED_CAPTION_625_F1, 22, 22 },
{ VBI_SLICED_WSS_625, 23, 23 },
BLOCK_END,
};
static const block hi_f2_525 [] = {
{ VBI_SLICED_CAPTION_525_F2, 284, 284 },
BLOCK_END,
};
static const block low_625 [] = {
{ VBI_SLICED_VPS, 16, 16 },
{ VBI_SLICED_CAPTION_625, 22, 22 },
{ VBI_SLICED_WSS_625, 23, 23 },
{ VBI_SLICED_CAPTION_625, 335, 335 },
BLOCK_END,
};
static const block cc_625 [] = {
{ VBI_SLICED_CAPTION_625, 22, 22 },
{ VBI_SLICED_CAPTION_625, 335, 335 },
BLOCK_END,
};
static const block ttx_c_525 [] = {
{ VBI_SLICED_TELETEXT_C_525, 10, 21 },
{ VBI_SLICED_TELETEXT_C_525, 272, 284 },
BLOCK_END,
};
static const block ttx_d_525 [] = {
{ VBI_SLICED_TELETEXT_D_525, 10, 21 },
{ VBI_SLICED_TELETEXT_D_525, 272, 284 },
BLOCK_END,
};
static const block hi_525 [] = {
{ VBI_SLICED_TELETEXT_B_525, 10, 20 },
{ VBI_SLICED_CAPTION_525, 21, 21 },
{ VBI_SLICED_TELETEXT_B_525, 272, 283 },
{ VBI_SLICED_CAPTION_525, 284, 284 },
BLOCK_END,
};
static const block low_525 [] = {
{ VBI_SLICED_CAPTION_525, 21, 21 },
{ VBI_SLICED_CAPTION_525, 284, 284 },
BLOCK_END,
};
static void
test2 (const vbi_sampling_par *sp)
{
if (625 == sp->scanning) {
if (sp->sampling_rate >= 13500000) {
vbi_sampling_par sp1;
/* We cannot mix Teletext standards; bit rate and
FRC are too similar to reliable distinguish. */
test_vbi (sp, ttx_a, 1);
test_vbi (sp, ttx_c_625, 1);
/* Needs sampling beyond 0H + 63 us (?) */
if (sp->bytes_per_line
== 2048 * VBI_PIXFMT_BPP (sp->sampling_format))
test_vbi (sp, ttx_d_625, 1);
test_vbi (sp, hi_625, 1);
test_video (sp, hi_625, 1);
if (!sp->interlaced) {
sp1 = *sp;
sp1.start[1] = 0;
sp1.count[1] = 0;
test_vbi (&sp1, hi_f1_625, 2);
}
} else if (sp->sampling_rate >= 5000000) {
test_vbi (sp, low_625, 1);
test_video (sp, low_625, 1);
} else {
/* WSS not possible below 5 MHz due to a cri_rate
check in bit_slicer_init(), but much less won't
work anyway. */
test_vbi (sp, cc_625, 1);
test_video (sp, cc_625, 1);
}
} else {
if (sp->sampling_rate >= 13500000) {
vbi_sampling_par sp1;
test_vbi (sp, ttx_c_525, 1);
test_vbi (sp, ttx_d_525, 1);
test_vbi (sp, hi_525, 1);
test_video (sp, hi_525, 1);
if (!sp->interlaced) {
sp1 = *sp;
sp1.start[0] = 0;
sp1.count[0] = 0;
test_vbi (&sp1, hi_f2_525, 2);
}
} else {
test_vbi (sp, low_525, 1);
test_video (sp, low_525, 1);
}
}
}
static void
test1 (const vbi_sampling_par *sp)
{
static const struct {
unsigned int sampling_rate;
unsigned int samples_per_line;
} res [] = {
/* bt8x8 PAL ~35.5 MHz / 2048
bt8x8 NTSC ~28.6 MHz / 2048
PAL 1:1 ~14.7 MHz / 768
ITU-R BT.601 13.5 MHz / 720
NTSC 1:1 ~12.3 MHz / 640 */
{ 35468950, 2048 },
{ 27000000, 1440 },
{ 13500000, 720 },
{ 3000000, 176 },
};
vbi_sampling_par sp2;
unsigned int i;
for (i = 0; i < N_ELEMENTS (res); ++i) {
if (verbose)
fprintf (stderr, "%.2f MHz %u spl\n",
res[i].sampling_rate / 1e6,
res[i].samples_per_line);
sp2 = *sp;
sp2.sampling_rate = res[i].sampling_rate;
sp2.bytes_per_line = res[i].samples_per_line
* VBI_PIXFMT_BPP (sp2.sampling_format);
sp2.offset = (int)(9.7e-6 * sp2.sampling_rate);
test2 (&sp2);
}
}
static void
test_services (void)
{
vbi_sampling_par sp;
vbi_service_set set;
memset (&sp, 0x55, sizeof (sp));
set = vbi_sampling_par_from_services (&sp,
/* &max_rate */ NULL,
VBI_VIDEOSTD_SET_625_50,
~0 & ~VBI_SLICED_VBI_625);
assert (set == (VBI_SLICED_TELETEXT_A |
VBI_SLICED_TELETEXT_B_625 |
VBI_SLICED_TELETEXT_C_625 |
VBI_SLICED_TELETEXT_D_625 |
VBI_SLICED_VPS |
VBI_SLICED_VPS_F2 |
VBI_SLICED_CAPTION_625 |
VBI_SLICED_WSS_625));
test2 (&sp);
set = vbi_sampling_par_from_services (&sp,
/* &max_rate */ NULL,
VBI_VIDEOSTD_SET_525_60,
~0 & ~VBI_SLICED_VBI_525);
assert (set == (VBI_SLICED_TELETEXT_B_525 |
VBI_SLICED_TELETEXT_C_525 |
VBI_SLICED_TELETEXT_D_525 |
VBI_SLICED_CAPTION_525 |
VBI_SLICED_2xCAPTION_525
/* Needs fix */
/* | VBI_SLICED_WSS_CPR1204 */ ));
test2 (&sp);
}
static void
test_line_order (vbi_bool synchronous)
{
vbi_sampling_par sp;
memset (&sp, 0x55, sizeof (sp));
sp.scanning = 625;
sp.sampling_format = VBI_PIXFMT_YUV420;
sp.start[0] = 6;
sp.count[0] = 23 - 6 + 1;
sp.start[1] = 318;
sp.count[1] = 335 - 318 + 1;
sp.interlaced = FALSE;
sp.synchronous = synchronous;
test1 (&sp);
sp.interlaced = TRUE;
test1 (&sp);
sp.scanning = 525;
sp.sampling_format = VBI_PIXFMT_YUV420;
sp.start[0] = 10;
sp.count[0] = 21 - 10 + 1;
sp.start[1] = 272;
sp.count[1] = 284 - 272 + 1;
sp.interlaced = FALSE;
sp.synchronous = synchronous;
test1 (&sp);
}
int
main (int argc,
char ** argv)
{
argc = argc;
argv = argv;
verbose = (argc > 1);
test_services ();
test_line_order (/* synchronous */ TRUE);
test_line_order (/* synchronous */ FALSE);
/* More... */
exit (EXIT_SUCCESS);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1