diff options
| author | Mark Purcell <msp@debian.org> | 2010-02-25 21:13:46 +1100 | 
|---|---|---|
| committer | etobi <git@e-tobi.net> | 2013-09-03 09:48:46 +0200 | 
| commit | 665818f1969f893f05edf5b70eb1804c89b2829e (patch) | |
| tree | 34ba68cee03c52d769a5a51b456b7e9d63cd091a /util/atsc_epg | |
| parent | 109c7947d6a11a2a54eff1b19615ed80ea2f0602 (diff) | |
| parent | 9fe4d4ea9c054e539ab679ed2e9c076c35beb69d (diff) | |
| download | linux-dvb-apps-665818f1969f893f05edf5b70eb1804c89b2829e.tar.gz | |
Imported Debian patch 1.1.1+rev1355-1debian/1.1.1+rev1355-1
Diffstat (limited to '')
| -rw-r--r-- | util/atsc_epg/Makefile | 16 | ||||
| -rw-r--r-- | util/atsc_epg/README | 12 | ||||
| -rw-r--r-- | util/atsc_epg/atsc_epg.c | 1249 | 
3 files changed, 1277 insertions, 0 deletions
| diff --git a/util/atsc_epg/Makefile b/util/atsc_epg/Makefile new file mode 100644 index 0000000..747e2b8 --- /dev/null +++ b/util/atsc_epg/Makefile @@ -0,0 +1,16 @@ +# Makefile for linuxtv.org dvb-apps/util/atsc_epg + +binaries = atsc_epg + +inst_bin = $(binaries) + +CPPFLAGS += -I../../lib -std=c99 -D_POSIX_SOURCE +#LDFLAGS  += -static -L../../lib/libdvbapi -L../../lib/libucsi +LDFLAGS  += -L../../lib/libdvbapi -L../../lib/libucsi +LDLIBS   += -ldvbapi -lucsi + +.PHONY: all + +all: $(binaries) + +include ../../Make.rules diff --git a/util/atsc_epg/README b/util/atsc_epg/README new file mode 100644 index 0000000..6a82453 --- /dev/null +++ b/util/atsc_epg/README @@ -0,0 +1,12 @@ +Hi there, + +atsc_epg is a small utility for obtaining information such as programs, EPG  +(electronic program guide) from an ATSC channel.  + +Pulling the detailed information, i.e., option '-t', may take fairly long  +time, or never ending, which is a bug of the PSIP generator. Ctrl+C can be  +used to abort and the received parts will be printed. + +Enjoy, +Yufei + diff --git a/util/atsc_epg/atsc_epg.c b/util/atsc_epg/atsc_epg.c new file mode 100644 index 0000000..55e266d --- /dev/null +++ b/util/atsc_epg/atsc_epg.c @@ -0,0 +1,1249 @@ +/* + * atsc_epg utility + * + * Copyright (C) 2009 Yufei Yuan <yfyuan@gmail.com> + * 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <errno.h> +#include <getopt.h> +#include <stdarg.h> +#include <libdvbapi/dvbfe.h> +#include <libdvbapi/dvbdemux.h> +#include <libucsi/dvb/section.h> +#include <libucsi/atsc/section.h> +#include <libucsi/atsc/types.h> + +#define TIMEOUT				60 +#define RRT_TIMEOUT			60 +#define MAX_NUM_EVENT_TABLES		128 +#define TITLE_BUFFER_LEN		4096 +#define MESSAGE_BUFFER_LEN		(16 * 1024) +#define MAX_NUM_CHANNELS		16 +#define MAX_NUM_EVENTS_PER_CHANNEL	(4 * 24 * 7) + +static int atsc_scan_table(int dmxfd, uint16_t pid, enum atsc_section_tag tag, +	void **table_section); + +static const char *program; +static int adapter = 0; +static int period = 12; /* hours */ +static int frequency; +static int enable_ett = 0; +static int ctrl_c = 0; +static const char *modulation = NULL; +static char separator[80]; +void (*old_handler)(int); + +struct atsc_string_buffer { +	int buf_len; +	int buf_pos; +	char *string; +}; + +struct atsc_event_info { +	uint16_t id; +	struct tm start; +	struct tm end; +	int title_pos; +	int title_len; +	int msg_pos; +	int msg_len; +}; + +struct atsc_eit_section_info { +	uint8_t section_num; +	uint8_t num_events; +	uint8_t num_etms; +	uint8_t num_received_etms; +	struct atsc_event_info **events; +}; + +struct atsc_eit_info { +	int num_eit_sections; +	struct atsc_eit_section_info *section; +}; + +struct atsc_channel_info { +	uint8_t num_eits; +	uint8_t service_type; +	char short_name[8]; +	uint16_t major_num; +	uint16_t minor_num; +	uint16_t tsid; +	uint16_t prog_num; +	uint16_t src_id; +	struct atsc_eit_info *eit; +	struct atsc_event_info *last_event; +	int event_info_index; +	struct atsc_event_info e[MAX_NUM_EVENTS_PER_CHANNEL]; +	struct atsc_string_buffer title_buf; +	struct atsc_string_buffer msg_buf; +}; + +struct atsc_virtual_channels_info { +	int num_channels; +	uint16_t eit_pid[MAX_NUM_EVENT_TABLES]; +	uint16_t ett_pid[MAX_NUM_EVENT_TABLES]; +	struct atsc_channel_info ch[MAX_NUM_CHANNELS]; +} guide; + +struct mgt_table_name { +	uint16_t range; +	const char *string; +}; + +struct mgt_table_name mgt_tab_name_array[] = { +	{0x0000, "terrestrial VCT with current_next_indictor=1"}, +	{0x0001, "terrestrial VCT with current_next_indictor=0"}, +	{0x0002, "cable VCT with current_next_indictor=1"}, +	{0x0003, "cable VCT with current_next_indictor=0"}, +	{0x0004, "channel ETT"}, +	{0x0005, "DCCSCT"}, +	{0x00FF, "reserved for future ATSC use"}, +	{0x017F, "EIT"}, +	{0x01FF, "reserved for future ATSC use"}, +	{0x027F, "event ETT"}, +	{0x02FF, "reserved for future ATSC use"}, /* FIXME */ +	{0x03FF, "RRT with rating region"}, +	{0x0FFF, "user private"}, +	{0x13FF, "reserved for future ATSC use"}, +	{0x14FF, "DCCT with dcc_id"}, +	{0xFFFF, "reserved for future ATSC use"} +}; + +const char *channel_modulation_mode[] = { +	"", +	"analog", +	"SCTE mode 1", +	"SCTE mode 2", +	"ATSC 8VSB", +	"ATSC 16VSB" +}; + +const char *channel_service_type[] = { +	"", +	"analog TV", +	"ATSC digital TV", +	"ATSC audio", +	"ATSC data-only" +}; + +void *(*table_callback[16])(struct atsc_section_psip *) = +{ +	NULL, NULL, NULL, NULL, NULL, NULL, NULL, +	(void *(*)(struct atsc_section_psip *))atsc_mgt_section_codec, +	(void *(*)(struct atsc_section_psip *))atsc_tvct_section_codec, +	(void *(*)(struct atsc_section_psip *))atsc_cvct_section_codec, +	(void *(*)(struct atsc_section_psip *))atsc_rrt_section_codec, +	(void *(*)(struct atsc_section_psip *))atsc_eit_section_codec, +	(void *(*)(struct atsc_section_psip *))atsc_ett_section_codec, +	(void *(*)(struct atsc_section_psip *))atsc_stt_section_codec, +	NULL, NULL +}; + +static void int_handler(int sig_num) +{ +	if(SIGINT != sig_num) { +		return; +	} +	ctrl_c = 1; +} + +/* shamelessly stolen from dvbsnoop, but almost not modified */ +static uint32_t get_bits(const uint8_t *buf, int startbit, int bitlen) +{ +	const uint8_t *b; +	uint32_t mask,tmp_long; +	int bitHigh,i; + +	b = &buf[startbit / 8]; +	startbit %= 8; + +	bitHigh = 8; +	tmp_long = b[0]; +	for (i = 0; i < ((bitlen-1) >> 3); i++) { +		tmp_long <<= 8; +		tmp_long  |= b[i+1]; +		bitHigh   += 8; +	} + +	startbit = bitHigh - startbit - bitlen; +	tmp_long = tmp_long >> startbit; +	mask     = (1ULL << bitlen) - 1; +	return tmp_long & mask; +} + +static void usage(void) +{ +	fprintf(stderr, "usage: %s [-a <n>] -f <frequency> [-p <period>]" +		" [-m <modulation>] [-t] [-h]\n", program); +} + +static void help(void) +{ +	fprintf(stderr, +	"\nhelp:\n" +	"%s [-a <n>] -f <frequency> [-p <period>] [-m <modulation>] [-t] [-h]\n" +	"  -a: adapter index to use, (default 0)\n" +	"  -f: tuning frequency\n" +	"  -p: period in hours, (default 12)\n" +	"  -m: modulation ATSC vsb_8|vsb_16 (default vsb_8)\n" +	"  -t: enable ETT to receive program details, if available\n" +	"  -h: display this message\n", program); +} + +static int close_frontend(struct dvbfe_handle *fe) +{ +	if(NULL == fe) { +		fprintf(stderr, "%s(): NULL pointer detected\n", __FUNCTION__); +	} + +	dvbfe_close(fe); + +	return 0; +} + +static int open_frontend(struct dvbfe_handle **fe) +{ +	struct dvbfe_info fe_info; + +	if(NULL == (*fe = dvbfe_open(adapter, 0, 0))) { +		fprintf(stderr, "%s(): error calling dvbfe_open()\n", +			__FUNCTION__); +		return -1; +	} +	dvbfe_get_info(*fe, 0, &fe_info, DVBFE_INFO_QUERYTYPE_IMMEDIATE, 0); +	if(DVBFE_TYPE_ATSC != fe_info.type) { +		fprintf(stderr, "%s(): only ATSC frontend supported currently\n", +			__FUNCTION__); +		return -1; +	} +	fe_info.feparams.frequency = frequency; +	fe_info.feparams.inversion = DVBFE_INVERSION_AUTO; +	fe_info.feparams.u.atsc.modulation = DVBFE_ATSC_MOD_VSB_8; +	fprintf(stdout, "tuning to %d Hz, please wait...\n", frequency); +	if(dvbfe_set(*fe, &fe_info.feparams, TIMEOUT * 1000)) { +		fprintf(stderr, "%s(): cannot lock to %d Hz in %d seconds\n", +			__FUNCTION__, frequency, TIMEOUT); +		return -1; +	} +	fprintf(stdout, "tuner locked.\n"); + +	return 0; +} + +#if ENABLE_RRT +/* this is untested as since this part of the library is broken */ +static int parse_rrt(int dmxfd) +{ +	const enum atsc_section_tag tag = stag_atsc_rating_region; +	struct atsc_rrt_section *rrt; +	struct atsc_text *region_name; +	struct atsc_text_string *atsc_str; +	int i, j, ret; + +	i = 0; +	fprintf(stdout, "waiting for RRT: "); +	fflush(stdout); +	while(i < RRT_TIMEOUT) { +		ret = atsc_scan_table(dmxfd, ATSC_BASE_PID, tag, (void **)&rrt); +		if(0 > ret) { +			fprintf(stderr, "%s(): error calling atsc_scan_table()\n", +				__FUNCTION__); +			return -1; +		} +		if(0 == ret) { +			if(RRT_TIMEOUT > i) { +				fprintf(stdout, "."); +				fflush(stdout); +			} else { +				fprintf(stdout, "\nno RRT in %d seconds\n", +					RRT_TIMEOUT); +				return 0; +			} +			i += TIMEOUT; +		} else { +			fprintf(stdout, "\n"); +			fflush(stdout); +			break; +		} +	} + +	region_name = atsc_rrt_section_rating_region_name_text(rrt); +	atsc_text_strings_for_each(region_name, atsc_str, i) { +		struct atsc_text_string_segment *seg; + +		atsc_text_string_segments_for_each(atsc_str, seg, j) { +			const char *c; +			int k; +			if(seg->mode < 0x3E) { +				fprintf(stderr, "%s(): text mode of 0x%02X " +					"not supported yet\n", +					__FUNCTION__, seg->mode); +				return -1; +			} +			c = (const char *)atsc_text_string_segment_bytes(seg); +			for(k = 0; k < seg->number_bytes; k++) { +				fprintf(stdout, "%c", c[k]); +			} +		} +	} + +	return 0; +} +#endif + +static int parse_stt(int dmxfd) +{ +	const enum atsc_section_tag tag = stag_atsc_system_time; +	const struct atsc_stt_section *stt; +	time_t rx_time; +	time_t sys_time; +	int ret; + +	ret = atsc_scan_table(dmxfd, ATSC_BASE_PID, tag, (void **)&stt); +	if(0 > ret) { +		fprintf(stderr, "%s(): error calling atsc_scan_table()\n", +			__FUNCTION__); +		return -1; +	} +	if(0 == ret) { +		fprintf(stdout, "no STT in %d seconds\n", TIMEOUT); +		return 0; +	} + +	rx_time = atsctime_to_unixtime(stt->system_time); +	time(&sys_time); +	fprintf(stdout, "system time: %s", ctime(&sys_time)); +	fprintf(stdout, "TS STT time: %s", ctime(&rx_time)); + +	return 0; +} + +static int parse_tvct(int dmxfd) +{ +	int num_sections; +	uint32_t section_pattern; +	const enum atsc_section_tag tag = stag_atsc_terrestrial_virtual_channel; +	struct atsc_tvct_section *tvct; +	struct atsc_tvct_channel *ch; +	struct atsc_channel_info *curr_info; +	int i, k, ret; + +	section_pattern = 0; +	num_sections = -1; + +	do { +		ret = atsc_scan_table(dmxfd, ATSC_BASE_PID, tag, (void **)&tvct); +		if(0 > ret) { +			fprintf(stderr, "%s(): error calling atsc_scan_table()\n", +			__FUNCTION__); +			return -1; +		} +		if(0 == ret) { +			fprintf(stdout, "no TVCT in %d seconds\n", TIMEOUT); +			return 0; +		} + +		if(-1 == num_sections) { +			num_sections = 1 + tvct->head.ext_head.last_section_number; +			if(32 < num_sections) { +				fprintf(stderr, "%s(): no support yet for " +					"tables having more than 32 sections\n", +					__FUNCTION__); +				return -1; +			} +		} else { +			if(num_sections != +				1 + tvct->head.ext_head.last_section_number) { +				fprintf(stderr, +					"%s(): last section number does not match\n", +					__FUNCTION__); +				return -1; +			} +		} +		if(section_pattern & (1 << tvct->head.ext_head.section_number)) { +			continue; +		} +		section_pattern |= 1 << tvct->head.ext_head.section_number; + +		if(MAX_NUM_CHANNELS < guide.num_channels + +			tvct->num_channels_in_section) { +			fprintf(stderr, "%s(): no support for more than %d " +				"virtual channels in a pyhsical channel\n", +				__FUNCTION__, MAX_NUM_CHANNELS); +			return -1; +		} +		curr_info = &guide.ch[guide.num_channels]; +		guide.num_channels += tvct->num_channels_in_section; + +	atsc_tvct_section_channels_for_each(tvct, ch, i) { +		/* initialize the curr_info structure */ +		/* each EIT covers 3 hours */ +		curr_info->num_eits = (period / 3) + !!(period % 3); +		while (curr_info->num_eits && +			(0xFFFF == guide.eit_pid[curr_info->num_eits - 1])) { +			curr_info->num_eits -= 1; +		} +		if(curr_info->eit) { +			fprintf(stderr, "%s(): non-NULL pointer detected " +				"during initialization", __FUNCTION__); +			return -1; +		} +		if(NULL == (curr_info->eit = calloc(curr_info->num_eits, +			sizeof(struct atsc_eit_info)))) { +			fprintf(stderr, "%s(): error calling calloc()\n", +				__FUNCTION__); +			return -1; +		} +		if(NULL == (curr_info->title_buf.string = calloc(TITLE_BUFFER_LEN, +			sizeof(char)))) { +			fprintf(stderr, "%s(): error calling calloc()\n", +				__FUNCTION__); +			return -1; +		} +		curr_info->title_buf.buf_len = TITLE_BUFFER_LEN; +		curr_info->title_buf.buf_pos = 0; + +		if(NULL == (curr_info->msg_buf.string = calloc(MESSAGE_BUFFER_LEN, +			sizeof(char)))) { +			fprintf(stderr, "%s(): error calling calloc()\n", +				__FUNCTION__); +			return -1; +		} +		curr_info->msg_buf.buf_len = MESSAGE_BUFFER_LEN; +		curr_info->msg_buf.buf_pos = 0; + +		for(k = 0; k < 7; k++) { +			curr_info->short_name[k] = +				get_bits((const uint8_t *)ch->short_name, +				k * 16, 16); +		} +		curr_info->service_type = ch->service_type; +		curr_info->major_num = ch->major_channel_number; +		curr_info->minor_num = ch->minor_channel_number; +		curr_info->tsid = ch->channel_TSID; +		curr_info->prog_num = ch->program_number; +		curr_info->src_id = ch->source_id; +		curr_info++; +		} +	} while(section_pattern != (uint32_t)((1 << num_sections) - 1)); + +	return 0; +} + +static int match_event(struct atsc_eit_info *eit, uint16_t event_id, +	struct atsc_event_info **event, uint8_t *curr_index) +{ +	int j, k; +	struct atsc_eit_section_info *section; + +	if(NULL == eit || NULL == event || NULL == curr_index) { +		fprintf(stderr, "%s(): NULL pointer detected\n", __FUNCTION__); +		return -1; +	} + +	for(j = 0; j < eit->num_eit_sections; j++) { +		section = &eit->section[j]; + +		for(k = 0; k < section->num_events; k++) { +			if(section->events[k] && section->events[k]->id == +				event_id) { +				*event = section->events[k]; +				break; +			} +		} +		if(*event) { +			*curr_index = j; +			break; +		} +	} + +	return 0; +} + +static int parse_message(struct atsc_channel_info *channel, +	struct atsc_ett_section *ett, struct atsc_event_info *event) +{ +	int i, j; +	struct atsc_text *text; +	struct atsc_text_string *str; + +	if(NULL == ett || NULL == event || NULL == channel) { +		fprintf(stderr, "%s(): NULL pointer detected\n", __FUNCTION__); +		return -1; +	} + +	text = atsc_ett_section_extended_text_message(ett); +	atsc_text_strings_for_each(text, str, i) { +		struct atsc_text_string_segment *seg; + +		atsc_text_string_segments_for_each(str, seg, j) { +			event->msg_pos = channel->msg_buf.buf_pos; +			if(0 > atsc_text_segment_decode(seg, +				(uint8_t **)&channel->msg_buf.string, +				(size_t *)&channel->msg_buf.buf_len, +				(size_t *)&channel->msg_buf.buf_pos)) { +				fprintf(stderr, "%s(): error calling " +					"atsc_text_segment_decode()\n", +					__FUNCTION__); +				return -1; +			} +			event->msg_len = 1 + channel->msg_buf.buf_pos - +				event->msg_pos; +		} +	} + +	return 0; +} + +static int parse_ett(int dmxfd, int index, uint16_t pid) +{ +	uint8_t curr_index; +	uint32_t section_pattern; +	const enum atsc_section_tag tag = stag_atsc_extended_text; +	struct atsc_eit_info *eit; +	struct atsc_ett_section *ett; +	struct atsc_channel_info *channel; +	struct atsc_event_info *event; +	struct atsc_eit_section_info *section; +	uint16_t source_id, event_id; +	int c, ret; + +	if(0xFFFF == guide.ett_pid[index]) { +		return 0; +	} + +	for(c = 0; c < guide.num_channels; c++) { +		channel = &guide.ch[c]; +		eit = &channel->eit[index]; + +		section_pattern = 0; +		while(section_pattern != +			(uint32_t)((1 << eit->num_eit_sections) - 1)) { +			if(ctrl_c) { +				return 0; +			} +			ret = atsc_scan_table(dmxfd, pid, tag, (void **)&ett); +			fprintf(stdout, "."); +			fflush(stdout); +			if(0 > ret) { +				fprintf(stderr, "%s(): error calling " +					"atsc_scan_table()\n", __FUNCTION__); +				return -1; +			} +			if(0 == ret) { +				fprintf(stdout, "no ETT %d in %d seconds\n", +					index, TIMEOUT); +				return 0; +			} + +			source_id = ett->ETM_source_id; +			event_id = ett->ETM_sub_id; +			if(source_id != channel->src_id) { +				continue; +			} + +			event = NULL; +			if(match_event(eit, event_id, &event, &curr_index)) { +				fprintf(stderr, "%s(): error calling " +					"match_event()\n", __FUNCTION__); +				return -1; +			} +			if(NULL == event) { +				continue; +			} +			if(section_pattern & (1 << curr_index)) { +				/* the section has been filled, so skip, +				 * not consider version yet +				 */ +				continue; +			} +			if(event->msg_len) { +				/* the message has been filled */ +				continue; +			} + +			if(parse_message(channel, ett, event)) { +				fprintf(stderr, "%s(): error calling " +					"parse_message()\n", __FUNCTION__); +				return -1; +			} +			section = &eit->section[curr_index]; +			if(++section->num_received_etms == section->num_etms) { +				section_pattern |= 1 << curr_index; +			} +		} +	} + +	return 0; +} + +static int parse_events(struct atsc_channel_info *curr_info, +	struct atsc_eit_section *eit, struct atsc_eit_section_info *section) +{ +	int i, j, k; +	struct atsc_eit_event *e; +	time_t start_time, end_time; + +	if(NULL == curr_info || NULL == eit) { +		fprintf(stderr, "%s(): NULL pointer detected\n", __FUNCTION__); +		return -1; +	} + +	atsc_eit_section_events_for_each(eit, e, i) { +		struct atsc_text *title; +		struct atsc_text_string *str; +		struct atsc_event_info *e_info = +			&curr_info->e[curr_info->event_info_index]; + +		if(0 == i && curr_info->last_event) { +			if(e->event_id == curr_info->last_event->id) { +				section->events[i] = NULL; +				/* skip if it's the same event spanning +				 * over sections +				 */ +				continue; +			} +		} +		curr_info->event_info_index += 1; +		section->events[i] = e_info; +		e_info->id = e->event_id; +		start_time = atsctime_to_unixtime(e->start_time); +		end_time = start_time + e->length_in_seconds; +		localtime_r(&start_time, &e_info->start); +		localtime_r(&end_time, &e_info->end); +		if(0 != e->ETM_location && 3 != e->ETM_location) { +			/* FIXME assume 1 and 2 is interchangable as of now */ +			section->num_etms++; +		} + +		title = atsc_eit_event_name_title_text(e); +		atsc_text_strings_for_each(title, str, j) { +			struct atsc_text_string_segment *seg; + +			atsc_text_string_segments_for_each(str, seg, k) { +				e_info->title_pos = curr_info->title_buf.buf_pos; +				if(0 > atsc_text_segment_decode(seg, +					(uint8_t **)&curr_info->title_buf.string, +					(size_t *)&curr_info->title_buf.buf_len, +					(size_t *)&curr_info->title_buf.buf_pos)) { +					fprintf(stderr, "%s(): error calling " +						"atsc_text_segment_decode()\n", +						__FUNCTION__); +					return -1; +				} +				e_info->title_len = curr_info->title_buf.buf_pos - +					e_info->title_pos + 1; +			} +		} +	} + +	return 0; +} + +static int parse_eit(int dmxfd, int index, uint16_t pid) +{ +	int num_sections; +	uint8_t section_num; +	uint8_t curr_channel_index; +	uint32_t section_pattern; +	const enum atsc_section_tag tag = stag_atsc_event_information; +	struct atsc_eit_section *eit; +	struct atsc_channel_info *curr_info; +	struct atsc_eit_info *eit_info; +	struct atsc_eit_section_info *section; +	uint16_t source_id; +	uint32_t eit_instance_pattern = 0; +	int i, k, ret; + +	while(eit_instance_pattern != +		(uint32_t)((1 << guide.num_channels) - 1)) { +		source_id = 0xFFFF; +		section_pattern = 0; +		num_sections = -1; + +		do { +			ret = atsc_scan_table(dmxfd, pid, tag, (void **)&eit); +			fprintf(stdout, "."); +			fflush(stdout); +			if(0 > ret) { +				fprintf(stderr, "%s(): error calling " +					"atsc_scan_table()\n", __FUNCTION__); +				return -1; +			} +			if(0 == ret) { +				fprintf(stdout, "no EIT %d in %d seconds\n", +					index, TIMEOUT); +				return 0; +			} + +			if(0xFFFF == source_id) { +			source_id = atsc_eit_section_source_id(eit); +			for(k = 0; k < guide.num_channels; k++) { +				if(source_id == guide.ch[k].src_id) { +					curr_info = &guide.ch[k]; +					curr_channel_index = k; +					if(0 == index) { +						curr_info->last_event = NULL; +					} +					break; +				} +			} +			if(k == guide.num_channels) { +				fprintf(stderr, "%s(): cannot find source_id " +					"0x%04X in the EIT\n", +					__FUNCTION__, source_id); +				return -1; +			} +			} else { +				if(source_id != +					atsc_eit_section_source_id(eit)) { +					continue; +				} +			} +			if(eit_instance_pattern & (1 << curr_channel_index)) { +				/* we have received this instance, +				 * so quit quick +				 */ +				break; +			} + +			if(-1 == num_sections) { +				num_sections = 1 + +					eit->head.ext_head.last_section_number; +				if(32 < num_sections) { +					fprintf(stderr, +						"%s(): no support yet for " +						"tables having more than " +						"32 sections\n", __FUNCTION__); +					return -1; +				} +			} else { +				if(num_sections != 1 + +					eit->head.ext_head.last_section_number) { +					fprintf(stderr, +						"%s(): last section number " +						"does not match\n", +						__FUNCTION__); +					return -1; +				} +			} +			if(section_pattern & +				(1 << eit->head.ext_head.section_number)) { +				continue; +			} +			section_pattern |= 1 << eit->head.ext_head.section_number; + +			eit_info = &curr_info->eit[index]; +			if(NULL == (eit_info->section = +				realloc(eit_info->section, +				(eit_info->num_eit_sections + 1) * +				sizeof(struct atsc_eit_section_info)))) { +				fprintf(stderr, +					"%s(): error calling realloc()\n", +					__FUNCTION__); +				return -1; +			} +			section_num = eit->head.ext_head.section_number; +			if(0 == eit_info->num_eit_sections) { +				eit_info->num_eit_sections = 1; +				section = eit_info->section; +			} else { +				/* have to sort it into section order +				 * (temporal order) +				 */ +				for(i = 0; i < eit_info->num_eit_sections; i++) { +					if(eit_info->section[i].section_num > +						section_num) { +						break; +					} +				} +				memmove(&eit_info->section[i + 1], +					&eit_info->section[i], +					(eit_info->num_eit_sections - i) * +					sizeof(struct atsc_eit_section_info)); +				section = &eit_info->section[i - 1]; +				section = &eit_info->section[i]; +				eit_info->num_eit_sections += 1; +			} + +			section->section_num = section_num; +			section->num_events = eit->num_events_in_section; +			section->num_etms = 0; +			section->num_received_etms = 0; +			if(NULL == (section->events = calloc(section->num_events, +				sizeof(struct atsc_event_info *)))) { +				fprintf(stderr, "%s(): error calling calloc()\n", +					__FUNCTION__); +				return -1; +			} +			if(parse_events(curr_info, eit, section)) { +				fprintf(stderr, "%s(): error calling " +					"parse_events()\n", __FUNCTION__); +				return -1; +			} +		} while(section_pattern != (uint32_t)((1 << num_sections) - 1)); +		eit_instance_pattern |= 1 << curr_channel_index; +	} + +	for(i = 0; i < guide.num_channels; i++) { +		struct atsc_channel_info *channel = &guide.ch[i]; +		struct atsc_eit_info *ei = &channel->eit[index]; +		struct atsc_eit_section_info *s; + +		if(0 == ei->num_eit_sections) { +			channel->last_event = NULL; +			continue; +		} +		s = &ei->section[ei->num_eit_sections - 1]; +		/* BUG: it's incorrect when last section has no event */ +		if(0 == s->num_events) { +			channel->last_event = NULL; +			continue; +		} +		channel->last_event = s->events[s->num_events - 1]; +	} + +	return 0; +} + +static int parse_mgt(int dmxfd) +{ +	const enum atsc_section_tag tag = stag_atsc_master_guide; +	struct atsc_mgt_section *mgt; +	struct atsc_mgt_table *t; +	int i, j, ret; + +	ret = atsc_scan_table(dmxfd, ATSC_BASE_PID, tag, (void **)&mgt); +	if(0 > ret) { +		fprintf(stderr, "%s(): error calling atsc_scan_table()\n", +			__FUNCTION__); +		return -1; +	} +	if(0 == ret) { +		fprintf(stdout, "no MGT in %d seconds\n", TIMEOUT); +		return 0; +	} + +	fprintf(stdout, "MGT table:\n"); +	atsc_mgt_section_tables_for_each(mgt, t, i) { +		struct mgt_table_name table; + +	for(j = 0; j < (int)(sizeof(mgt_tab_name_array) / +		sizeof(struct mgt_table_name)); j++) { +		if(t->table_type > mgt_tab_name_array[j].range) { +			continue; +		} +		table = mgt_tab_name_array[j]; +		if(0 == j || mgt_tab_name_array[j - 1].range + 1 == +			mgt_tab_name_array[j].range) { +			j = -1; +		} else { +			j = t->table_type - mgt_tab_name_array[j - 1].range - 1; +			if(0x017F == table.range) { +				guide.eit_pid[j] = t->table_type_PID; +			} else if (0x027F == table.range) { +				guide.ett_pid[j] = t->table_type_PID; +			} +		} +		break; +	} + +		fprintf(stdout, "  %2d: type = 0x%04X, PID = 0x%04X, %s", i, +		    t->table_type, t->table_type_PID, table.string); +		if(-1 != j) { +		    fprintf(stdout, " %d", j); +		} +		fprintf(stdout, "\n"); +	} + +	return 0; +} + +static int cleanup_guide(void) +{ +	int i, j, k; + +	for(i = 0; i < guide.num_channels; i++) { +		struct atsc_channel_info *channel = &guide.ch[i]; + +		if(channel->title_buf.string) { +			free(channel->title_buf.string); +		} +		if(channel->msg_buf.string) { +			free(channel->msg_buf.string); +		} +		for(j = 0; j < channel->num_eits; j++) { +			struct atsc_eit_info *eit = &channel->eit[j]; + +			for(k = 0; k < eit->num_eit_sections; k++) { +				struct atsc_eit_section_info *section = +					&eit->section[k]; +				if(section->num_events) { +					free(section->events); +				} +			} +			if(k) { +				free(eit->section); +			} +		} +		if(j) { +			free(channel->eit); +		} +	} + +	return 0; +} + +static int print_events(struct atsc_channel_info *channel, +	struct atsc_eit_section_info *section) +{ +	int m; +	char line[256]; + +	if(NULL == section) { +		fprintf(stderr, "%s(): NULL pointer detected", __FUNCTION__); +		return -1; +	} +	for(m = 0; m < section->num_events; m++) { +		struct atsc_event_info *event = +			section->events[m]; + +		if(NULL == event) { +			continue; +		} +		fprintf(stdout, "|%02d:%02d--%02d:%02d| ", +			event->start.tm_hour, event->start.tm_min, +			event->end.tm_hour, event->end.tm_min); +		snprintf(line, event->title_len, "%s", +			&channel->title_buf.string[event->title_pos]); +		line[event->title_len] = '\0'; +		fprintf(stdout, "%s\n", line); +		if(event->msg_len) { +			int len = event->msg_len; +			int pos = event->msg_pos; +			size_t part; + +			do { +				part = len > 255 ? 255 : len; +				snprintf(line, part, "%s", +					&channel->msg_buf.string[pos]); +				line[part] = '\0'; +				fprintf(stdout, "%s", line); +				len -= part; +				pos += part; +			} while(0 < len); +			fprintf(stdout, "\n"); +		} +	} +	return 0; +} + +static int print_guide(void) +{ +	int i, j, k; + +	fprintf(stdout, "%s\n", separator); +	for(i = 0; i < guide.num_channels; i++) { +		struct atsc_channel_info *channel = &guide.ch[i]; + +		fprintf(stdout, "%d.%d  %s\n", channel->major_num, +			channel->minor_num, channel->short_name); +		for(j = 0; j < channel->num_eits; j++) { +			struct atsc_eit_info *eit = &channel->eit[j]; + +			for(k = 0; k < eit->num_eit_sections; k++) { +				struct atsc_eit_section_info *section = +					&eit->section[k]; +				if(print_events(channel, section)) { +					fprintf(stderr, "%s(): error calling " +						"print_events()\n", __FUNCTION__); +					return -1; +				} +			} +		} +		fprintf(stdout, "%s\n", separator); +	} + +	return 0; +} + +static int open_demux(int *dmxfd) +{ +	if((*dmxfd = dvbdemux_open_demux(adapter, 0, 0)) < 0) { +		fprintf(stderr, "%s(): error calling dvbdemux_open_demux()\n", +			__FUNCTION__); +		return -1; +	} +	return 0; +} + +static int close_demux(int dmxfd) +{ +	if(dvbdemux_stop(dmxfd)) { +		fprintf(stderr, "%s(): error calling dvbdemux_stop()\n", +			__FUNCTION__); +		return -1; +	} +	return 0; +} + +/* used other utilities as template and generalized here */ +static int atsc_scan_table(int dmxfd, uint16_t pid, enum atsc_section_tag tag, +	void **table_section) +{ +	uint8_t filter[18]; +	uint8_t mask[18]; +	unsigned char sibuf[4096]; +	int size; +	int ret; +	struct pollfd pollfd; +	struct section *section; +	struct section_ext *section_ext; +	struct atsc_section_psip *psip; + +	/* create a section filter for the table */ +	memset(filter, 0, sizeof(filter)); +	memset(mask, 0, sizeof(mask)); +	filter[0] = tag; +	mask[0] = 0xFF; +	if(dvbdemux_set_section_filter(dmxfd, pid, filter, mask, 1, 1)) { +		fprintf(stderr, "%s(): error calling atsc_scan_table()\n", +			__FUNCTION__); +		return -1; +	} + +	/* poll for data */ +	pollfd.fd = dmxfd; +	pollfd.events = POLLIN | POLLERR |POLLPRI; +	if((ret = poll(&pollfd, 1, TIMEOUT * 1000)) < 0) { +		if(ctrl_c) { +			return 0; +		} +		fprintf(stderr, "%s(): error calling poll()\n", __FUNCTION__); +		return -1; +	} + +	if(0 == ret) { +		return 0; +	} + +	/* read it */ +	if((size = read(dmxfd, sibuf, sizeof(sibuf))) < 0) { +		fprintf(stderr, "%s(): error calling read()\n", __FUNCTION__); +		return -1; +	} + +	/* parse section */ +	section = section_codec(sibuf, size); +	if(NULL == section) { +		fprintf(stderr, "%s(): error calling section_codec()\n", +			__FUNCTION__); +		return -1; +	} + +	section_ext = section_ext_decode(section, 0); +	if(NULL == section_ext) { +		fprintf(stderr, "%s(): error calling section_ext_decode()\n", +			__FUNCTION__); +		return -1; +	} + +	psip = atsc_section_psip_decode(section_ext); +	if(NULL == psip) { +		fprintf(stderr, +			"%s(): error calling atsc_section_psip_decode()\n", +			__FUNCTION__); +		return -1; +	} + +	*table_section = table_callback[tag & 0x0F](psip); +	if(NULL == *table_section) { +		fprintf(stderr, "%s(): error decode table section\n", +			__FUNCTION__); +		return -1; +	} + +	return 1; +} + +int main(int argc, char *argv[]) +{ +	int i, dmxfd; +	struct dvbfe_handle *fe; + +	program = argv[0]; + +	if(1 == argc) { +		usage(); +		exit(-1); +	} + +	for( ; ; ) { +		char c; + +		if(-1 == (c = getopt(argc, argv, "a:f:p:m:th"))) { +			break; +		} + +		switch(c) { +		case 'a': +			adapter = strtoll(optarg, NULL, 0); +			break; + +		case 'f': +			frequency = strtol(optarg, NULL, 0); +			break; + +		case 'p': +			period = strtol(optarg, NULL, 0); +			/* each table covers 3 hours */ +			if((3 * MAX_NUM_EVENT_TABLES) < period) { +				period = 3 * MAX_NUM_EVENT_TABLES; +			} +			break; + +		case 'm': +			/* just stub, so far ATSC only has VSB_8 */ +			modulation = optarg; +			break; + +		case 't': +			enable_ett = 1; +			break; + +		case 'h': +			help(); +			exit(0); + +		default: +			usage(); +			exit(-1); +		} +	} + +	memset(separator, '-', sizeof(separator)); +	separator[79] = '\0'; +	memset(&guide, 0, sizeof(struct atsc_virtual_channels_info)); +	memset(guide.eit_pid, 0xFF, MAX_NUM_EVENT_TABLES * sizeof(uint16_t)); +	memset(guide.ett_pid, 0xFF, MAX_NUM_EVENT_TABLES * sizeof(uint16_t)); + +	if(open_frontend(&fe)) { +		fprintf(stderr, "%s(): error calling open_frontend()\n", +			__FUNCTION__); +		return -1; +	} + +	if(open_demux(&dmxfd)) { +		fprintf(stderr, "%s(): error calling open_demux()\n", +			__FUNCTION__); +		return -1; +	} + +	if(parse_stt(dmxfd)) { +		fprintf(stderr, "%s(): error calling parse_stt()\n", +			__FUNCTION__); +		return -1; +	} + +	if(parse_mgt(dmxfd)) { +		fprintf(stderr, "%s(): error calling parse_mgt()\n", +			__FUNCTION__); +		return -1; +	} + +	if(parse_tvct(dmxfd)) { +		fprintf(stderr, "%s(): error calling parse_tvct()\n", +			__FUNCTION__); +		return -1; +	} + +#ifdef ENABLE_RRT +	if(parse_rrt(dmxfd)) { +		fprintf(stderr, "%s(): error calling parse_rrt()\n", +			__FUNCTION__); +		return -1; +	} +#endif + +	fprintf(stdout, "receiving EIT "); +	for(i = 0; i < guide.ch[0].num_eits; i++) { +		if(parse_eit(dmxfd, i, guide.eit_pid[i])) { +			fprintf(stderr, "%s(): error calling parse_eit()\n", +				__FUNCTION__); +			return -1; +		} +	} +	fprintf(stdout, "\n"); + +	old_handler = signal(SIGINT, int_handler); +	if(enable_ett) { +		fprintf(stdout, "receiving ETT "); +		for(i = 0; i < guide.ch[0].num_eits; i++) { +			if(0xFFFF != guide.ett_pid[i]) { +				if(parse_ett(dmxfd, i, guide.ett_pid[i])) { +					fprintf(stderr, "%s(): error calling " +						"parse_eit()\n", __FUNCTION__); +					return -1; +				} +			} +			if(ctrl_c) { +				break; +			} +		} +		fprintf(stdout, "\n"); +	} +	signal(SIGINT, old_handler); + +	if(print_guide()) { +		fprintf(stderr, "%s(): error calling print_guide()\n", +			__FUNCTION__); +		return -1; +	} + +	if(cleanup_guide()) { +		fprintf(stderr, "%s(): error calling cleanup_guide()\n", +			__FUNCTION__); +		return -1; +	} + +	if(close_demux(dmxfd)) { +		fprintf(stderr, "%s(): error calling close_demux()\n", +			__FUNCTION__); +		return -1; +	} + +	if(close_frontend(fe)) { +		fprintf(stderr, "%s(): error calling close_demux()\n", +			__FUNCTION__); +		return -1; +	} + +	return 0; +} | 
