diff mbox

New utility program atsc_epg added to dvb-apps utility suite.

Message ID ccdf9f470906181839h4047acc1t1d537300a0b4b581@mail.gmail.com (mailing list archive)
State Not Applicable
Delegated to: Mauro Carvalho Chehab
Headers show

Commit Message

Yufei Yuan June 19, 2009, 1:39 a.m. UTC
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

Comments

hermann pitton June 20, 2009, 12:41 a.m. UTC | #1
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 $@
>
Yufei Yuan June 20, 2009, 1 a.m. UTC | #2
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
hermann pitton June 20, 2009, 1:28 a.m. UTC | #3
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 mbox

Patch

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 $@