1906 lines
36 KiB
C
1906 lines
36 KiB
C
/*
|
|
* libzvbi test
|
|
*
|
|
* Copyright (C) 2005 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., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301, USA.
|
|
*/
|
|
|
|
/* $Id: sliced.c,v 1.18 2008/03/01 07:37:29 mschimek Exp $ */
|
|
|
|
/* For libzvbi version 0.2.x / 0.3.x. */
|
|
|
|
#undef NDEBUG
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
/* Misc. helper functions. */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
#include <locale.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "dvb_mux.h"
|
|
#include "dvb_demux.h"
|
|
#include "io.h"
|
|
#include "io-sim.h"
|
|
#include "raw_decoder.h"
|
|
#include "vbi.h"
|
|
#include "sliced_vbi.h"
|
|
|
|
#if 2 == VBI_VERSION_MINOR
|
|
# include "proxy-msg.h"
|
|
# include "proxy-client.h"
|
|
# define sp_sample_format sampling_format
|
|
# define sp_samples_per_line bytes_per_line
|
|
# define VBI_PIXFMT_Y8 VBI_PIXFMT_YUV420
|
|
# define vbi_pixfmt_name(x) "Y8"
|
|
# define vbi_pixfmt_bytes_per_pixel(x) 1
|
|
#elif 3 == VBI_VERSION_MINOR
|
|
# define sp_sample_format sample_format
|
|
# define sp_samples_per_line samples_per_line
|
|
#else
|
|
# error VBI_VERSION_MINOR == ?
|
|
#endif
|
|
|
|
#undef _
|
|
#define _(x) x /* later */
|
|
|
|
typedef vbi_bool
|
|
read_loop_fn (struct stream * st);
|
|
|
|
typedef vbi_bool
|
|
write_fn (struct stream * st,
|
|
const vbi_sliced * sliced,
|
|
unsigned int n_lines,
|
|
const uint8_t * raw,
|
|
const vbi_sampling_par *sp,
|
|
double sample_time,
|
|
int64_t stream_time);
|
|
|
|
struct stream {
|
|
uint8_t buffer[4096];
|
|
|
|
uint8_t b64_buffer[4096];
|
|
|
|
vbi_sliced sliced[64];
|
|
vbi_sliced sliced2[64];
|
|
|
|
uint8_t * raw;
|
|
|
|
const uint8_t * bp;
|
|
const uint8_t * end;
|
|
|
|
stream_callback_fn * callback;
|
|
|
|
read_loop_fn * loop;
|
|
write_fn * write_func;
|
|
|
|
vbi_dvb_mux * mx;
|
|
vbi_dvb_demux * dx;
|
|
#if 2 == VBI_VERSION_MINOR
|
|
vbi_proxy_client * proxy;
|
|
#endif
|
|
vbi_capture * cap;
|
|
vbi_raw_decoder * rd;
|
|
vbi_sampling_par sp;
|
|
|
|
vbi_bool raw_valid;
|
|
vbi_bool decode_raw;
|
|
vbi_bool debug;
|
|
|
|
unsigned int sliced2_lines;
|
|
|
|
double sample_time;
|
|
int64_t stream_time;
|
|
|
|
unsigned int interfaces;
|
|
unsigned int system;
|
|
vbi_bool read_not_pull;
|
|
|
|
int fd;
|
|
vbi_bool close_fd;
|
|
void *userdata ;
|
|
};
|
|
|
|
#ifndef HAVE_PROGRAM_INVOCATION_NAME
|
|
char * program_invocation_name;
|
|
char * program_invocation_short_name;
|
|
#endif
|
|
|
|
const char * option_dev_name;
|
|
unsigned int option_dvb_pid;
|
|
vbi_bool option_quiet;
|
|
unsigned int option_log_mask;
|
|
|
|
static vbi_bool have_dev_name;
|
|
|
|
void
|
|
vprint_error (const char * template,
|
|
va_list ap)
|
|
{
|
|
if (option_quiet)
|
|
return;
|
|
|
|
fprintf (stderr, "%s: ", program_invocation_short_name);
|
|
|
|
vfprintf (stderr, template, ap);
|
|
|
|
fputc ('\n', stderr);
|
|
}
|
|
|
|
void
|
|
error_msg (const char * template,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start (ap, template);
|
|
vprint_error (template, ap);
|
|
va_end (ap);
|
|
}
|
|
|
|
void
|
|
error_exit (const char * template,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start (ap, template);
|
|
vprint_error (template, ap);
|
|
va_end (ap);
|
|
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
void
|
|
write_error_exit (const char * msg)
|
|
{
|
|
if (NULL == msg)
|
|
msg = strerror (errno);
|
|
|
|
error_exit (_("Write error: %s."), msg);
|
|
}
|
|
|
|
void
|
|
read_error_exit (const char * msg)
|
|
{
|
|
if (NULL == msg)
|
|
msg = strerror (errno);
|
|
|
|
error_exit (_("Read error: %s."), msg);
|
|
}
|
|
|
|
void
|
|
no_mem_exit (void)
|
|
{
|
|
error_exit (_("Out of memory."));
|
|
}
|
|
|
|
static void
|
|
premature_exit (void)
|
|
{
|
|
error_exit (_("Premature end of input file."));
|
|
}
|
|
|
|
static void
|
|
bad_format_exit (void)
|
|
{
|
|
error_exit (_("Invalid data in input file."));
|
|
}
|
|
|
|
void
|
|
stream_delete (struct stream * st)
|
|
{
|
|
if (NULL == st)
|
|
return;
|
|
|
|
if (st->close_fd) {
|
|
if (-1 == close (st->fd)) {
|
|
if (NULL != st->write_func)
|
|
write_error_exit (/* msg: errno */ NULL);
|
|
}
|
|
}
|
|
|
|
vbi_capture_delete (st->cap);
|
|
#if 3 == VBI_VERSION_MINOR
|
|
vbi_raw_decoder_delete (st->rd);
|
|
#endif
|
|
|
|
free (st->raw);
|
|
|
|
CLEAR (*st);
|
|
|
|
free (st);
|
|
}
|
|
|
|
vbi_bool
|
|
stream_loop (struct stream * st)
|
|
{
|
|
return st->loop (st);
|
|
}
|
|
|
|
static vbi_bool
|
|
pes_ts_cb (vbi_dvb_mux * mx,
|
|
void * user_data,
|
|
const uint8_t * packet,
|
|
unsigned int packet_size)
|
|
{
|
|
struct stream *st = (struct stream *) user_data;
|
|
ssize_t actual;
|
|
|
|
mx = mx; /* unused */
|
|
|
|
assert (packet_size < 66000);
|
|
|
|
actual = write (st->fd, packet, packet_size);
|
|
if (actual != (ssize_t) packet_size)
|
|
write_error_exit (/* msg: errno */ NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static vbi_bool
|
|
write_func_pes_ts (struct stream * st,
|
|
const vbi_sliced * sliced,
|
|
unsigned int n_lines,
|
|
const uint8_t * raw,
|
|
const vbi_sampling_par *sp,
|
|
double sample_time,
|
|
int64_t stream_time)
|
|
{
|
|
vbi_bool success;
|
|
|
|
assert (NULL != sliced);
|
|
assert (n_lines <= 32);
|
|
assert (stream_time >= 0);
|
|
|
|
raw = raw; /* unused */
|
|
sp = sp;
|
|
sample_time = sample_time;
|
|
|
|
success = vbi_dvb_mux_feed (st->mx,
|
|
sliced, n_lines,
|
|
(VBI_SLICED_CAPTION_625 |
|
|
VBI_SLICED_TELETEXT_B_625 |
|
|
VBI_SLICED_VPS |
|
|
VBI_SLICED_WSS_625),
|
|
/* raw */ NULL,
|
|
/* sp */ NULL,
|
|
/* pts */ stream_time);
|
|
if (!success) {
|
|
/* Probably. */
|
|
error_exit (_("Maximum PES packet size %u bytes "
|
|
"is too small for this input stream."),
|
|
vbi_dvb_mux_get_max_pes_packet_size (st->mx));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
struct service {
|
|
const char * name;
|
|
vbi_service_set id;
|
|
unsigned int n_bytes;
|
|
};
|
|
|
|
static const struct service
|
|
service_map [] = {
|
|
{ "TELETEXT_B", VBI_SLICED_TELETEXT_B, 42 },
|
|
{ "CAPTION_625", VBI_SLICED_CAPTION_625, 2 },
|
|
{ "VPS", VBI_SLICED_VPS | VBI_SLICED_VPS_F2, 13 },
|
|
{ "WSS_625", VBI_SLICED_WSS_625, 2 },
|
|
{ "WSS_CPR1204", VBI_SLICED_WSS_CPR1204, 3 },
|
|
{ NULL, 0, 0 },
|
|
{ NULL, 0, 0 },
|
|
{ "CAPTION_525", VBI_SLICED_CAPTION_525, 2 },
|
|
};
|
|
|
|
static void
|
|
st_printf (struct stream * st,
|
|
const char * templ,
|
|
...)
|
|
{
|
|
va_list ap;
|
|
int n;
|
|
|
|
va_start (ap, templ);
|
|
n = vsnprintf ((char *) st->buffer, sizeof (st->buffer), templ, ap);
|
|
va_end (ap);
|
|
|
|
if (n < 1 || n >= (int) sizeof (st->buffer))
|
|
error_exit (_("Buffer overflow."));
|
|
|
|
if (n != write (st->fd, st->buffer, n))
|
|
write_error_exit (/* msg: errno */ NULL);
|
|
}
|
|
|
|
static const uint8_t
|
|
base64 [] = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
"0123456789+/");
|
|
|
|
static void
|
|
encode_base64 (uint8_t * out,
|
|
const uint8_t * in,
|
|
unsigned int n_bytes)
|
|
{
|
|
unsigned int block;
|
|
|
|
for (; n_bytes >= 3; n_bytes -= 3) {
|
|
block = in[0] * 65536 + in[1] * 256 + in[2];
|
|
in += 3;
|
|
out[0] = base64[block >> 18];
|
|
out[1] = base64[(block >> 12) & 0x3F];
|
|
out[2] = base64[(block >> 6) & 0x3F];
|
|
out[3] = base64[block & 0x3F];
|
|
out += 4;
|
|
}
|
|
|
|
switch (n_bytes) {
|
|
case 2:
|
|
block = in[0] * 256 + in[1];
|
|
out[0] = base64[block >> 10];
|
|
out[1] = base64[(block >> 4) & 0x3F];
|
|
out[2] = base64[(block << 2) & 0x3F];
|
|
out[3] = '=';
|
|
out += 4;
|
|
break;
|
|
|
|
case 1:
|
|
block = in[0];
|
|
out[0] = base64[block >> 2];
|
|
out[1] = base64[(block << 4) & 0x3F];
|
|
out[2] = '=';
|
|
out[3] = '=';
|
|
out += 4;
|
|
break;
|
|
}
|
|
|
|
*out = 0;
|
|
}
|
|
|
|
static void
|
|
write_xml_sliced (struct stream * st,
|
|
const vbi_sliced * sliced,
|
|
unsigned int n_lines)
|
|
{
|
|
const vbi_sliced *sliced_end;
|
|
|
|
sliced_end = sliced + n_lines;
|
|
|
|
assert (N_ELEMENTS (st->b64_buffer)
|
|
>= (sizeof (sliced->data) + 2) * 4 / 3 + 1);
|
|
|
|
for (; sliced < sliced_end; ++sliced) {
|
|
unsigned int i;
|
|
|
|
if (VBI_SLICED_VBI_525 == sliced->id
|
|
|| VBI_SLICED_VBI_625 == sliced->id)
|
|
continue;
|
|
|
|
for (i = 0; i < N_ELEMENTS (service_map); ++i) {
|
|
if (sliced->id & service_map[i].id)
|
|
break;
|
|
}
|
|
|
|
if (i >= N_ELEMENTS (service_map))
|
|
error_exit (_("Unknown data service."));
|
|
|
|
assert (service_map[i].n_bytes
|
|
<= sizeof (sliced->data));
|
|
|
|
encode_base64 (st->b64_buffer,
|
|
sliced->data,
|
|
service_map[i].n_bytes);
|
|
|
|
if (0 == sliced->line) {
|
|
st_printf (st,
|
|
"<vbi-sliced service=\"%s\""
|
|
">%s</vbi-sliced>\n",
|
|
service_map[i].name,
|
|
st->b64_buffer);
|
|
} else {
|
|
st_printf (st,
|
|
"<vbi-sliced service=\"%s\" "
|
|
"line=\"%u\">%s</vbi-sliced>\n",
|
|
service_map[i].name,
|
|
sliced->line,
|
|
st->b64_buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
write_xml_raw (struct stream * st,
|
|
const uint8_t * raw,
|
|
const vbi_sampling_par *sp)
|
|
{
|
|
const char *format;
|
|
unsigned int n_samples;
|
|
unsigned int n_rows;
|
|
unsigned int row;
|
|
|
|
assert ((N_ELEMENTS (st->b64_buffer) - 1) * 3 / 4 - 2
|
|
>= (unsigned int) sp->sp_samples_per_line);
|
|
|
|
format = vbi_pixfmt_name (sp->sp_sample_format);
|
|
|
|
if (NULL == format)
|
|
error_exit (_("Unknown raw VBI format."));
|
|
|
|
n_samples = sp->sp_samples_per_line
|
|
* vbi_pixfmt_bytes_per_pixel (sp->sp_sample_format);
|
|
|
|
n_rows = sp->count[0] + sp->count[1];
|
|
|
|
if (sp->interlaced)
|
|
assert (sp->count[0] == sp->count[1]);
|
|
|
|
for (row = 0; row < n_rows; ++row) {
|
|
unsigned int line;
|
|
|
|
if (sp->interlaced) {
|
|
line = sp->start[row & 1];
|
|
if (line > 0)
|
|
line += row >> 1;
|
|
} else if (row < (unsigned int) sp->count[0]) {
|
|
line = sp->start[0];
|
|
if (line > 0)
|
|
line += row;
|
|
} else {
|
|
line = sp->start[1];
|
|
if (line > 0)
|
|
line += row - sp->count[0];
|
|
}
|
|
|
|
encode_base64 (st->b64_buffer, raw, n_samples);
|
|
|
|
if (0 == line) {
|
|
st_printf (st,
|
|
"<vbi-raw format=\"%s\" "
|
|
"sampling-rate=\"%u\" "
|
|
"offset=\"%u\">%s</vbi-raw>\n",
|
|
format,
|
|
sp->sampling_rate,
|
|
sp->offset,
|
|
st->b64_buffer);
|
|
} else {
|
|
st_printf (st,
|
|
"<vbi-raw format=\"%s\" "
|
|
"sampling-rate=\"%u\" "
|
|
"offset=\"%u\" "
|
|
"line=\"%u\">%s</vbi-raw>\n",
|
|
format,
|
|
sp->sampling_rate,
|
|
sp->offset,
|
|
line,
|
|
st->b64_buffer);
|
|
}
|
|
|
|
raw += sp->bytes_per_line;
|
|
}
|
|
}
|
|
|
|
static vbi_bool
|
|
write_func_xml (struct stream * st,
|
|
const vbi_sliced * sliced,
|
|
unsigned int n_lines,
|
|
const uint8_t * raw,
|
|
const vbi_sampling_par *sp,
|
|
double sample_time,
|
|
int64_t stream_time)
|
|
{
|
|
struct timeval tv;
|
|
double intpart;
|
|
|
|
if (NULL == sliced && NULL == raw)
|
|
return TRUE;
|
|
|
|
if (NULL != sliced)
|
|
assert (n_lines <= st->system);
|
|
|
|
if (NULL != raw)
|
|
assert (NULL != sp);
|
|
|
|
assert (sample_time >= 0);
|
|
assert (stream_time >= 0);
|
|
|
|
tv.tv_usec = (int)(1e6 * modf (sample_time, &intpart));
|
|
tv.tv_sec = (int) intpart;
|
|
|
|
st_printf (st,
|
|
"<frame video-standard=\"%s\" "
|
|
"sample-time=\"%" PRId64 ".%06u\" "
|
|
"stream-time=\"%" PRId64 "\">\n",
|
|
(525 == st->system) ? "525_60" : "625_50",
|
|
(int64_t) tv.tv_sec,
|
|
(unsigned int) tv.tv_usec,
|
|
stream_time);
|
|
|
|
if (NULL != sliced)
|
|
write_xml_sliced (st, sliced, n_lines);
|
|
|
|
if (NULL != raw)
|
|
write_xml_raw (st, raw, sp);
|
|
|
|
st_printf (st, "</frame>\n");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static vbi_bool
|
|
write_func_old_sliced (struct stream * st,
|
|
const vbi_sliced * sliced,
|
|
unsigned int n_lines,
|
|
const uint8_t * raw,
|
|
const vbi_sampling_par *sp,
|
|
double sample_time,
|
|
int64_t stream_time)
|
|
{
|
|
if (NULL == sliced && NULL == raw)
|
|
return TRUE;
|
|
|
|
if (NULL != sliced)
|
|
assert (n_lines <= 254);
|
|
else
|
|
n_lines = 0;
|
|
|
|
if (NULL != raw)
|
|
assert (NULL != sp);
|
|
|
|
stream_time = stream_time;
|
|
|
|
st_printf (st,
|
|
"%f\n%c",
|
|
sample_time - st->sample_time,
|
|
n_lines + (NULL != raw));
|
|
|
|
while (n_lines > 0) {
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < N_ELEMENTS (service_map); ++i) {
|
|
if (sliced->id & service_map[i].id) {
|
|
int n;
|
|
|
|
st_printf (st, "%c%c%c",
|
|
/* service number */ i,
|
|
/* line number low/high */
|
|
sliced->line & 0xFF,
|
|
sliced->line >> 8);
|
|
|
|
n = service_map[i].n_bytes;
|
|
assert (n > 0
|
|
&& n <= (int) sizeof (sliced->data));
|
|
|
|
if (n != write (st->fd, sliced->data, n))
|
|
write_error_exit (NULL);
|
|
}
|
|
}
|
|
|
|
++sliced;
|
|
--n_lines;
|
|
}
|
|
|
|
if (NULL != raw) {
|
|
uint8_t *p = st->b64_buffer;
|
|
int n;
|
|
|
|
#define w8(n) *p++ = n
|
|
#define w16(n) w8 ((n) & 0xFF); w8 (((n) >> 8) & 0xFF)
|
|
#define w32(n) w16 ((n) & 0xFFFF); w16 (((n) >> 16) & 0xFFFF)
|
|
|
|
w8 (255); /* service number */
|
|
w16 (0); /* line number */
|
|
w16 (st->system);
|
|
w32 (sp->sampling_rate);
|
|
w16 (sp->sp_samples_per_line);
|
|
w16 (sp->bytes_per_line);
|
|
w16 (sp->offset);
|
|
w16 (sp->start[0]);
|
|
w16 (sp->start[1]);
|
|
w16 (sp->count[0]);
|
|
w16 (sp->count[1]);
|
|
w8 (sp->interlaced);
|
|
w8 (sp->synchronous);
|
|
#undef w8
|
|
#undef w16
|
|
#undef w32
|
|
n = p - st->b64_buffer;
|
|
assert (n > 0 && n <= (int) sizeof (st->b64_buffer));
|
|
|
|
if (n != write (st->fd, st->b64_buffer, n))
|
|
write_error_exit (NULL);
|
|
|
|
n = (sp->count[0] + sp->count[1]) * sp->bytes_per_line;
|
|
assert (n > 0 && n <= 625 * 4096);
|
|
|
|
if (n != write (st->fd, raw, n))
|
|
write_error_exit (NULL);
|
|
}
|
|
|
|
st->sample_time = sample_time;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
vbi_bool
|
|
write_stream_sliced (struct stream * st,
|
|
const vbi_sliced * sliced,
|
|
unsigned int n_lines,
|
|
const uint8_t * raw,
|
|
const vbi_sampling_par *sp,
|
|
double sample_time,
|
|
int64_t stream_time)
|
|
{
|
|
return st->write_func (st, sliced, n_lines,
|
|
raw, sp,
|
|
sample_time, stream_time);
|
|
}
|
|
|
|
void
|
|
write_stream_set_data_identifier
|
|
(struct stream * st,
|
|
unsigned int data_identifier)
|
|
{
|
|
assert (NULL != st->mx);
|
|
|
|
if (!vbi_dvb_mux_set_data_identifier (st->mx, data_identifier)) {
|
|
error_exit (_("Invalid data identifier 0x%x."),
|
|
data_identifier);
|
|
}
|
|
}
|
|
|
|
void
|
|
write_stream_set_pes_packet_size
|
|
(struct stream * st,
|
|
unsigned int min,
|
|
unsigned int max)
|
|
{
|
|
assert (NULL != st->mx);
|
|
|
|
if (!vbi_dvb_mux_set_pes_packet_size (st->mx, min, max))
|
|
no_mem_exit ();
|
|
}
|
|
|
|
struct stream *
|
|
write_stream_new (const char * file_name,
|
|
enum file_format file_format,
|
|
unsigned int ts_pid,
|
|
unsigned int system)
|
|
{
|
|
struct stream *st;
|
|
|
|
assert (525 == system || 625 == system);
|
|
|
|
st = calloc (1, sizeof (*st));
|
|
if (NULL == st)
|
|
no_mem_exit ();
|
|
|
|
if (NULL == file_name
|
|
|| 0 == strcmp (file_name, "-")) {
|
|
st->fd = STDOUT_FILENO;
|
|
|
|
if (isatty (STDOUT_FILENO)) {
|
|
error_exit (_("Output of this program is binary "
|
|
"data. You should pipe it to another "
|
|
"tool or redirect to a file.\n"));
|
|
}
|
|
} else {
|
|
st->fd = open (file_name,
|
|
O_WRONLY | O_CREAT | O_EXCL,
|
|
(S_IRUSR | S_IWUSR |
|
|
S_IRGRP | S_IWGRP |
|
|
S_IROTH | S_IWOTH));
|
|
if (-1 == st->fd) {
|
|
error_exit (_("Cannot open '%s' for writing: %s."),
|
|
file_name, strerror (errno));
|
|
}
|
|
|
|
st->close_fd = TRUE;
|
|
}
|
|
|
|
switch (file_format) {
|
|
case FILE_FORMAT_SLICED:
|
|
st->write_func = write_func_old_sliced;
|
|
break;
|
|
|
|
case FILE_FORMAT_XML:
|
|
st->write_func = write_func_xml;
|
|
break;
|
|
|
|
case FILE_FORMAT_DVB_PES:
|
|
st->write_func = write_func_pes_ts;
|
|
|
|
st->mx = vbi_dvb_pes_mux_new (pes_ts_cb,
|
|
/* user_data */ st);
|
|
if (NULL == st->mx)
|
|
no_mem_exit ();
|
|
|
|
break;
|
|
|
|
case FILE_FORMAT_DVB_TS:
|
|
st->write_func = write_func_pes_ts;
|
|
|
|
st->mx = vbi_dvb_ts_mux_new (ts_pid,
|
|
pes_ts_cb,
|
|
/* user_data */ st);
|
|
if (NULL == st->mx)
|
|
no_mem_exit ();
|
|
|
|
break;
|
|
|
|
default:
|
|
error_exit (_("Unknown output file format."));
|
|
break;
|
|
}
|
|
|
|
st->sample_time = 0.0;
|
|
st->stream_time = 0;
|
|
|
|
st->system = system;
|
|
|
|
return st;
|
|
}
|
|
|
|
static vbi_bool
|
|
read_more (struct stream * st)
|
|
{
|
|
unsigned int retry;
|
|
uint8_t *s;
|
|
uint8_t *e;
|
|
|
|
s = /* const cast */ st->end;
|
|
e = st->buffer + sizeof (st->buffer);
|
|
printf("111read_more %d \n",sizeof (st->buffer));
|
|
if (s >= e)
|
|
s = st->buffer;
|
|
|
|
//**********************************get buffer way***********
|
|
return TRUE;
|
|
//**********************************finish**********
|
|
retry = 100;
|
|
|
|
do {
|
|
ssize_t actual;
|
|
int saved_errno;
|
|
|
|
actual = read (st->fd, s, e - s);
|
|
if (0 == actual)
|
|
return FALSE; /* EOF */
|
|
printf("111read_more actual %d \n",actual);
|
|
if (actual > 0) {
|
|
st->bp = s;
|
|
st->end = s + actual;
|
|
return TRUE;
|
|
}
|
|
|
|
saved_errno = errno;
|
|
|
|
if (EINTR != saved_errno) {
|
|
read_error_exit (/* msg: errno */ NULL);
|
|
}
|
|
} while (--retry > 0);
|
|
|
|
read_error_exit (/* msg: errno */ NULL);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static vbi_bool
|
|
read_loop_pes_ts (struct stream * st)
|
|
{
|
|
for (;;) {
|
|
double sample_time;
|
|
int64_t pts;
|
|
unsigned int left;
|
|
unsigned int n_lines;
|
|
|
|
if (st->bp >= st->end) {
|
|
if (!read_more (st))
|
|
break; /* EOF */
|
|
}
|
|
|
|
left = st->end - st->bp;
|
|
|
|
n_lines = vbi_dvb_demux_cor (st->dx,
|
|
st->sliced,
|
|
N_ELEMENTS (st->sliced),
|
|
&pts,
|
|
&st->bp,
|
|
&left);
|
|
|
|
if (0 == n_lines)
|
|
continue;
|
|
|
|
if (pts < 0) {
|
|
/* XXX WTF? */
|
|
continue;
|
|
}
|
|
|
|
sample_time = pts * (1 / 90000.0);
|
|
|
|
if (!st->callback (st->sliced, n_lines,
|
|
/* raw */ NULL,
|
|
/* sp */ NULL,
|
|
sample_time, pts,NULL))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static vbi_bool
|
|
next_byte (struct stream * st,
|
|
int * c)
|
|
{
|
|
do {
|
|
|
|
if (st->bp < st->end) {
|
|
*c = *st->bp++;
|
|
printf("next_byte %x\n",*c);
|
|
return TRUE;
|
|
}
|
|
} while (read_more (st));
|
|
|
|
return FALSE; /* EOF */
|
|
}
|
|
|
|
static void
|
|
next_block (struct stream * st,
|
|
uint8_t * buffer,
|
|
unsigned int buffer_size)
|
|
{
|
|
do {
|
|
unsigned int available;
|
|
|
|
available = st->end - st->bp;
|
|
|
|
if (buffer_size <= available) {
|
|
memcpy (buffer, st->bp, buffer_size);
|
|
st->bp += buffer_size;
|
|
return;
|
|
}
|
|
|
|
memcpy (buffer, st->bp, available);
|
|
|
|
st->bp += available;
|
|
|
|
buffer += available;
|
|
buffer_size -= available;
|
|
|
|
} while (read_more (st));
|
|
|
|
premature_exit ();
|
|
}
|
|
|
|
static uint8_t *
|
|
next_raw_data (struct stream * st,
|
|
vbi_sampling_par * sp)
|
|
{
|
|
uint8_t sp_buffer[32];
|
|
unsigned int system;
|
|
unsigned int size;
|
|
uint8_t *p;
|
|
|
|
next_block (st, sp_buffer, 22);
|
|
|
|
p = sp_buffer;
|
|
|
|
CLEAR (*sp);
|
|
|
|
#define r8(v) v = *p++
|
|
#define r16(v) v = p[0] | (p[1] << 8); p += 2
|
|
#define r32(v) v = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); p += 4
|
|
r16 (system);
|
|
sp->sp_sample_format = VBI_PIXFMT_Y8;
|
|
r32 (sp->sampling_rate);
|
|
r16 (sp->sp_samples_per_line);
|
|
r16 (sp->bytes_per_line);
|
|
r16 (sp->offset);
|
|
r16 (sp->start[0]);
|
|
r16 (sp->start[1]);
|
|
r16 (sp->count[0]);
|
|
r16 (sp->count[1]);
|
|
r8 (sp->interlaced);
|
|
r8 (sp->synchronous);
|
|
#undef r8
|
|
#undef r16
|
|
#undef r32
|
|
|
|
assert (22 == p - sp_buffer);
|
|
|
|
#if 2 == VBI_VERSION_MINOR
|
|
|
|
switch (system) {
|
|
case 525:
|
|
case 625:
|
|
sp->scanning = system;
|
|
|
|
default:
|
|
bad_format_exit ();
|
|
break;
|
|
}
|
|
|
|
#elif 3 == VBI_VERSION_MINOR
|
|
|
|
switch (system) {
|
|
case 525:
|
|
sp->videostd_set = VBI_VIDEOSTD_SET_525_60;
|
|
break;
|
|
|
|
case 625:
|
|
sp->videostd_set = VBI_VIDEOSTD_SET_625_50;
|
|
break;
|
|
|
|
default:
|
|
bad_format_exit ();
|
|
break;
|
|
}
|
|
|
|
#else
|
|
# error VBI_VERSION_MINOR == ?
|
|
#endif
|
|
|
|
size = (sp->count[0] + sp->count[1]) * sp->bytes_per_line;
|
|
|
|
p = malloc (size);
|
|
if (NULL == p)
|
|
no_mem_exit ();
|
|
|
|
next_block (st, p, size);
|
|
|
|
#if 3 == VBI_VERSION_MINOR
|
|
|
|
if (st->debug) {
|
|
if (NULL == st->rd) {
|
|
vbi_bool success;
|
|
|
|
st->rd = vbi_raw_decoder_new (sp);
|
|
if (NULL == st->rd)
|
|
return p;
|
|
vbi_raw_decoder_add_services (st->rd, -1, 1);
|
|
success = vbi_raw_decoder_debug (st->rd, TRUE);
|
|
assert (success);
|
|
}
|
|
|
|
st->sliced2_lines =
|
|
vbi_raw_decoder_decode (st->rd,
|
|
st->sliced2,
|
|
N_ELEMENTS (st->sliced2),
|
|
p);
|
|
}
|
|
|
|
#endif /* 3 == VBI_VERSION_MINOR */
|
|
|
|
return p;
|
|
}
|
|
|
|
static vbi_bool
|
|
next_time_delta (struct stream * st,
|
|
double * dt)
|
|
{
|
|
char buffer[32];
|
|
unsigned int i;
|
|
printf("start next_time_delta");
|
|
for (i = 0; i < N_ELEMENTS (buffer); ++i) {
|
|
int c;
|
|
|
|
if (!next_byte (st, &c)) {
|
|
if (i > 0)
|
|
premature_exit ();
|
|
else
|
|
return FALSE;
|
|
}
|
|
printf("cccccccccc = %x i= %d \n",c,i);
|
|
if ('\n' == c) {
|
|
if (0 == i) {
|
|
bad_format_exit ();
|
|
} else {
|
|
buffer[i] = 0;
|
|
*dt = strtod (buffer, NULL);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if ('-' != c && '.' != c && !isdigit (c))
|
|
bad_format_exit ();
|
|
|
|
buffer[i] = c;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static vbi_bool
|
|
read_loop_old_sliced (struct stream * st)
|
|
{
|
|
printf("start read_loop_old_sliced ");
|
|
for (;;) {
|
|
vbi_sliced *s;
|
|
uint8_t *raw;
|
|
vbi_sampling_par sp;
|
|
double dt;
|
|
vbi_bool success;
|
|
int n_lines;
|
|
int count;
|
|
|
|
//*************temp cancel********
|
|
//if (!next_time_delta (st, &dt))
|
|
// break; /* EOF */
|
|
//*************finish*************
|
|
|
|
/* Time in seconds since last frame. */
|
|
if (dt < 0.0)
|
|
dt = -dt;
|
|
|
|
st->sample_time += dt;
|
|
|
|
printf("st->sample_time = %lf\n", st->sample_time);
|
|
|
|
n_lines = (st->end - st->bp) / 8; //8 is hardware struct length
|
|
|
|
|
|
//if (!next_byte (st, &n_lines))
|
|
// bad_format_exit ();
|
|
printf("n_lines = %d",n_lines);
|
|
if ((unsigned int) n_lines > N_ELEMENTS (st->sliced))
|
|
n_lines = N_ELEMENTS (st->sliced); //bad_format_exit ();
|
|
if (n_lines == 0)
|
|
break;
|
|
|
|
|
|
s = st->sliced;
|
|
raw = NULL;
|
|
st->raw_valid = FALSE;
|
|
|
|
for (count = n_lines; count > 0; --count) {
|
|
int index;
|
|
int field_id;
|
|
int nbytes;
|
|
int line;
|
|
|
|
|
|
|
|
if (!next_byte (st, &index))
|
|
premature_exit ();
|
|
|
|
if (!next_byte (st, &field_id))
|
|
premature_exit ();
|
|
|
|
if (!next_byte (st, &nbytes))
|
|
premature_exit ();
|
|
|
|
if (!next_byte (st, &nbytes))
|
|
premature_exit ();
|
|
s->line += (line & 15) * 256;
|
|
|
|
if (!next_byte (st, &line))
|
|
premature_exit ();
|
|
s->line = line;
|
|
|
|
if (!next_byte (st, &line))
|
|
premature_exit ();
|
|
s->line += (line & 15) * 256;
|
|
|
|
printf(" index= %d, field_id =%d ,nbytes =%d ,line = %d \n,",index,field_id,nbytes,s->line);
|
|
//****************************************change if 284 = 21 else ....
|
|
if (s->line == 284)
|
|
s->line = 21;
|
|
else
|
|
if (s->line == 21)
|
|
s->line = 284;
|
|
//****************************************finish **************
|
|
switch (index) {
|
|
|
|
case 0:
|
|
s->id = VBI_SLICED_CAPTION_525;
|
|
next_block (st, s->data, 2);
|
|
printf(" s->data[0] =%d ,s->data[1] = %d \n",s->data[0],s->data[1]);
|
|
break;
|
|
|
|
case 1:
|
|
s->id = VBI_SLICED_TELETEXT_B;
|
|
next_block (st, s->data, 42);
|
|
break;
|
|
|
|
case 2:
|
|
s->id = VBI_SLICED_CAPTION_625;
|
|
next_block (st, s->data, 2);
|
|
break;
|
|
|
|
case 3:
|
|
s->id = VBI_SLICED_VPS;
|
|
next_block (st, s->data, 13);
|
|
break;
|
|
|
|
case 4:
|
|
s->id = VBI_SLICED_WSS_625;
|
|
next_block (st, s->data, 2);
|
|
break;
|
|
|
|
case 5:
|
|
s->id = VBI_SLICED_WSS_CPR1204;
|
|
next_block (st, s->data, 3);
|
|
break;
|
|
|
|
|
|
|
|
case 255:
|
|
raw = next_raw_data (st, &sp);
|
|
st->raw_valid = TRUE;
|
|
break;
|
|
|
|
default:
|
|
bad_format_exit ();
|
|
break;
|
|
}
|
|
|
|
++s;
|
|
}
|
|
|
|
st->stream_time = st->sample_time * 90000;
|
|
|
|
if (st->raw_valid && st->decode_raw) {
|
|
printf("st->raw_valid && st->decode_raw is true\n");
|
|
success = st->callback (st->sliced2,
|
|
st->sliced2_lines,
|
|
raw, &sp,
|
|
st->sample_time,
|
|
st->stream_time,
|
|
st->userdata);
|
|
} else {
|
|
printf("st->raw_valid && st->decode_raw isnot true\n");
|
|
|
|
success = st->callback (st->sliced, n_lines,
|
|
raw, &sp,
|
|
st->sample_time,
|
|
st->stream_time,
|
|
st->userdata);
|
|
}
|
|
|
|
free (raw);
|
|
raw = NULL;
|
|
|
|
if (!success)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static vbi_bool
|
|
look_ahead (struct stream * st,
|
|
unsigned int n_bytes)
|
|
{
|
|
assert (n_bytes <= sizeof (st->buffer));
|
|
|
|
do {
|
|
unsigned int available;
|
|
const uint8_t *end;
|
|
|
|
available = st->end - st->bp;
|
|
if (available >= n_bytes)
|
|
return TRUE;
|
|
|
|
end = st->buffer + sizeof (st->buffer);
|
|
|
|
if (n_bytes > (unsigned int)(end - st->bp)) {
|
|
memmove (st->buffer, st->bp, available);
|
|
|
|
st->bp = st->buffer;
|
|
st->end = st->buffer + available;
|
|
}
|
|
} while (read_more (st));
|
|
|
|
return FALSE; /* EOF */
|
|
}
|
|
|
|
static vbi_bool
|
|
is_old_sliced_format (const uint8_t s[8])
|
|
{
|
|
unsigned int i;
|
|
|
|
if ('0' != s[0] || '.' != s[1])
|
|
return FALSE;
|
|
|
|
for (i = 2; i < 8; ++i) {
|
|
if (!isdigit (s[i]))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static vbi_bool
|
|
is_xml_format (const uint8_t s[6])
|
|
{
|
|
unsigned int i;
|
|
|
|
if ('<' != s[0])
|
|
return FALSE;
|
|
|
|
for (i = 1; i < 6; ++i) {
|
|
if (!isalpha (s[i]))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static vbi_bool
|
|
is_pes_format (const uint8_t s[4])
|
|
{
|
|
return (0x00 == s[0] &&
|
|
0x00 == s[1] &&
|
|
0x01 == s[2] &&
|
|
0xBD == s[3]);
|
|
}
|
|
|
|
static vbi_bool
|
|
is_ts_format (const uint8_t s[1])
|
|
{
|
|
return (0x47 == s[0]);
|
|
}
|
|
|
|
static enum file_format
|
|
detect_file_format (struct stream * st)
|
|
{
|
|
if (!look_ahead (st, 8))
|
|
return 0; /* unknown format */
|
|
|
|
if (is_old_sliced_format (st->bp))
|
|
return FILE_FORMAT_SLICED;
|
|
|
|
if (is_xml_format (st->buffer))
|
|
return FILE_FORMAT_XML;
|
|
|
|
/* Can/shall we guess a PID? */
|
|
if (0) {
|
|
/* Somewhat unreliable and works only if the
|
|
packets are aligned. */
|
|
if (is_ts_format (st->buffer))
|
|
return FILE_FORMAT_DVB_TS;
|
|
}
|
|
|
|
/* Works only if the packets are aligned. */
|
|
if (is_pes_format (st->buffer))
|
|
return FILE_FORMAT_DVB_PES;
|
|
|
|
return 0; /* unknown format */
|
|
}
|
|
|
|
struct stream *
|
|
read_stream_new (const char * fbuffer,
|
|
int length,
|
|
enum file_format file_format,
|
|
unsigned int ts_pid,
|
|
stream_callback_fn * callback ,
|
|
void *userdata)
|
|
{
|
|
struct stream *st;
|
|
|
|
st = calloc (1, sizeof (*st));
|
|
if (NULL == st)
|
|
no_mem_exit ();
|
|
|
|
/*if (NULL == file_name
|
|
|| 0 == strcmp (file_name, "-")) {
|
|
st->fd = STDIN_FILENO;
|
|
|
|
if (isatty (STDIN_FILENO))
|
|
error_exit (_("No VBI data on standard input."));
|
|
} else {
|
|
st->fd = open (file_name, O_RDONLY, 0);
|
|
if (-1 == st->fd) {
|
|
error_exit (_("Cannot open '%s' for reading: %s."),
|
|
file_name, strerror (errno));
|
|
}
|
|
|
|
st->close_fd = TRUE;
|
|
}*/
|
|
|
|
|
|
|
|
if (0 == file_format)
|
|
file_format = detect_file_format (st);
|
|
|
|
switch (file_format) {
|
|
case FILE_FORMAT_SLICED:
|
|
st->loop = read_loop_old_sliced;
|
|
break;
|
|
|
|
case FILE_FORMAT_XML:
|
|
st->loop = NULL;
|
|
error_exit ("XML read function "
|
|
"not implemented yet.");
|
|
break;
|
|
|
|
case FILE_FORMAT_DVB_PES:
|
|
st->loop = read_loop_pes_ts;
|
|
|
|
st->dx = vbi_dvb_pes_demux_new (/* callback */ NULL,
|
|
/* user_data */ NULL);
|
|
if (NULL == st->dx)
|
|
no_mem_exit ();
|
|
|
|
break;
|
|
|
|
case FILE_FORMAT_DVB_TS:
|
|
st->loop = read_loop_pes_ts;
|
|
|
|
st->dx = _vbi_dvb_ts_demux_new (/* callback */ NULL,
|
|
/* user_data */ NULL,
|
|
ts_pid);
|
|
if (NULL == st->dx)
|
|
no_mem_exit ();
|
|
|
|
break;
|
|
|
|
default:
|
|
error_exit (_("Unknown input file format."));
|
|
break;
|
|
}
|
|
|
|
st->callback = callback;
|
|
|
|
st->sample_time = 0.0;
|
|
st->stream_time = 0;
|
|
|
|
st->bp = st->buffer;
|
|
st->end = st->buffer;
|
|
|
|
printf("finish read_stream_new" );
|
|
|
|
//***************************************add buffer*********
|
|
memset (st->buffer, 0,sizeof (st->buffer));
|
|
memcpy (st->buffer,fbuffer, length);
|
|
st->end = st->buffer + length;
|
|
st->userdata = userdata;
|
|
//printf("st->bp,st->end,length === %d %d %d",st->bp,st->end,length );
|
|
//***************************************finish*********
|
|
return st;
|
|
}
|
|
|
|
static vbi_bool
|
|
capture_loop (struct stream * st)
|
|
{
|
|
struct timeval timeout;
|
|
|
|
timeout.tv_sec = 2;
|
|
timeout.tv_usec = 0;
|
|
|
|
for (;;) {
|
|
vbi_capture_buffer *raw_buffer;
|
|
vbi_capture_buffer *sliced_buffer;
|
|
uint8_t *raw;
|
|
vbi_sliced *sliced;
|
|
int n_lines;
|
|
double sample_time;
|
|
int64_t stream_time;
|
|
int r;
|
|
|
|
if (st->read_not_pull) {
|
|
r = vbi_capture_read (st->cap,
|
|
st->raw,
|
|
st->sliced,
|
|
&n_lines,
|
|
&sample_time,
|
|
&timeout);
|
|
} else {
|
|
r = vbi_capture_pull (st->cap,
|
|
&raw_buffer,
|
|
&sliced_buffer,
|
|
&timeout);
|
|
}
|
|
|
|
switch (r) {
|
|
case -1:
|
|
read_error_exit (/* msg: errno */ NULL);
|
|
|
|
case 0:
|
|
error_exit (_("Read timeout."));
|
|
|
|
case 1:
|
|
break;
|
|
|
|
default:
|
|
assert (0);
|
|
}
|
|
|
|
if (st->read_not_pull) {
|
|
raw = st->raw;
|
|
sliced = st->sliced;
|
|
} else {
|
|
if (NULL != raw_buffer)
|
|
raw = raw_buffer->data;
|
|
else
|
|
raw = NULL;
|
|
|
|
sliced = sliced_buffer->data;
|
|
n_lines = sliced_buffer->size / sizeof (vbi_sliced);
|
|
|
|
sample_time = sliced_buffer->timestamp;
|
|
}
|
|
|
|
if (st->interfaces & INTERFACE_DVB) {
|
|
stream_time = vbi_capture_dvb_last_pts (st->cap);
|
|
} else {
|
|
stream_time = sample_time * 90000;
|
|
}
|
|
|
|
if (!st->callback (sliced, n_lines, raw, &st->sp,
|
|
sample_time, stream_time,NULL))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
capture_stream_sim_set_flags (struct stream * st,
|
|
unsigned int flags)
|
|
{
|
|
if (NULL == st->cap)
|
|
return;
|
|
|
|
if (st->interfaces & INTERFACE_SIM) {
|
|
_vbi_capture_sim_set_flags (st->cap, flags);
|
|
|
|
if (flags & _VBI_RAW_NOISE_2) {
|
|
vbi_capture_sim_add_noise (st->cap,
|
|
/* min_freq */ 0,
|
|
/* max_freq */ 5000000,
|
|
/* amplitude */ 25);
|
|
} else {
|
|
vbi_capture_sim_add_noise (st->cap, 0, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
capture_stream_sim_decode_raw (struct stream * st,
|
|
vbi_bool enable)
|
|
{
|
|
st->decode_raw = !!enable;
|
|
|
|
if (NULL == st->cap)
|
|
return;
|
|
|
|
if (st->interfaces & INTERFACE_SIM)
|
|
vbi_capture_sim_decode_raw (st->cap, enable);
|
|
}
|
|
|
|
vbi_bool
|
|
capture_stream_sim_load_caption (struct stream * st,
|
|
const char * stream,
|
|
vbi_bool append)
|
|
{
|
|
if (NULL == st->cap)
|
|
return FALSE;
|
|
|
|
if (0 == (st->interfaces & INTERFACE_SIM))
|
|
return FALSE;
|
|
|
|
#if 2 == VBI_VERSION_MINOR
|
|
stream = stream; /* unused */
|
|
append = append;
|
|
|
|
return FALSE;
|
|
#elif 3 == VBI_VERSION_MINOR
|
|
return vbi_capture_sim_load_caption (st->cap, stream, append);
|
|
#else
|
|
# error VBI_VERSION_MINOR == ?
|
|
#endif
|
|
}
|
|
|
|
vbi_bool
|
|
capture_stream_get_point (struct stream * st,
|
|
vbi_bit_slicer_point *point,
|
|
unsigned int row,
|
|
unsigned int nth_bit)
|
|
{
|
|
#if 2 == VBI_VERSION_MINOR
|
|
st = st; /* unused */
|
|
point = point;
|
|
row = row;
|
|
nth_bit = nth_bit;
|
|
|
|
return FALSE;
|
|
#elif 3 == VBI_VERSION_MINOR
|
|
if (NULL != st->cap) {
|
|
return vbi_capture_sampling_point
|
|
(st->cap, point, row, nth_bit);
|
|
} else if (NULL != st->rd) {
|
|
if (!st->raw_valid)
|
|
return FALSE;
|
|
return vbi_raw_decoder_sampling_point
|
|
(st->rd, point, row, nth_bit);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
#else
|
|
# error VBI_VERSION_MINOR == ?
|
|
#endif
|
|
}
|
|
|
|
vbi_bool
|
|
capture_stream_debug (struct stream * st,
|
|
vbi_bool enable)
|
|
{
|
|
#if 2 == VBI_VERSION_MINOR
|
|
st = st; /* unused */
|
|
enable = enable;
|
|
|
|
return FALSE;
|
|
#elif 3 == VBI_VERSION_MINOR
|
|
st->debug = TRUE;
|
|
|
|
if (NULL != st->cap) {
|
|
return vbi_capture_debug (st->cap, enable);
|
|
} else if (NULL != st->rd) {
|
|
return vbi_raw_decoder_debug (st->rd, enable);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
#else
|
|
# error VBI_VERSION_MINOR == ?
|
|
#endif
|
|
}
|
|
|
|
void
|
|
capture_stream_get_sampling_par (struct stream * st,
|
|
vbi_sampling_par * sp)
|
|
{
|
|
assert (NULL != sp);
|
|
|
|
*sp = st->sp;
|
|
}
|
|
|
|
static void
|
|
capture_error_msg (const char * interface_name,
|
|
const char * errstr)
|
|
{
|
|
error_msg (_("Cannot capture VBI data "
|
|
"with %s interface: %s."),
|
|
interface_name, errstr);
|
|
}
|
|
|
|
struct stream *
|
|
capture_stream_new (unsigned int interfaces,
|
|
const char * dev_name,
|
|
unsigned int system,
|
|
vbi_service_set services,
|
|
unsigned int n_buffers,
|
|
unsigned int ts_pid,
|
|
vbi_bool sim_interlaced,
|
|
vbi_bool sim_synchronous,
|
|
vbi_bool capture_raw_data,
|
|
vbi_bool read_not_pull,
|
|
unsigned int strict,
|
|
stream_callback_fn * callback)
|
|
{
|
|
struct stream *st;
|
|
vbi_bool trace;
|
|
|
|
assert (0 != interfaces);
|
|
assert (525 == system || 625 == system);
|
|
assert (0 != services);
|
|
assert (NULL != callback);
|
|
|
|
st = calloc (1, sizeof (*st));
|
|
if (NULL == st)
|
|
no_mem_exit ();
|
|
|
|
trace = (0 != (option_log_mask & VBI_LOG_INFO));
|
|
|
|
if (interfaces & INTERFACE_SIM) {
|
|
st->cap = vbi_capture_sim_new (system,
|
|
&services,
|
|
sim_interlaced,
|
|
sim_synchronous);
|
|
if (NULL == st->cap)
|
|
no_mem_exit ();
|
|
|
|
interfaces = INTERFACE_SIM;
|
|
}
|
|
|
|
if (NULL == dev_name
|
|
&& (interfaces & (INTERFACE_DVB |
|
|
INTERFACE_V4L2 |
|
|
INTERFACE_V4L |
|
|
INTERFACE_BKTR))) {
|
|
error_exit (_("No device name specified."));
|
|
}
|
|
|
|
if (interfaces & INTERFACE_DVB) {
|
|
char *errstr;
|
|
|
|
assert (0 == (interfaces & (INTERFACE_V4L2 |
|
|
INTERFACE_V4L |
|
|
INTERFACE_BKTR |
|
|
INTERFACE_PROXY)));
|
|
|
|
#if 2 == VBI_VERSION_MINOR /* not ported to 0.3 yet */
|
|
assert (NULL != dev_name);
|
|
|
|
if (capture_raw_data) {
|
|
error_exit (_("Cannot capture raw VBI data "
|
|
"from a DVB device."));
|
|
}
|
|
|
|
st->cap = vbi_capture_dvb_new2 (dev_name,
|
|
ts_pid,
|
|
&errstr,
|
|
trace);
|
|
if (NULL == st->cap) {
|
|
interfaces &= ~INTERFACE_DVB;
|
|
capture_error_msg ("DVB", errstr);
|
|
} else {
|
|
interfaces = INTERFACE_DVB;
|
|
}
|
|
#elif 3 == VBI_VERSION_MINOR
|
|
ts_pid = ts_pid;
|
|
capture_raw_data = capture_raw_data;
|
|
errstr = NULL;
|
|
|
|
error_exit ("Sorry, no DVB support yet.");
|
|
#else
|
|
# error VBI_VERSION_MINOR == ?
|
|
#endif
|
|
}
|
|
|
|
if (interfaces & INTERFACE_PROXY) {
|
|
#if 2 == VBI_VERSION_MINOR
|
|
char *errstr = NULL;
|
|
|
|
st->proxy = vbi_proxy_client_create(dev_name,
|
|
"test/capture",
|
|
0, /* no flags */
|
|
&errstr,
|
|
trace);
|
|
if (NULL != st->proxy) {
|
|
st->cap = vbi_capture_proxy_new(st->proxy,
|
|
n_buffers,
|
|
system,
|
|
&services,
|
|
strict,
|
|
&errstr );
|
|
if (NULL == st->cap) {
|
|
interfaces &= ~INTERFACE_PROXY;
|
|
capture_error_msg ("PROXY", errstr);
|
|
free (errstr);
|
|
} else {
|
|
interfaces = INTERFACE_PROXY;
|
|
}
|
|
} else {
|
|
capture_error_msg ("PROXY", errstr);
|
|
free (errstr);
|
|
}
|
|
#else
|
|
error_exit ("Sorry, the proxy interface is not "
|
|
"available yet.\n");
|
|
#endif
|
|
}
|
|
|
|
if (interfaces & INTERFACE_V4L2) {
|
|
char *errstr;
|
|
|
|
st->cap = vbi_capture_v4l2_new (dev_name,
|
|
n_buffers,
|
|
&services,
|
|
strict,
|
|
&errstr,
|
|
trace);
|
|
if (NULL == st->cap) {
|
|
interfaces &= ~INTERFACE_V4L2;
|
|
capture_error_msg ("V4L2", errstr);
|
|
free (errstr);
|
|
} else {
|
|
interfaces = INTERFACE_V4L2;
|
|
}
|
|
}
|
|
|
|
if (interfaces & INTERFACE_V4L) {
|
|
char *errstr;
|
|
|
|
st->cap = vbi_capture_v4l_new (dev_name,
|
|
system,
|
|
&services,
|
|
strict,
|
|
&errstr,
|
|
trace);
|
|
if (NULL == st->cap) {
|
|
interfaces &= ~INTERFACE_V4L;
|
|
capture_error_msg ("V4L", errstr);
|
|
free (errstr);
|
|
} else {
|
|
interfaces = INTERFACE_V4L;
|
|
}
|
|
}
|
|
|
|
if (interfaces & INTERFACE_BKTR) {
|
|
char *errstr;
|
|
|
|
st->cap = vbi_capture_bktr_new (dev_name,
|
|
system,
|
|
&services,
|
|
strict,
|
|
&errstr,
|
|
trace);
|
|
if (NULL == st->cap) {
|
|
interfaces &= ~INTERFACE_BKTR;
|
|
capture_error_msg ("BKTR", errstr);
|
|
free (errstr);
|
|
} else {
|
|
interfaces = INTERFACE_BKTR;
|
|
}
|
|
}
|
|
|
|
if (0 == interfaces)
|
|
exit (EXIT_FAILURE);
|
|
|
|
if (interfaces & (INTERFACE_SIM |
|
|
INTERFACE_V4L2 |
|
|
INTERFACE_V4L |
|
|
INTERFACE_BKTR |
|
|
INTERFACE_PROXY)) {
|
|
unsigned int max_lines;
|
|
unsigned int raw_size;
|
|
|
|
st->sp = *vbi_capture_parameters (st->cap);
|
|
|
|
max_lines = st->sp.count[0] + st->sp.count[1];
|
|
assert (N_ELEMENTS (st->sliced) >= max_lines);
|
|
|
|
raw_size = st->sp.bytes_per_line * max_lines;
|
|
assert (raw_size > 0);
|
|
|
|
st->raw = malloc (raw_size);
|
|
if (NULL == st->raw)
|
|
no_mem_exit ();
|
|
} else if (interfaces & INTERFACE_DVB) {
|
|
assert (N_ELEMENTS (st->sliced) >= 2 * 32);
|
|
|
|
/* XXX We should have sampling parameters because
|
|
DVB VBI can transmit raw VBI samples.
|
|
For now let's just make write_stream_new() happy. */
|
|
CLEAR (st->sp);
|
|
#if 2 == VBI_VERSION_MINOR
|
|
st->sp.scanning = 625;
|
|
#else
|
|
st->sp.videostd_set = VBI_VIDEOSTD_SET_625_50;
|
|
#endif
|
|
}
|
|
|
|
st->loop = capture_loop;
|
|
|
|
st->interfaces = interfaces;
|
|
|
|
st->read_not_pull = read_not_pull;
|
|
|
|
st->callback = callback;
|
|
|
|
return st;
|
|
}
|
|
|
|
void
|
|
parse_option_verbose (void)
|
|
{
|
|
option_log_mask = option_log_mask * 2 + 1;
|
|
|
|
vbi_set_log_fn (option_log_mask,
|
|
vbi_log_on_stderr,
|
|
/* user_data */ NULL);
|
|
}
|
|
|
|
void
|
|
parse_option_quiet (void)
|
|
{
|
|
option_quiet = TRUE;
|
|
|
|
option_log_mask = 0;
|
|
|
|
vbi_set_log_fn (option_log_mask,
|
|
/* log_function */ NULL,
|
|
/* user_data */ NULL);
|
|
}
|
|
|
|
unsigned int
|
|
parse_option_ts (void)
|
|
{
|
|
unsigned long int value;
|
|
const char *s = optarg;
|
|
char *end;
|
|
|
|
assert (NULL != optarg);
|
|
|
|
value = strtoul (s, &end, 0);
|
|
|
|
if (value <= 0x000F || value >= 0x1FFF) {
|
|
error_exit (_("Invalid PID %u."), value);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
void
|
|
parse_option_dvb_pid (void)
|
|
{
|
|
if (!have_dev_name) {
|
|
/* Change default. */
|
|
option_dev_name = "/dev/dvb/adapter0/demux0";
|
|
}
|
|
|
|
option_dvb_pid = parse_option_ts ();
|
|
}
|
|
|
|
void
|
|
parse_option_dev_name (void)
|
|
{
|
|
assert (NULL != optarg);
|
|
|
|
option_dev_name = optarg;
|
|
|
|
have_dev_name = TRUE;
|
|
}
|
|
|
|
void
|
|
init_helpers (int argc,
|
|
char ** argv)
|
|
{
|
|
argc = argc;
|
|
argv = argv;
|
|
|
|
#ifndef HAVE_PROGRAM_INVOCATION_NAME
|
|
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = strlen (argv[0]); i > 0; --i) {
|
|
if ('/' == argv[0][i - 1])
|
|
break;
|
|
}
|
|
|
|
program_invocation_name = argv[0];
|
|
program_invocation_short_name = &argv[0][i];
|
|
}
|
|
|
|
#endif
|
|
|
|
setlocale (LC_ALL, "");
|
|
|
|
option_dev_name = "/dev/vbi";
|
|
|
|
option_log_mask = VBI_LOG_NOTICE * 2 - 1;
|
|
|
|
vbi_set_log_fn (option_log_mask,
|
|
vbi_log_on_stderr,
|
|
/* user_data */ NULL);
|
|
}
|