Message ID | ccdf9f470906181839h4047acc1t1d537300a0b4b581@mail.gmail.com (mailing list archive) |
---|---|
State | Not Applicable |
Delegated to: | Mauro Carvalho Chehab |
Headers | show |
Am Donnerstag, den 18.06.2009, 20:39 -0500 schrieb Yufei Yuan: > This one is about the utility itself. I do apologize for the length > here as "inline" patch is preferred according to the guide and I don't > have any public online storage. Please let me know if this causes any > inconvenience. > > Signed-off-by: Yufei Yuan <yfyuan@gmail.com> > > diff -uprN dvb-apps/util/atsc_epg/atsc_epg.c > dvb-apps_new/util/atsc_epg/atsc_epg.c > --- dvb-apps/util/atsc_epg/atsc_epg.c 1969-12-31 18:00:00.000000000 > -0600 > +++ dvb-apps_new/util/atsc_epg/atsc_epg.c 2009-06-18 > 20:17:24.527925142 -0500 > @@ -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; > +} > diff -uprN dvb-apps/util/atsc_epg/Makefile > dvb-apps_new/util/atsc_epg/Makefile > --- dvb-apps/util/atsc_epg/Makefile 1969-12-31 18:00:00.000000000 > -0600 > +++ dvb-apps_new/util/atsc_epg/Makefile 2009-06-18 20:11:58.362985962 > -0500 > @@ -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 -uprN dvb-apps/util/atsc_epg/README dvb-apps_new/util/atsc_epg/README > --- dvb-apps/util/atsc_epg/README 1969-12-31 18:00:00.000000000 -0600 > +++ dvb-apps_new/util/atsc_epg/README 2009-06-18 20:33:47.836924378 -0500 > @@ -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 -uprN dvb-apps/util/Makefile dvb-apps_new/util/Makefile > --- dvb-apps/util/Makefile 2009-06-18 19:43:30.034986539 -0500 > +++ dvb-apps_new/util/Makefile 2009-06-18 20:11:41.169986806 -0500 > @@ -3,6 +3,7 @@ > .PHONY: all clean install > > all clean install: > + $(MAKE) -C atsc_epg $@ > $(MAKE) -C av7110_loadkeys $@ > $(MAKE) -C dib3000-watch $@ > $(MAKE) -C dst-utils $@ >
Thanks for your time. It's my first time to do this, so I have been trying to follow literally on the wiki page to do it right. If you can elaborate a bit about what is broken? Is it the patch created incorrectly, or it is pasted incorrectly, or the style is still problematic? I noticed that cutting and pasting from my console to the gmail compose window does not seem working alright. How do you normally do the inlining? I have a full weekend to do this, and I do realize from the wiki page that it does not appear to be simple, :) I now simply disable the footer, don't worry. Regards, On Fri, Jun 19, 2009 at 7:41 PM, hermann pitton<hermann-pitton@arcor.de> wrote: > > Am Donnerstag, den 18.06.2009, 20:39 -0500 schrieb Yufei Yuan: >> This one is about the utility itself. I do apologize for the length >> here as "inline" patch is preferred according to the guide and I don't >> have any public online storage. Please let me know if this causes any >> inconvenience. >> >> Signed-off-by: Yufei Yuan <yfyuan@gmail.com> >> >> diff -uprN dvb-apps/util/atsc_epg/atsc_epg.c >> dvb-apps_new/util/atsc_epg/atsc_epg.c >> --- dvb-apps/util/atsc_epg/atsc_epg.c  1969-12-31 18:00:00.000000000 >> -0600 >> +++ dvb-apps_new/util/atsc_epg/atsc_epg.c    2009-06-18 >> 20:17:24.527925142 -0500 >> @@ -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; >> +} >> diff -uprN dvb-apps/util/atsc_epg/Makefile >> dvb-apps_new/util/atsc_epg/Makefile >> --- dvb-apps/util/atsc_epg/Makefile   1969-12-31 18:00:00.000000000 >> -0600 >> +++ dvb-apps_new/util/atsc_epg/Makefile 2009-06-18 20:11:58.362985962 >> -0500 >> @@ -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 -uprN dvb-apps/util/atsc_epg/README dvb-apps_new/util/atsc_epg/README >> --- dvb-apps/util/atsc_epg/README    1969-12-31 18:00:00.000000000 -0600 >> +++ dvb-apps_new/util/atsc_epg/README  2009-06-18 20:33:47.836924378 -0500 >> @@ -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 -uprN dvb-apps/util/Makefile dvb-apps_new/util/Makefile >> --- dvb-apps/util/Makefile    2009-06-18 19:43:30.034986539 -0500 >> +++ dvb-apps_new/util/Makefile  2009-06-18 20:11:41.169986806 -0500 >> @@ -3,6 +3,7 @@ >>  .PHONY: all clean install >> >>  all clean install: >> +    $(MAKE) -C atsc_epg $@ >>     $(MAKE) -C av7110_loadkeys $@ >>     $(MAKE) -C dib3000-watch $@ >>     $(MAKE) -C dst-utils $@ >> > > -- > Even uttering "HI" or "HAO" is offensive, sometime, somewhere. Reader > discretion is advised. > -- > > My, the patch is still pretty well broken. > > I leave it to Manu to help you further in the submission procedures ;) > > As said, keep your Confucius stuff if you like. > > I won't rant a second time on it. > > But don't ever complain here, if others do have some good arguments too, > in their footers ... > > Cheers, > Hermann > > > > > > > > > -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, Am Freitag, den 19.06.2009, 20:00 -0500 schrieb Yufei Yuan: > Thanks for your time. It's my first time to do this, so I have been > trying to follow literally on the wiki page to do it right. If you can > elaborate a bit about what is broken? Is it the patch created > incorrectly, or it is pasted incorrectly, or the style is still > problematic? > > I noticed that cutting and pasting from my console to the gmail > compose window does not seem working alright. How do you normally do > the inlining? > > I have a full weekend to do this, and I do realize from the wiki page > that it does not appear to be simple, :) > > I now simply disable the footer, don't worry. Just keep it on your decision. I'm not against to learn from the past. It starts with lots of broken lines. Cheers, Hermann > > Regards, > > On Fri, Jun 19, 2009 at 7:41 PM, hermann pitton<hermann-pitton@arcor.de> wrote: > > > > Am Donnerstag, den 18.06.2009, 20:39 -0500 schrieb Yufei Yuan: > >> This one is about the utility itself. I do apologize for the length > >> here as "inline" patch is preferred according to the guide and I don't > >> have any public online storage. Please let me know if this causes any > >> inconvenience. > >> > >> Signed-off-by: Yufei Yuan <yfyuan@gmail.com> > >> > >> diff -uprN dvb-apps/util/atsc_epg/atsc_epg.c > >> dvb-apps_new/util/atsc_epg/atsc_epg.c > >> --- dvb-apps/util/atsc_epg/atsc_epg.c 1969-12-31 18:00:00.000000000 > >> -0600 > >> +++ dvb-apps_new/util/atsc_epg/atsc_epg.c 2009-06-18 > >> 20:17:24.527925142 -0500 > >> @@ -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; > >> +} > >> diff -uprN dvb-apps/util/atsc_epg/Makefile > >> dvb-apps_new/util/atsc_epg/Makefile > >> --- dvb-apps/util/atsc_epg/Makefile 1969-12-31 18:00:00.000000000 > >> -0600 > >> +++ dvb-apps_new/util/atsc_epg/Makefile 2009-06-18 20:11:58.362985962 > >> -0500 > >> @@ -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 -uprN dvb-apps/util/atsc_epg/README dvb-apps_new/util/atsc_epg/README > >> --- dvb-apps/util/atsc_epg/README 1969-12-31 18:00:00.000000000 -0600 > >> +++ dvb-apps_new/util/atsc_epg/README 2009-06-18 20:33:47.836924378 -0500 > >> @@ -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 -uprN dvb-apps/util/Makefile dvb-apps_new/util/Makefile > >> --- dvb-apps/util/Makefile 2009-06-18 19:43:30.034986539 -0500 > >> +++ dvb-apps_new/util/Makefile 2009-06-18 20:11:41.169986806 -0500 > >> @@ -3,6 +3,7 @@ > >> .PHONY: all clean install > >> > >> all clean install: > >> + $(MAKE) -C atsc_epg $@ > >> $(MAKE) -C av7110_loadkeys $@ > >> $(MAKE) -C dib3000-watch $@ > >> $(MAKE) -C dst-utils $@ > >> > > > > -- > > Even uttering "HI" or "HAO" is offensive, sometime, somewhere. Reader > > discretion is advised. > > -- > > > > My, the patch is still pretty well broken. > > > > I leave it to Manu to help you further in the submission procedures ;) > > > > As said, keep your Confucius stuff if you like. > > > > I won't rant a second time on it. > > > > But don't ever complain here, if others do have some good arguments too, > > in their footers ... > > > > Cheers, > > Hermann > > > > > > > > > > > > > > > > > > -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff -uprN dvb-apps/util/atsc_epg/README dvb-apps_new/util/atsc_epg/README --- dvb-apps/util/atsc_epg/README 1969-12-31 18:00:00.000000000 -0600 +++ dvb-apps_new/util/atsc_epg/README 2009-06-18 20:33:47.836924378 -0500 @@ -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 -uprN dvb-apps/util/Makefile dvb-apps_new/util/Makefile --- dvb-apps/util/Makefile 2009-06-18 19:43:30.034986539 -0500 +++ dvb-apps_new/util/Makefile 2009-06-18 20:11:41.169986806 -0500 @@ -3,6 +3,7 @@ .PHONY: all clean install all clean install: + $(MAKE) -C atsc_epg $@ $(MAKE) -C av7110_loadkeys $@ $(MAKE) -C dib3000-watch $@ $(MAKE) -C dst-utils $@
This one is about the utility itself. I do apologize for the length here as "inline" patch is preferred according to the guide and I don't have any public online storage. Please let me know if this causes any inconvenience. Signed-off-by: Yufei Yuan <yfyuan@gmail.com> diff -uprN dvb-apps/util/atsc_epg/atsc_epg.c dvb-apps_new/util/atsc_epg/atsc_epg.c --- dvb-apps/util/atsc_epg/atsc_epg.c 1969-12-31 18:00:00.000000000 -0600 +++ dvb-apps_new/util/atsc_epg/atsc_epg.c 2009-06-18 20:17:24.527925142 -0500 @@ -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; +} diff -uprN dvb-apps/util/atsc_epg/Makefile dvb-apps_new/util/atsc_epg/Makefile --- dvb-apps/util/atsc_epg/Makefile 1969-12-31 18:00:00.000000000 -0600 +++ dvb-apps_new/util/atsc_epg/Makefile 2009-06-18 20:11:58.362985962 -0500 @@ -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