| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251 |
- /*
- * 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);
- if (title == NULL)
- continue;
- 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;
- }
|