From 5f90b5667bda4e5a1962fe99ddc21c231f3b5260 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sun, 29 Sep 2013 19:04:20 +0200 Subject: [PATCH] Initial commit. --- decode_sdt.c | 421 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 decode_sdt.c diff --git a/decode_sdt.c b/decode_sdt.c new file mode 100644 index 0000000..7f336f8 --- /dev/null +++ b/decode_sdt.c @@ -0,0 +1,421 @@ +/* + * Decode SDT and NIT from a TS stream (for instance dvr0), and output in a + * relatively machine-readable format. + * + * Based on the decode_sdt.c example from libdvbpsi, and uses some source code + * from MuMuDVB. + * + * Copyright (C): + * 2001-2011 VideoLAN + * Dave Chapman 2001, 2002. + * 2004-2011 Brice DUBOST + * 2013 Steinar H. Gunderson + * + * 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 + +static int packet_num = 0; +static int last_used_packet_num = 0; + +/** +@brief The different encodings that can be used +Cf EN 300 468 Annex A (I used v1.9.1) + */ +static const char *encodings_en300468[] ={ + "ISO8859-1", + "ISO8859-2", + "ISO8859-3", + "ISO8859-4", + "ISO8859-5", + "ISO8859-6", + "ISO8859-7", + "ISO8859-8", + "ISO8859-9", + "ISO8859-10", + "ISO8859-11", + "ISO8859-12", + "ISO8859-13", + "ISO8859-14", + "ISO8859-15", + "ISO-10646", //control char 0x11 + "GB2312", //control char 0x13 + "BIG5", //control char 0x14 + "ISO-10646/UTF8", //control char 0x15 +}; + +// Convert text according to EN 300 468 annex A. +static int convert_en300468_string(char *string, int max_len) +{ + int encoding_control_char = 8; //cf encodings_en300468 + char *dest; + char *tempdest, *tempbuf; + unsigned char *src; + /* remove control characters and convert to UTF-8 the channel name */ + //If no channel encoding is specified, it seems that most of the broadcasters + //uses ISO/IEC 8859-9. But the norm (EN 300 468) said that it should be Latin-1 (ISO/IEC 6937 + euro) + + //temporary buffers allocation + tempdest = tempbuf = (char *)malloc(sizeof(char)*2*strlen(string)); + if (tempdest==NULL) { + return -1; + } + + int len = 0; + for (src = (unsigned char *)string; *src; src++) { + if (*src >= 0x20 && (*src < 0x80 || *src > 0x9f)) { + //We copy non-control characters + *tempdest++ = *src; + len++; + } else if (*src <= 0x20) { + //control character recognition based on EN 300 468 v1.9.1 Annex A + if (*src <= 0x0b) { + encoding_control_char=(int) *src+4-1; + } else if(*src == 0x10) { //ISO/IEC 8859 : See table A.4 + src++;//we skip the current byte + src++;//This one is always set to 0 + if (*src >= 0x01 && *src <=0x0f) + encoding_control_char=(int) *src-1; + } + else if (*src==0x11) // ISO/IEC 10646 : Basic Multilingual Plane + encoding_control_char = 15; + else if (*src==0x13) // GB-2312-1980 : Simplified Chinese Character + encoding_control_char = 16; + else if (*src==0x14) // Big5 subset of ISO/IEC 10646 : Traditional Chinese + encoding_control_char = 17; + else if (*src==0x15) // UTF-8 encoding of ISO/IEC 10646 : Basic Multilingual Plane + encoding_control_char = 18; + } + } + //Conversion to utf8 + iconv_t cd; + //we open the conversion table + cd = iconv_open("UTF8", encodings_en300468[encoding_control_char]); + + size_t inSize, outSize = max_len; + inSize = len; + //pointers initialisation because iconv change them, we store + dest = string; + tempdest = tempbuf; + //conversion + iconv(cd, &tempdest, &inSize, &dest, &outSize); + *dest = '\0'; + free(tempbuf); + iconv_close(cd); + + return encoding_control_char; +} + + +static bool ReadPacket(int i_fd, uint8_t * p_dst) +{ + static uint8_t buf[188 * 100]; + static int in_buf_start = 0; + static int in_buf_end = 0; + +has_stuff: + // Skip to the first 0x47 byte. + while (in_buf_end > in_buf_start && buf[in_buf_start] != 0x47) { + ++in_buf_start; + } + + if (in_buf_end - in_buf_start >= 188) { + memcpy(p_dst, buf + in_buf_start, 188); + in_buf_start += 188; + return true; + } + + // Check if there's a partial packet in the buffer. + int partial_len = in_buf_end - in_buf_start; + if (partial_len == 0) { + in_buf_start = in_buf_end = 0; + } else if (partial_len > 0) { + memmove(buf, buf + in_buf_start, partial_len); + in_buf_start = 0; + in_buf_end = partial_len; + } + + int ret = read(i_fd, buf + in_buf_start, sizeof(buf) - in_buf_end); + if (ret == -1) { + perror("read"); + return false; + } + if (ret == 0) { + return false; + } + + in_buf_end += ret; + + goto has_stuff; +} + + +static void ParseSDT(void *p_zero, dvbpsi_sdt_t * p_sdt) +{ + last_used_packet_num = packet_num; + dvbpsi_sdt_service_t *p_service = p_sdt->p_first_service; + for (dvbpsi_sdt_service_t * p_service = p_sdt->p_first_service; + p_service != NULL; + p_service = p_service->p_next) { + int service_type = -1; + char provider_name[256] = { 0 }; + char channel_name[256] = { 0 }; + dvbpsi_descriptor_t *p_descriptor = + p_service->p_first_descriptor; + + // Search for a Service Descriptor (0x48), which contains the provider and channel names. + for (dvbpsi_descriptor_t * p_descriptor = p_service->p_first_descriptor; + p_descriptor != NULL; + p_descriptor = p_descriptor->p_next) { + uint8_t *ptr; + int i, len; + if (p_descriptor->i_tag != 0x48) { + continue; + } + service_type = p_descriptor->p_data[0]; + ptr = p_descriptor->p_data + 1; + + len = *ptr++; + memcpy(provider_name, ptr, len); + provider_name[len] = 0; + ptr += len; + + len = *ptr++; + memcpy(channel_name, ptr, len); + channel_name[len] = 0; + + convert_en300468_string(provider_name, sizeof(provider_name)); + convert_en300468_string(channel_name, sizeof(channel_name)); + } + + // Show television types only. + if (service_type == 0x01 || service_type == 0x11 || service_type == 0x16 || + service_type == 0x19 || service_type == 0x1c) { + printf("sid %d/%d: ts_id=%d, provider_name=\"%s\", channel_name=\"%s\"\n", + p_sdt->i_network_id, p_service->i_service_id, + p_sdt->i_extension, provider_name, + channel_name); + } + } + dvbpsi_sdt_delete(p_sdt); +} + +// little endian assumed +typedef struct { + uint8_t frequency_4; + uint8_t frequency_3; + uint8_t frequency_2; + uint8_t frequency_1; + uint16_t orbital_position; // big-endian + uint8_t modulation_type : 2; + uint8_t modulation_system : 1; + uint8_t roll_off : 2; + uint8_t polarization : 2; + uint8_t west_east_flag : 1; + uint8_t symbol_rate_12; + uint8_t symbol_rate_34; + uint8_t symbol_rate_56; + uint8_t FEC_inner : 4; + uint8_t symbol_rate_7 : 4; +} descr_sat_delivery_t; + +static void ParseNIT(void *p_zero, dvbpsi_nit_t * p_nit) +{ + last_used_packet_num = packet_num; + for (dvbpsi_nit_ts_t *p_ts = p_nit->p_first_ts; + p_ts != NULL; + p_ts = p_ts->p_next) { + // Search through the descriptors until we find one of type 0x43 + // (Delivery System Descriptor). + for (dvbpsi_descriptor_t *p_descriptor = p_ts->p_first_descriptor; + p_descriptor != NULL; + p_descriptor = p_descriptor->p_next) { + char buf[256], *ptr; + int i, len; + if (p_descriptor->i_tag != 0x43) { + continue; + } + + descr_sat_delivery_t *d = + (descr_sat_delivery_t *) p_descriptor->p_data; + printf("ts %d/%d: ", p_nit->i_network_id, p_ts->i_ts_id); + printf("freq=%x%02x%02x, ", d->frequency_4, + d->frequency_3, d->frequency_2, + d->frequency_1); + printf("pol=%c, ", "HVLR"[d->polarization]); + if (d->modulation_system) { + printf("delivery_system=DVBS2, "); + } else { + printf("delivery_system=DVBS, "); + } + if (d->modulation_type == 0) { + printf("modulation=QAMAUTO, "); + } else if (d->modulation_type == 1) { + printf("modulation=QPSK, "); + } else if (d->modulation_type == 2) { + printf("modulation=8PSK, "); + } else if (d->modulation_type == 3) { + printf("modulation=QAM64, "); + } + printf("srate=%x%02x%02x, ", d->symbol_rate_12, d->symbol_rate_34, d->symbol_rate_56); + if (d->FEC_inner == 0) { + printf("coderate=auto"); + } else if (d->FEC_inner == 1) { + printf("coderate=1/2"); + } else if (d->FEC_inner == 2) { + printf("coderate=2/3"); + } else if (d->FEC_inner == 3) { + printf("coderate=3/4"); + } else if (d->FEC_inner == 4) { + printf("coderate=5/6"); + } else if (d->FEC_inner == 5) { + printf("coderate=7/8"); + } else if (d->FEC_inner == 6) { + printf("coderate=8/9"); + } else if (d->FEC_inner == 7) { + printf("coderate=3/5"); + } else if (d->FEC_inner == 8) { + printf("coderate=4/5"); + } else if (d->FEC_inner == 9) { + printf("coderate=9/10"); + } else { + printf("coderate=none"); + } + printf("\n"); + break; + } + } + dvbpsi_nit_delete(p_nit); +} + +/***************************************************************************** + * DVBPSI messaging callback + *****************************************************************************/ +static void message(dvbpsi_t * handle, const dvbpsi_msg_level_t level, + const char *msg) +{ + switch (level) { + case DVBPSI_MSG_ERROR: + fprintf(stderr, "Error: "); + break; + case DVBPSI_MSG_WARN: + fprintf(stderr, "Warning: "); + break; + case DVBPSI_MSG_DEBUG: + fprintf(stderr, "Debug: "); + break; + default: /* do nothing */ + return; + } + fprintf(stderr, "%s\n", msg); +} + +/***************************************************************************** + * NewSubtable + *****************************************************************************/ +static void NewSubtableNIT(dvbpsi_t * p_dvbpsi, uint8_t i_table_id, + uint16_t i_extension, void *p_zero) +{ + if (i_table_id == 0x40) { + if (!dvbpsi_nit_attach(p_dvbpsi, i_table_id, i_extension, ParseNIT, NULL)) + fprintf(stderr, "Failed to attach PSI subdecoder\n"); + } +} + +/***************************************************************************** + * NewSubtable + *****************************************************************************/ +static void NewSubtableSDT(dvbpsi_t * p_dvbpsi, uint8_t i_table_id, + uint16_t i_extension, void *p_zero) +{ + if (i_table_id == 0x42 || i_table_id == 0x46) { + if (!dvbpsi_sdt_attach(p_dvbpsi, i_table_id, i_extension, ParseSDT, NULL)) + fprintf(stderr, "Failed to attach SDT subdecoder\n"); + } +} + +/***************************************************************************** + * main + *****************************************************************************/ +int main(int i_argc, char *pa_argv[]) +{ + int i_fd; + uint8_t data[188]; + dvbpsi_t *p_dvbpsi_sdt, *p_dvbpsi_nit; + bool b_ok; + + if (i_argc != 2) + return 1; + + i_fd = open(pa_argv[1], 0); + if (i_fd < 0) + return 1; + + p_dvbpsi_sdt = dvbpsi_new(&message, DVBPSI_MSG_ERROR); + if (p_dvbpsi_sdt == NULL) + goto out; + + p_dvbpsi_nit = dvbpsi_new(&message, DVBPSI_MSG_ERROR); + if (p_dvbpsi_nit == NULL) + goto out; + + if (!dvbpsi_AttachDemux(p_dvbpsi_sdt, NewSubtableSDT, NULL)) + goto out; + + if (!dvbpsi_AttachDemux(p_dvbpsi_nit, NewSubtableNIT, NULL)) + goto out; + + while (ReadPacket(i_fd, data)) { + uint16_t i_pid = ((uint16_t)(data[1] & 0x1f) << 8) + data[2]; + if (i_pid == 0x10) + dvbpsi_packet_push(p_dvbpsi_nit, data); + if (i_pid == 0x11) + dvbpsi_packet_push(p_dvbpsi_sdt, data); + + // After 100k packets with no new information, we assume we are done. + ++packet_num; + if ((packet_num - last_used_packet_num) >= 100000) + break; + } + +out: + if (p_dvbpsi_sdt) { + dvbpsi_DetachDemux(p_dvbpsi_sdt); + dvbpsi_delete(p_dvbpsi_sdt); + } + if (p_dvbpsi_nit) { + dvbpsi_DetachDemux(p_dvbpsi_nit); + dvbpsi_delete(p_dvbpsi_nit); + } + close(i_fd); + return 0; +} -- 2.39.2