From 0403c639f3cbae2f81c54dad040076fec348a092 Mon Sep 17 00:00:00 2001 From: Christophe Massiot Date: Mon, 16 Aug 2010 16:59:19 +0000 Subject: [PATCH] * mpeg/psi.h: Fix a bug with descriptor 0x05. * mpeg/psi.h, dvb/si.h: Cosmetic changes in print functions. * examples/dvb_print_si.c: New example file to print DVB tables in a TS stream. --- dvb/si.h | 4 +- examples/Makefile | 13 ++ examples/dvb_print_si.c | 443 ++++++++++++++++++++++++++++++++++++++++ mpeg/psi.h | 23 ++- mpeg/psi_print.h | 15 +- 5 files changed, 482 insertions(+), 16 deletions(-) create mode 100644 examples/Makefile create mode 100644 examples/dvb_print_si.c diff --git a/dvb/si.h b/dvb/si.h index c349ba3..f3eafa3 100644 --- a/dvb/si.h +++ b/dvb/si.h @@ -166,7 +166,7 @@ static inline void desc56_print(uint8_t *p_desc, f_print pf_print, while ((p_desc_n = desc56_get_language(p_desc, j)) != NULL) { j++; pf_print(opaque, - " - desc 56 telx language=%3.3s type=%hhu mag=%hhu page=%hhu", + " - desc 56 telx language=%3.3s type=0x%hhx mag=%hhu page=%hhu", (const char *)desc56n_get_code(p_desc_n), desc56n_get_teletexttype(p_desc_n), desc56n_get_teletextmagazine(p_desc_n), @@ -239,7 +239,7 @@ static inline void desc59_print(uint8_t *p_desc, f_print pf_print, while ((p_desc_n = desc59_get_language(p_desc, j)) != NULL) { j++; pf_print(opaque, - " - desc 59 dvbs language=%3.3s type=%hhu composition=%hu ancillary=%hu", + " - desc 59 dvbs language=%3.3s type=0x%hhx composition=%hu ancillary=%hu", (const char *)desc59n_get_code(p_desc_n), desc59n_get_subtitlingtype(p_desc_n), desc59n_get_compositionpage(p_desc_n), diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..d4d1a55 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,13 @@ +# $Id$ + +CFLAGS = -Wall -O2 +LDFLAGS = +OBJ = dvb_print_si + +all: $(OBJ) + +%: %.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + +clean: + -rm $(OBJ) diff --git a/examples/dvb_print_si.c b/examples/dvb_print_si.c new file mode 100644 index 0000000..9c68866 --- /dev/null +++ b/examples/dvb_print_si.c @@ -0,0 +1,443 @@ +/***************************************************************************** + * dvb_print_si.c: Prints tables from a TS file + ***************************************************************************** + * Copyright (C) 2010 VideoLAN + * $Id$ + * + * Authors: Christophe Massiot + * + * This program is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/***************************************************************************** + * Local declarations + *****************************************************************************/ +#define MAX_PIDS 8192 +#define READ_ONCE 7 + +typedef struct ts_pid_t { + int i_psi_refcount; + int8_t i_last_cc; + + /* biTStream PSI section gathering */ + uint8_t *p_psi_buffer; + uint16_t i_psi_buffer_used; +} ts_pid_t; + +typedef struct sid_t { + uint16_t i_sid, i_pmt_pid; + uint8_t *p_current_pmt; +} sid_t; + +ts_pid_t p_pids[MAX_PIDS]; +static sid_t **pp_sids = NULL; +static int i_nb_sids = 0; + +static PSI_TABLE_DECLARE(pp_current_pat_sections); +static PSI_TABLE_DECLARE(pp_next_pat_sections); +static PSI_TABLE_DECLARE(pp_current_nit_sections); +static PSI_TABLE_DECLARE(pp_next_nit_sections); +static PSI_TABLE_DECLARE(pp_current_sdt_sections); +static PSI_TABLE_DECLARE(pp_next_sdt_sections); + +/***************************************************************************** + * print + *****************************************************************************/ +static void print(void *_unused, const char *psz_format, ...) +{ + char psz_fmt[strlen(psz_format) + 2]; + va_list args; + va_start(args, psz_format); + strcpy(psz_fmt, psz_format); + strcat(psz_fmt, "\n"); + vprintf(psz_fmt, args); +} + +/***************************************************************************** + * handle_pat + *****************************************************************************/ +static void handle_pat(void) +{ + PSI_TABLE_DECLARE(pp_old_pat_sections); + uint8_t i_last_section = psi_table_get_lastsection(pp_next_pat_sections); + uint8_t i; + + if (psi_table_validate(pp_current_pat_sections) && + psi_table_get_version(pp_current_pat_sections) + == psi_table_get_version(pp_next_pat_sections)) { + /* Same version PAT. Shortcut. */ + psi_table_free(pp_next_pat_sections); + psi_table_init(pp_next_pat_sections); + return; + } + + if (!pat_table_validate(pp_next_pat_sections)) { + printf("invalid PAT received"); + psi_table_free(pp_next_pat_sections); + psi_table_init(pp_next_pat_sections); + return; + } + + /* Switch tables. */ + psi_table_copy(pp_old_pat_sections, pp_current_pat_sections); + psi_table_copy(pp_current_pat_sections, pp_next_pat_sections); + psi_table_init(pp_next_pat_sections); + + for (i = 0; i <= i_last_section; i++) { + uint8_t *p_section = psi_table_get_section(pp_current_pat_sections, i); + const uint8_t *p_program; + int j = 0; + + while ((p_program = pat_get_program(p_section, j)) != NULL) { + const uint8_t *p_old_program = NULL; + uint16_t i_sid = patn_get_program(p_program); + uint16_t i_pid = patn_get_pid(p_program); + j++; + + if (i_sid == 0) { + if (i_pid != NIT_PID) + printf( + "NIT is carried on PID %hu which isn't DVB compliant", + i_pid); + continue; /* NIT */ + } + + if (!psi_table_validate(pp_old_pat_sections) + || (p_old_program = + pat_table_find_program(pp_old_pat_sections, i_sid)) + == NULL + || patn_get_pid(p_old_program) != i_pid) { + sid_t *p_sid; + int i_pmt; + if (p_old_program != NULL) + p_pids[patn_get_pid(p_old_program)].i_psi_refcount--; + p_pids[i_pid].i_psi_refcount++; + + for (i_pmt = 0; i_pmt < i_nb_sids; i_pmt++) + if (pp_sids[i_pmt]->i_sid == i_sid || + pp_sids[i_pmt]->i_sid == 0) + break; + + if (i_pmt == i_nb_sids) { + p_sid = malloc(sizeof(sid_t)); + pp_sids = realloc(pp_sids, ++i_nb_sids * sizeof(sid_t *)); + pp_sids[i_pmt] = p_sid; + p_sid->p_current_pmt = NULL; + } + else + p_sid = pp_sids[i_pmt]; + + p_sid->i_sid = i_sid; + p_sid->i_pmt_pid = i_pid; + } + } + } + + if (psi_table_validate(pp_old_pat_sections)) { + i_last_section = psi_table_get_lastsection( pp_old_pat_sections ); + for (i = 0; i <= i_last_section; i++) { + uint8_t *p_section = psi_table_get_section(pp_old_pat_sections, i); + const uint8_t *p_program; + int j = 0; + + while ((p_program = pat_get_program(p_section, j)) != NULL) { + uint16_t i_sid = patn_get_program(p_program); + j++; + + if (i_sid == 0) + continue; /* NIT */ + + if (pat_table_find_program(pp_current_pat_sections, i_sid) + == NULL) { + int i_pmt; + for (i_pmt = 0; i_pmt < i_nb_sids; i_pmt++) + if (pp_sids[i_pmt]->i_sid == i_sid) { + pp_sids[i_pmt]->i_sid = 0; + free(pp_sids[i_pmt]->p_current_pmt); + break; + } + } + } + } + + psi_table_free(pp_old_pat_sections); + } + + pat_table_print( pp_current_pat_sections, print, NULL ); +} + +static void handle_pat_section(uint16_t i_pid, uint8_t *p_section) +{ + if (i_pid != PAT_PID || !pat_validate(p_section)) { + printf("invalid PAT section received on PID %hu", i_pid); + free(p_section); + return; + } + + if (!psi_table_section(pp_next_pat_sections, p_section)) + return; + + handle_pat(); +} + +/***************************************************************************** + * handle_pmt + *****************************************************************************/ +static void handle_pmt(uint16_t i_pid, uint8_t *p_pmt) +{ + uint16_t i_sid = pmt_get_program(p_pmt); + sid_t *p_sid; + int i; + + /* we do this before checking the service ID */ + if (!pmt_validate(p_pmt)) { + printf("invalid PMT section received on PID %hu", i_pid); + free(p_pmt); + return; + } + + for (i = 0; i < i_nb_sids; i++) + if (pp_sids[i]->i_sid && pp_sids[i]->i_sid == i_sid) + break; + + if (i == i_nb_sids) { + printf("ghost PMT for service %hu carried on PID %hu", i_sid, i_pid); + p_sid = malloc(sizeof(sid_t)); + pp_sids = realloc(pp_sids, ++i_nb_sids * sizeof(sid_t *)); + pp_sids[i] = p_sid; + p_sid->i_sid = i_sid; + p_sid->i_pmt_pid = i_pid; + p_sid->p_current_pmt = NULL; + } else { + p_sid = pp_sids[i]; + if (i_pid != p_sid->i_pmt_pid) + printf("ghost PMT for service %hu carried on PID %hu", i_sid, + i_pid); + } + + if (p_sid->p_current_pmt != NULL && + psi_get_version(p_sid->p_current_pmt) == psi_get_version(p_pmt)) { + /* Same version PMT. Shortcut. */ + free(p_pmt); + return; + } + + free(p_sid->p_current_pmt); + p_sid->p_current_pmt = p_pmt; + + pmt_print(p_pmt, print, NULL); +} + +/***************************************************************************** + * handle_nit + *****************************************************************************/ +static void handle_nit(void) +{ + if (psi_table_validate(pp_current_nit_sections) && + psi_table_get_version(pp_current_nit_sections) + == psi_table_get_version(pp_next_nit_sections)) { + /* Same version NIT. Shortcut. */ + psi_table_free(pp_next_nit_sections); + psi_table_init(pp_next_nit_sections); + return; + } + + if (!nit_table_validate(pp_next_nit_sections)) { + printf("invalid NIT received"); + psi_table_free( pp_next_nit_sections ); + psi_table_init( pp_next_nit_sections ); + return; + } + + /* Switch tables. */ + psi_table_free(pp_current_nit_sections); + psi_table_copy(pp_current_nit_sections, pp_next_nit_sections); + psi_table_init(pp_next_nit_sections); + + nit_table_print(pp_current_nit_sections, print, NULL); +} + +static void handle_nit_section(uint16_t i_pid, uint8_t *p_section) +{ + if (i_pid != NIT_PID || !nit_validate(p_section)) { + printf("invalid NIT section received on PID %hu", i_pid); + free(p_section); + return; + } + + if (!psi_table_section(pp_next_nit_sections, p_section)) + return; + + handle_nit(); +} + +/***************************************************************************** + * handle_sdt + *****************************************************************************/ +static void handle_sdt(void) +{ + if (psi_table_validate(pp_current_sdt_sections) && + psi_table_get_version(pp_current_sdt_sections) + == psi_table_get_version(pp_next_sdt_sections)) { + /* Same version SDT. Shortcut. */ + psi_table_free(pp_next_sdt_sections); + psi_table_init(pp_next_sdt_sections); + return; + } + + if (!sdt_table_validate(pp_next_sdt_sections)) { + printf("invalid SDT received"); + psi_table_free(pp_next_sdt_sections); + psi_table_init(pp_next_sdt_sections); + return; + } + + /* Switch tables. */ + psi_table_free(pp_current_sdt_sections); + psi_table_copy(pp_current_sdt_sections, pp_next_sdt_sections); + psi_table_init(pp_next_sdt_sections); + + sdt_table_print(pp_current_sdt_sections, print, NULL); +} + +static void handle_sdt_section(uint16_t i_pid, uint8_t *p_section) +{ + if (i_pid != SDT_PID || !sdt_validate(p_section)) { + printf("invalid SDT section received on PID %hu", i_pid); + free(p_section); + return; + } + + if (!psi_table_section(pp_next_sdt_sections, p_section)) + return; + + handle_sdt(); +} + +/***************************************************************************** + * handle_section + *****************************************************************************/ +static void handle_section(uint16_t i_pid, uint8_t *p_section) +{ + uint8_t i_table_id = psi_get_tableid(p_section); + + if (!psi_validate(p_section)) { + printf("invalid section on PID %hu", i_pid); + free(p_section); + return; + } + + switch (i_table_id) { + case PAT_TABLE_ID: + handle_pat_section(i_pid, p_section); + break; + + case PMT_TABLE_ID: + handle_pmt(i_pid, p_section); + break; + + case NIT_TABLE_ID_ACTUAL: + handle_nit_section(i_pid, p_section); + break; + + case SDT_TABLE_ID_ACTUAL: + handle_sdt_section(i_pid, p_section); + break; + + default: + free( p_section ); + break; + } +} + +/***************************************************************************** + * handle_psi_packet + *****************************************************************************/ +static void handle_psi_packet(uint8_t *p_ts) +{ + uint16_t i_pid = ts_get_pid(p_ts); + ts_pid_t *p_pid = &p_pids[i_pid]; + uint8_t i_cc = ts_get_cc(p_ts); + const uint8_t *p_payload = ts_payload(p_ts); + uint8_t i_length; + + if (ts_check_duplicate(i_cc, p_pid->i_last_cc) || !ts_has_payload(p_ts)) + return; + + if (p_pid->i_last_cc != -1 + && ts_check_discontinuity(i_cc, p_pid->i_last_cc)) + psi_assemble_reset(&p_pid->p_psi_buffer, &p_pid->i_psi_buffer_used); + + if (psi_assemble_empty(&p_pid->p_psi_buffer, &p_pid->i_psi_buffer_used)) + p_payload = ts_section(p_ts); + + i_length = p_ts + TS_SIZE - p_payload; + + while (i_length) { + uint8_t *p_section = psi_assemble_payload(&p_pid->p_psi_buffer, + &p_pid->i_psi_buffer_used, + &p_payload, &i_length); + if (p_section != NULL) + handle_section(i_pid, p_section); + } +} + +/***************************************************************************** + * Main loop + *****************************************************************************/ +int main(int i_argc, char **ppsz_argv) +{ + int i; + + if (ppsz_argv[1] != NULL && + (!strcmp(ppsz_argv[1], "-h") || !strcmp(ppsz_argv[1], "--help"))) { + fprintf(stderr, "usage: %s < [> ]\n", + ppsz_argv[0]); + return 1; + } + + memset(p_pids, 0, sizeof(p_pids)); + + for (i = 0; i < 8192; i++) { + p_pids[i].i_last_cc = -1; + psi_assemble_init( &p_pids[i].p_psi_buffer, + &p_pids[i].i_psi_buffer_used ); + } + + p_pids[PAT_PID].i_psi_refcount++; + p_pids[NIT_PID].i_psi_refcount++; + p_pids[SDT_PID].i_psi_refcount++; + + while (!feof(stdin) && !ferror(stdin)) { + uint8_t p_ts[TS_SIZE]; + size_t i_ret = fread(p_ts, sizeof(p_ts), 1, stdin); + if (i_ret != 1) continue; + if (!ts_validate(p_ts)) + printf("invalid TS packet"); + else { + uint16_t i_pid = ts_get_pid(p_ts); + ts_pid_t *p_pid = &p_pids[i_pid]; + if (p_pid->i_psi_refcount) + handle_psi_packet(p_ts); + p_pid->i_last_cc = ts_get_cc(p_ts); + } + } + + return 0; +} diff --git a/mpeg/psi.h b/mpeg/psi.h index 6a4eb62..f4ad3ce 100644 --- a/mpeg/psi.h +++ b/mpeg/psi.h @@ -73,7 +73,7 @@ static inline void desc_print(const uint8_t *p_desc, f_print pf_print, static inline void desc05_init(uint8_t *p_desc) { desc_set_tag(p_desc, 0x05); - desc_set_length(p_desc, DESC05_HEADER_SIZE); + desc_set_length(p_desc, DESC05_HEADER_SIZE - DESC_HEADER_SIZE); } static inline void desc05_set_identifier(uint8_t *p_desc, uint8_t p_id[4]) @@ -114,7 +114,7 @@ static inline uint16_t desc09_get_pid(const uint8_t *p_desc) static inline void desc09_print(const uint8_t *p_desc, f_print pf_print, void *opaque) { - pf_print(opaque, " - desc 09 sysid=%hx pid=%hu", + pf_print(opaque, " - desc 09 sysid=0x%hx pid=%hu", desc09_get_sysid(p_desc), desc09_get_pid(p_desc)); } @@ -168,7 +168,7 @@ static inline void desc0a_print(uint8_t *p_desc, f_print pf_print, while ((p_desc_n = desc0a_get_language(p_desc, j)) != NULL) { j++; - pf_print(opaque, " - desc 0a language=%3.3s audiotype=%hhu", + pf_print(opaque, " - desc 0a language=%3.3s audiotype=0x%hhx", (const char *)desc0an_get_code(p_desc_n), desc0an_get_audiotype(p_desc_n)); } @@ -648,6 +648,8 @@ static inline void psi_table_copy(uint8_t **pp_dest, uint8_t **pp_src) psi_get_tableid(pp_sections[0]) #define psi_table_get_version(pp_sections) \ psi_get_version(pp_sections[0]) +#define psi_table_get_current(pp_sections) \ + psi_get_current(pp_sections[0]) #define psi_table_get_lastsection(pp_sections) \ psi_get_lastsection(pp_sections[0]) #define psi_table_get_tableidext(pp_sections) \ @@ -830,9 +832,10 @@ static inline void pat_table_print(uint8_t **pp_sections, f_print pf_print, uint8_t i_last_section = psi_table_get_lastsection(pp_sections); uint8_t i; - pf_print(opaque, "new PAT tsid=%hu version=%hhu", + pf_print(opaque, "new PAT tsid=%hu version=%hhu%s", psi_table_get_tableidext(pp_sections), - psi_table_get_version(pp_sections)); + psi_table_get_version(pp_sections), + !psi_table_get_current(pp_sections) ? " (next)" : ""); for (i = 0; i <= i_last_section; i++) { uint8_t *p_section = psi_table_get_section(pp_sections, i); @@ -840,10 +843,14 @@ static inline void pat_table_print(uint8_t **pp_sections, f_print pf_print, int j = 0; while ((p_program = pat_get_program(p_section, j)) != NULL) { + uint16_t i_program = patn_get_program(p_program); + uint16_t i_pid = patn_get_pid(p_program); j++; - pf_print(opaque, " * program number=%hu pid=%hu", - patn_get_program(p_program), - patn_get_pid(p_program)); + if (i_program == 0) + pf_print(opaque, " * NIT pid=%hu", i_pid); + else + pf_print(opaque, " * program number=%hu pid=%hu", + i_program, i_pid); } } diff --git a/mpeg/psi_print.h b/mpeg/psi_print.h index ca5e73a..b1ea5de 100644 --- a/mpeg/psi_print.h +++ b/mpeg/psi_print.h @@ -81,8 +81,9 @@ static inline void pmt_print(uint8_t *p_pmt, f_print pf_print, void *opaque) uint8_t *p_es; uint8_t j = 0; - pf_print(opaque, "new PMT program=%hu version=%hhu pcrpid=%hu", + pf_print(opaque, "new PMT program=%hu version=%hhu%s pcrpid=%hu", pmt_get_program(p_pmt), psi_get_version(p_pmt), + !psi_get_current(p_pmt) ? " (next)" : "", pmt_get_pcrpid(p_pmt)); descs_print(pmt_get_descs(p_pmt), pf_print, opaque); @@ -105,17 +106,18 @@ static inline void nit_table_print(uint8_t **pp_sections, f_print pf_print, uint8_t i_last_section = psi_table_get_lastsection(pp_sections); uint8_t i; - pf_print(opaque, "new NIT %s networkid=%hu version=%hhu", + pf_print(opaque, "new NIT %s networkid=%hu version=%hhu%s", psi_table_get_tableid(pp_sections) == NIT_TABLE_ID_ACTUAL ? "actual" : "other", psi_table_get_tableidext(pp_sections), - psi_table_get_version(pp_sections)); + psi_table_get_version(pp_sections), + !psi_table_get_current(pp_sections) ? " (next)" : ""); descs_print(nit_get_descs(psi_table_get_section(pp_sections, 0)), pf_print, opaque); for (i = 0; i <= i_last_section; i++) { uint8_t *p_section = psi_table_get_section(pp_sections, i); - const uint8_t *p_ts; + uint8_t *p_ts; int j = 0; while ((p_ts = nit_get_ts(p_section, j)) != NULL) { @@ -138,16 +140,17 @@ static inline void sdt_table_print(uint8_t **pp_sections, f_print pf_print, uint8_t i_last_section = psi_table_get_lastsection(pp_sections); uint8_t i; - pf_print(opaque, "new SDT %s tsid=%hu version=%hhu onid=%hu", + pf_print(opaque, "new SDT %s tsid=%hu version=%hhu%s onid=%hu", psi_table_get_tableid(pp_sections) == SDT_TABLE_ID_ACTUAL ? "actual" : "other", psi_table_get_tableidext(pp_sections), psi_table_get_version(pp_sections), + !psi_table_get_current(pp_sections) ? " (next)" : "", sdt_get_onid(psi_table_get_section(pp_sections, 0))); for (i = 0; i <= i_last_section; i++) { uint8_t *p_section = psi_table_get_section(pp_sections, i); - const uint8_t *p_service; + uint8_t *p_service; int j = 0; while ((p_service = sdt_get_service(p_section, j)) != NULL) {