From patchwork Sat Jun 20 07:30:25 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manu Abraham X-Patchwork-Id: 31537 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n5K7UVUi021428 for ; Sat, 20 Jun 2009 07:30:31 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751321AbZFTHa0 (ORCPT ); Sat, 20 Jun 2009 03:30:26 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1751625AbZFTHa0 (ORCPT ); Sat, 20 Jun 2009 03:30:26 -0400 Received: from mail-bw0-f213.google.com ([209.85.218.213]:47085 "EHLO mail-bw0-f213.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750843AbZFTHaZ (ORCPT ); Sat, 20 Jun 2009 03:30:25 -0400 Received: by bwz9 with SMTP id 9so2145970bwz.37 for ; Sat, 20 Jun 2009 00:30:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:in-reply-to:references :date:message-id:subject:from:to:cc:content-type :content-transfer-encoding; bh=5wMmw2CJ8mGyBx6aeTE43ydsNazQyxwQLSovghMj/L8=; b=wVC0rrWLwJIzenS0j0loFZn3FA1yYMEb/qNvj5U0k/ZpHo1YTFOY6QnzLiJld5RiOT 5Amxq1GSfclg2KOFCHXFVWo3dY+GJJywkgpPdCsni7MZ5vDZPwypwxdvgLZvfNR312gj hZfqpd9rWe+Bc73mtX72ER0fbUFofKXzWla9I= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type:content-transfer-encoding; b=eY+g/UVphX7XX/KaNJYTjBZnIRtavnJcuFK7G/TbkYN6tDeCpd/+mk1N3VUivZxSvx EXWHb2GGQNRlNuycrXwWXZCgtGfuGqO1XZ6Hef2zoCgVreAW6WaT+4N2u2rXSev+ZjB8 bc+uLETyPvYyG47aXJr8TPHLB427I/RgI9VGo= MIME-Version: 1.0 Received: by 10.223.126.66 with SMTP id b2mr2906341fas.67.1245483025623; Sat, 20 Jun 2009 00:30:25 -0700 (PDT) In-Reply-To: References: <1a297b360906180132l49aa7be4j8a1e238aa9bac65@mail.gmail.com> <1a297b360906180148lefc2d8fp972647ad0df64320@mail.gmail.com> Date: Sat, 20 Jun 2009 11:30:25 +0400 Message-ID: <1a297b360906200030y1322de83j296ced63e713ef66@mail.gmail.com> Subject: Re: [Patch] New utility program atsc_epg added to dvb-apps utility suite. From: Manu Abraham To: Yufei Yuan Cc: Linux Media Mailing List Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org On Fri, Jun 19, 2009 at 5:55 AM, Yufei Yuan wrote: > Not sure how to make it look right in gmail, but the inline patch does > not look right to my eyes. I have the patch attached behind as a > backup. You can attach the patch, no worries. I have applied the patch, but the CodingStyle is not very nice, mostly the wrapping to 80 cols is done in a bad way. It makes the code not easy to read. Also have made some comments inline from your patch. Please do fix the mentioned ones against the dvb-apps head Regards, Manu --- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff -uprN dvb-apps/util/atsc_epg/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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ] -f [-p ]" + " [-m ] [-t] [-h]\n", program); +} + +static void help(void) +{ + fprintf(stderr, + "\nhelp:\n" + "%s [-a ] -f [-p ] [-m ] [-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__); + } We don't emphasise much on kernel Coding style in the application suite, but nevertheless following a CodingStyle helps to improve the readability of the code involved therewith. Please do use a space after the keyword, before and after the braces. if, for, while etc are keywords. For a single line conditionality check, You can omit the brace, involves lesser typing and looks lesser noise in the code. + + 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__); Please don't wrap lines like this. It makes the code readability very bad. There is not much of a restriction to limit the no. of cols to 80, though at some points of times that helps in some ways. Here but it really shows out that the wrapping to 80 cols makes it really bad. A very good example. + 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 $@