From patchwork Sat Jun 20 02:38:02 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yufei Yuan X-Patchwork-Id: 31518 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 n5K2cFdm014834 for ; Sat, 20 Jun 2009 02:38:15 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752241AbZFTCiK (ORCPT ); Fri, 19 Jun 2009 22:38:10 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1752786AbZFTCiK (ORCPT ); Fri, 19 Jun 2009 22:38:10 -0400 Received: from mail-gx0-f214.google.com ([209.85.217.214]:57612 "EHLO mail-gx0-f214.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752241AbZFTCiH (ORCPT ); Fri, 19 Jun 2009 22:38:07 -0400 Received: by gxk10 with SMTP id 10so3515115gxk.13 for ; Fri, 19 Jun 2009 19:38:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:subject:from:reply-to:to:cc :in-reply-to:references:content-type:organization:date:message-id :mime-version:x-mailer:content-transfer-encoding; bh=64bvmIcRzrYsH7zFa3k3c3aoTF1K9XWeaXOembFBt+E=; b=Pq1M2PidEHVUMN5LPyF7yv/cfSw38Bn2dYJbpMmijN00n3i+odiDjqsCbvZW9ilBOR 4bZmMSYiY32++osu/0RSxS4LQduakeKOujklterJN3XjzeYIyVHvnFkAlgfQhD9hu701 5RIARVRILLANK7Ym2JpvVpMFEE2HAqQU9YbbY= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=subject:from:reply-to:to:cc:in-reply-to:references:content-type :organization:date:message-id:mime-version:x-mailer :content-transfer-encoding; b=k0cank6qe/Qo8m8B4x1RJTAZ6gORoZlO8QkhmpMDJwNSFBc5AFlJ/NbUwQQT1n8uej XpLRmobtP0p3jtIuZ6Zgb6svWTR1DG+WtreT1QDTwIl8liO/izC9SqxQsCNM8ADHsFP/ 31NwO9oBct1OTqHCBYLh/1C8qz1tV0Fff81Q0= Received: by 10.90.118.8 with SMTP id q8mr2693127agc.114.1245465489587; Fri, 19 Jun 2009 19:38:09 -0700 (PDT) Received: from ?192.168.0.106? (cpe-70-124-70-245.austin.res.rr.com [70.124.70.245]) by mx.google.com with ESMTPS id 39sm454723agb.31.2009.06.19.19.38.07 (version=SSLv3 cipher=RC4-MD5); Fri, 19 Jun 2009 19:38:09 -0700 (PDT) Subject: Re: [Patch] New utility program atsc_epg added to dvb-apps utility suite. From: Yufei Yuan Reply-To: yfyuan@gmail.com To: hermann pitton Cc: Manu Abraham , Linux Media Mailing List In-Reply-To: <1245461313.3887.17.camel@pc07.localdom.local> References: <1a297b360906180132l49aa7be4j8a1e238aa9bac65@mail.gmail.com> <1a297b360906180148lefc2d8fp972647ad0df64320@mail.gmail.com> <1245458510.3887.10.camel@pc07.localdom.local> <1245461313.3887.17.camel@pc07.localdom.local> Organization: Illegally Blind Date: Fri, 19 Jun 2009 21:38:02 -0500 Message-Id: <1245465487.14312.17.camel@core2duo.localdomain> Mime-Version: 1.0 X-Mailer: Evolution 2.26.2 (2.26.2-1.fc11) Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Ok, let me first summarize what I have done in order not to waste your time again. I used Evolution client, used preformatted option, sent it to my other email box, forwarded it back and saved it as text file, then patched the original tree, so far everything looks okay. Hopefully you guys can do start to do next step. I do apologize for your wasted time. Signed-off-by: Yufei Yuan On Sat, 2009-06-20 at 03:28 +0200, hermann pitton wrote: > 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 --- 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-19 20:31:17.710924970 -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__); + } + + 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; +} + +#ifdef 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 = 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 + 1, "%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 $@