From 8154e00f62e763aa03222a50f653d4cb2c95c96b Mon Sep 17 00:00:00 2001 From: Christophe Massiot Date: Fri, 13 Aug 2010 21:09:41 +0000 Subject: [PATCH] * ALL: New *_print() set of functions. * ALL (tables): Check CRC only when needed; also check tables coherence. * ALL (descriptors): Add a lot of missing _get_ functions. * ALL (descriptors): Fix off-by-one overflow of structure array. * mpeg/psi.h: Fix off-by-one overflow of PAT program array. --- common.h | 30 ++++++++ dvb/si.h | 165 +++++++++++++++++++++++++++++++++++++++++++- mpeg/psi.h | 176 ++++++++++++++++++++++++++++++++++++++++------- mpeg/psi_print.h | 172 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 516 insertions(+), 27 deletions(-) create mode 100644 common.h create mode 100644 mpeg/psi_print.h diff --git a/common.h b/common.h new file mode 100644 index 0000000..0628732 --- /dev/null +++ b/common.h @@ -0,0 +1,30 @@ +/***************************************************************************** + * common.h: (Rare) common declarations for all submodules + ***************************************************************************** + * Copyright (C) 2010 VideoLAN + * $Id: psi.h 1 2010-08-10 19:42:50Z massiot $ + * + * 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. + *****************************************************************************/ + +#ifndef __BITSTREAM_COMMON_H__ +#define __BITSTREAM_COMMON_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef void (*f_print)(void *, const char *, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dvb/si.h b/dvb/si.h index 73de2e9..c349ba3 100644 --- a/dvb/si.h +++ b/dvb/si.h @@ -22,6 +22,7 @@ #ifndef __BITSTREAM_DVB_SI_H__ #define __BITSTREAM_DVB_SI_H__ +#include #include #ifdef __cplusplus @@ -55,6 +56,14 @@ static inline void desc40_get_networkname(const uint8_t *p_desc, psz_network_name[i_length] = '\0'; } +static inline void desc40_print(const uint8_t *p_desc, f_print pf_print, + void *opaque) +{ + DESC_DECLARE_STRING1(psz_network_name); + desc40_get_networkname(p_desc, psz_network_name); + pf_print(opaque, " - desc 40 networkname=%s", psz_network_name); +} + /***************************************************************************** * Descriptor 0x48: Service descriptor *****************************************************************************/ @@ -81,6 +90,17 @@ static inline void desc48_get_service(const uint8_t *p_desc, psz_service[*p] = '\0'; } +static inline void desc48_print(const uint8_t *p_desc, f_print pf_print, + void *opaque) +{ + DESC_DECLARE_STRING1(psz_provider); + DESC_DECLARE_STRING1(psz_service); + desc48_get_provider(p_desc, psz_provider); + desc48_get_service(p_desc, psz_service); + pf_print(opaque, " - desc 48 provider=%s service=%s", psz_provider, + psz_service); +} + /***************************************************************************** * Descriptor 0x56: Teletext descriptor *****************************************************************************/ @@ -95,12 +115,14 @@ static inline void desc56_init(uint8_t *p_desc) static inline uint8_t *desc56_get_language(uint8_t *p_desc, uint8_t n) { uint8_t *p_desc_n = p_desc + DESC56_HEADER_SIZE + n * DESC56_LANGUAGE_SIZE; - if (p_desc_n - p_desc > desc_get_length(p_desc) + DESC56_HEADER_SIZE) + if (p_desc_n + DESC56_LANGUAGE_SIZE - p_desc + > desc_get_length(p_desc) + DESC56_HEADER_SIZE) return NULL; return p_desc_n; } #define desc56n_set_code desc0an_set_code +#define desc56n_get_code desc0an_get_code static inline void desc56n_set_teletexttype(uint8_t *p_desc_n, uint8_t i_type) { @@ -108,6 +130,11 @@ static inline void desc56n_set_teletexttype(uint8_t *p_desc_n, uint8_t i_type) p_desc_n[3] |= (i_type << 3) & 0xfc; } +static inline uint8_t desc56n_get_teletexttype(const uint8_t *p_desc_n) +{ + return p_desc_n[3] >> 3; +} + static inline void desc56n_set_teletextmagazine(uint8_t *p_desc_n, uint8_t i_magazine) { @@ -115,11 +142,38 @@ static inline void desc56n_set_teletextmagazine(uint8_t *p_desc_n, p_desc_n[3] |= (i_magazine & 0x3); } +static inline uint8_t desc56n_get_teletextmagazine(const uint8_t *p_desc_n) +{ + return p_desc_n[3] & 0x3; +} + static inline void desc56n_set_teletextpage(uint8_t *p_desc_n, uint8_t i_page) { p_desc_n[4] = i_page; } +static inline uint8_t desc56n_get_teletextpage(const uint8_t *p_desc_n) +{ + return p_desc_n[4]; +} + +static inline void desc56_print(uint8_t *p_desc, f_print pf_print, + void *opaque) +{ + uint8_t j = 0; + uint8_t *p_desc_n; + + 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", + (const char *)desc56n_get_code(p_desc_n), + desc56n_get_teletexttype(p_desc_n), + desc56n_get_teletextmagazine(p_desc_n), + desc56n_get_teletextpage(p_desc_n)); + } +} + /***************************************************************************** * Descriptor 0x59: Subtitling descriptor *****************************************************************************/ @@ -134,18 +188,25 @@ static inline void desc59_init(uint8_t *p_desc) static inline uint8_t *desc59_get_language(uint8_t *p_desc, uint8_t n) { uint8_t *p_desc_n = p_desc + DESC59_HEADER_SIZE + n * DESC59_LANGUAGE_SIZE; - if (p_desc_n - p_desc > desc_get_length(p_desc) + DESC59_HEADER_SIZE) + if (p_desc_n + DESC59_LANGUAGE_SIZE - p_desc + > desc_get_length(p_desc) + DESC59_HEADER_SIZE) return NULL; return p_desc_n; } #define desc59n_set_code desc0an_set_code +#define desc59n_get_code desc0an_get_code static inline void desc59n_set_subtitlingtype(uint8_t *p_desc_n, uint8_t i_type) { p_desc_n[3] = i_type; } +static inline uint8_t desc59n_get_subtitlingtype(const uint8_t *p_desc_n) +{ + return p_desc_n[3]; +} + static inline void desc59n_set_compositionpage(uint8_t *p_desc_n, uint16_t i_page) { @@ -153,12 +214,39 @@ static inline void desc59n_set_compositionpage(uint8_t *p_desc_n, p_desc_n[5] = i_page & 0xff; } +static inline uint16_t desc59n_get_compositionpage(const uint8_t *p_desc_n) +{ + return (p_desc_n[4] << 8) | p_desc_n[5]; +} + static inline void desc59n_set_ancillarypage(uint8_t *p_desc_n, uint16_t i_page) { p_desc_n[6] = i_page >> 8; p_desc_n[7] = i_page & 0xff; } +static inline uint16_t desc59n_get_ancillarypage(const uint8_t *p_desc_n) +{ + return (p_desc_n[6] << 8) | p_desc_n[7]; +} + +static inline void desc59_print(uint8_t *p_desc, f_print pf_print, + void *opaque) +{ + uint8_t j = 0; + uint8_t *p_desc_n; + + 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", + (const char *)desc59n_get_code(p_desc_n), + desc59n_get_subtitlingtype(p_desc_n), + desc59n_get_compositionpage(p_desc_n), + desc59n_get_ancillarypage(p_desc_n)); + } +} + /***************************************************************************** * Descriptor 0x6a: AC-3 descriptor *****************************************************************************/ @@ -174,6 +262,12 @@ static inline void desc6a_clear_flags(uint8_t *p_desc) p_desc[2] = 0; } +static inline void desc6a_print(const uint8_t *p_desc, f_print pf_print, + void *opaque) +{ + pf_print(opaque, " - desc 6a ac3"); +} + /***************************************************************************** * Network Information Table *****************************************************************************/ @@ -337,6 +431,53 @@ static inline bool nit_validate(const uint8_t *p_nit) return (p_nit_n - p_nit == i_section_size); } + +static inline uint8_t *nit_table_find_ts(uint8_t **pp_sections, + uint16_t i_tsid, uint16_t i_onid) +{ + uint8_t i_last_section = psi_table_get_lastsection(pp_sections); + uint8_t i; + + for (i = 0; i <= i_last_section; i++) { + uint8_t *p_section = psi_table_get_section(pp_sections, i); + uint8_t *p_ts; + int j = 0; + + while ((p_ts = nit_get_ts(p_section, j)) != NULL) { + j++; + if (nitn_get_tsid(p_ts) == i_tsid && nitn_get_onid(p_ts) == i_onid) + return p_ts; + } + } + + return NULL; +} + +static inline bool nit_table_validate(uint8_t **pp_sections) +{ + uint8_t i_last_section = psi_table_get_lastsection(pp_sections); + uint8_t i; + + for (i = 0; i <= i_last_section; i++) { + uint8_t *p_section = psi_table_get_section(pp_sections, i); + uint8_t *p_ts; + int j = 0; + + if (!psi_check_crc(p_section)) + return false; + + while ((p_ts = nit_get_ts(p_section, j)) != NULL) { + j++; + /* check that the program number if not already in the table */ + if (nit_table_find_ts(pp_sections, nitn_get_tsid(p_ts), + nitn_get_onid(p_ts)) != p_ts) + return false; + } + } + + return true; +} + /***************************************************************************** * Service Description Table *****************************************************************************/ @@ -508,6 +649,23 @@ static inline uint8_t *sdt_table_find_service(uint8_t **pp_sections, return NULL; } +static inline bool sdt_table_validate(uint8_t **pp_sections) +{ + uint8_t i_last_section = psi_table_get_lastsection(pp_sections); + uint8_t i; + + for (i = 0; i <= i_last_section; i++) { + uint8_t *p_section = psi_table_get_section(pp_sections, i); + uint8_t *p_ts; + int j = 0; + + if (!psi_check_crc(p_section)) + return false; + } + + return true; +} + /***************************************************************************** * Event Information Table *****************************************************************************/ @@ -569,6 +727,9 @@ static inline bool eit_validate(const uint8_t *p_eit) && i_tid <= EIT_TABLE_ID_SCHED_OTHER_LAST))) return false; + if (!psi_check_crc(p_eit)) + return false; + p_eit_n = p_eit + EIT_HEADER_SIZE; while (p_eit_n + EIT_EVENT_SIZE - p_eit <= i_section_size diff --git a/mpeg/psi.h b/mpeg/psi.h index 807ded7..817f2ec 100644 --- a/mpeg/psi.h +++ b/mpeg/psi.h @@ -21,6 +21,7 @@ #ifndef __BITSTREAM_MPEG_PSI_H__ #define __BITSTREAM_MPEG_PSI_H__ +#include #include #ifdef __cplusplus @@ -58,10 +59,16 @@ static inline uint8_t desc_get_length(const uint8_t *p_desc) return p_desc[1]; } +static inline void desc_print(const uint8_t *p_desc, f_print pf_print, + void *opaque) +{ + pf_print(opaque, " - desc %2.2hhx unknown", desc_get_tag(p_desc)); +} + /***************************************************************************** * Descriptor 0x05: Registration descriptor *****************************************************************************/ -#define DESC05_HEADER_SIZE 6 +#define DESC05_HEADER_SIZE (DESC_HEADER_SIZE + 4) static inline void desc05_init(uint8_t *p_desc) { @@ -77,10 +84,22 @@ static inline void desc05_set_identifier(uint8_t *p_desc, uint8_t p_id[4]) p_desc[5] = p_id[3]; } +static inline const uint8_t *desc05_get_identifier(const uint8_t *p_desc) +{ + return p_desc + 2; +} + +static inline void desc05_print(const uint8_t *p_desc, f_print pf_print, + void *opaque) +{ + pf_print(opaque, " - desc 05 identifier=%4.4s", + desc05_get_identifier(p_desc)); +} + /***************************************************************************** * Descriptor 0x09: Conditional access descriptor *****************************************************************************/ -#define DESC09_HEADER_SIZE 6 +#define DESC09_HEADER_SIZE (DESC_HEADER_SIZE + 4) static inline uint16_t desc09_get_sysid(const uint8_t *p_desc) { @@ -92,6 +111,13 @@ static inline uint16_t desc09_get_pid(const uint8_t *p_desc) return ((p_desc[4] & 0x1f) << 8) | p_desc[5]; } +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", + desc09_get_sysid(p_desc), desc09_get_pid(p_desc)); +} + /***************************************************************************** * Descriptor 0x0a: ISO-639 language descriptor *****************************************************************************/ @@ -106,7 +132,8 @@ static inline void desc0a_init(uint8_t *p_desc) static inline uint8_t *desc0a_get_language(uint8_t *p_desc, uint8_t n) { uint8_t *p_desc_n = p_desc + DESC0A_HEADER_SIZE + n * DESC0A_LANGUAGE_SIZE; - if (p_desc_n - p_desc > desc_get_length(p_desc) + DESC0A_HEADER_SIZE) + if (p_desc_n + DESC0A_LANGUAGE_SIZE - p_desc + > desc_get_length(p_desc) + DESC0A_HEADER_SIZE) return NULL; return p_desc_n; } @@ -118,11 +145,35 @@ static inline void desc0an_set_code(uint8_t *p_desc_n, const uint8_t p_code[3]) p_desc_n[2] = p_code[2]; } +static inline const uint8_t *desc0an_get_code(const uint8_t *p_desc_n) +{ + return p_desc_n; +} + static inline void desc0an_set_audiotype(uint8_t *p_desc_n, uint8_t i_type) { p_desc_n[3] = i_type; } +static inline uint8_t desc0an_get_audiotype(const uint8_t *p_desc_n) +{ + return p_desc_n[3]; +} + +static inline void desc0a_print(uint8_t *p_desc, f_print pf_print, + void *opaque) +{ + uint8_t j = 0; + uint8_t *p_desc_n; + + while ((p_desc_n = desc0a_get_language(p_desc, j)) != NULL) { + j++; + pf_print(opaque, " - desc 0a language=%3.3s audiotype=%hhu", + (const char *)desc0an_get_code(p_desc_n), + desc0an_get_audiotype(p_desc_n)); + } +} + /***************************************************************************** * Descriptors list *****************************************************************************/ @@ -344,7 +395,7 @@ static inline void psi_set_version(uint8_t *p_section, uint8_t i_version) p_section[5] = i_version << 1; } -static inline uint8_t psi_get_version(uint8_t *p_section) +static inline uint8_t psi_get_version(const uint8_t *p_section) { return (p_section[5] & 0x1e) >> 1; } @@ -424,12 +475,20 @@ static inline bool psi_validate(const uint8_t *p_section) - PSI_HEADER_SIZE + PSI_CRC_SIZE)) return false; - if (psi_get_syntax(p_section) && !psi_check_crc(p_section)) - return false; + /* only do the CRC check when it is strictly necessary */ return true; } +static inline bool psi_compare(const uint8_t *p_section1, + const uint8_t *p_section2) +{ + return psi_get_version(p_section1) == psi_get_version(p_section2) + && psi_get_length(p_section1) == psi_get_length(p_section2) + && !memcmp(p_section1, p_section2, + psi_get_length(p_section1) + PSI_HEADER_SIZE); +} + /***************************************************************************** * PSI section gathering *****************************************************************************/ @@ -569,7 +628,7 @@ static inline void psi_table_free(uint8_t **pp_sections) free(pp_sections[i]); } -static inline bool psi_table_validate(uint8_t **pp_sections) +static inline bool psi_table_validate(uint8_t * const *pp_sections) { return pp_sections[0] != NULL; } @@ -579,20 +638,14 @@ static inline void psi_table_copy(uint8_t **pp_dest, uint8_t **pp_src) memcpy(pp_dest, pp_src, PSI_TABLE_MAX_SECTIONS * sizeof(uint8_t *)); } -static inline uint16_t psi_table_get_tableidext(uint8_t **pp_sections) -{ - return psi_get_tableidext(pp_sections[0]); -} - -static inline uint8_t psi_table_get_version(uint8_t **pp_sections) -{ - return psi_get_version(pp_sections[0]); -} - -static inline uint8_t psi_table_get_lastsection(uint8_t **pp_sections) -{ - return psi_get_lastsection(pp_sections[0]); -} +#define psi_table_get_tableid(pp_sections) \ + psi_get_tableid(pp_sections[0]) +#define psi_table_get_version(pp_sections) \ + psi_get_version(pp_sections[0]) +#define psi_table_get_lastsection(pp_sections) \ + psi_get_lastsection(pp_sections[0]) +#define psi_table_get_tableidext(pp_sections) \ + psi_get_tableidext(pp_sections[0]) static inline bool psi_table_section(uint8_t **pp_sections, uint8_t *p_section) { @@ -628,6 +681,25 @@ static inline uint8_t *psi_table_get_section(uint8_t **pp_sections, uint8_t n) return pp_sections[n]; } +static inline bool psi_table_compare(uint8_t **pp_sections1, + uint8_t **pp_sections2) +{ + uint8_t i_last_section = psi_table_get_lastsection(pp_sections1); + uint8_t i; + + if (i_last_section != psi_table_get_lastsection(pp_sections2)) + return false; + + for (i = 0; i < i_last_section; i++) { + const uint8_t *p_section1 = psi_table_get_section(pp_sections1, i); + const uint8_t *p_section2 = psi_table_get_section(pp_sections2, i); + if (!psi_compare(p_section1, p_section2)) + return false; + } + + return true; +} + /***************************************************************************** * Program Association Table *****************************************************************************/ @@ -683,7 +755,8 @@ static inline uint16_t patn_get_pid(const uint8_t *p_pat_n) static inline uint8_t *pat_get_program(uint8_t *p_pat, uint8_t n) { uint8_t *p_pat_n = p_pat + PAT_HEADER_SIZE + n * PAT_PROGRAM_SIZE; - if (p_pat_n - p_pat > psi_get_length(p_pat) + PSI_HEADER_SIZE - PSI_CRC_SIZE) + if (p_pat_n + PAT_PROGRAM_SIZE - p_pat + > psi_get_length(p_pat) + PSI_HEADER_SIZE - PSI_CRC_SIZE) return NULL; return p_pat_n; } @@ -710,17 +783,67 @@ static inline uint8_t *pat_table_find_program(uint8_t **pp_sections, uint8_t *p_program; int j = 0; - while ((p_program = pat_get_program(p_section, j)) != NULL) - { + while ((p_program = pat_get_program(p_section, j)) != NULL) { + j++; if (patn_get_program(p_program) == i_program) return p_program; - j++; } } return NULL; } +static inline bool pat_table_validate(uint8_t **pp_sections) +{ + uint8_t i_last_section = psi_table_get_lastsection(pp_sections); + uint8_t i; + + for (i = 0; i <= i_last_section; i++) { + uint8_t *p_section = psi_table_get_section(pp_sections, i); + uint8_t *p_program; + int j = 0; + + if (!psi_check_crc(p_section)) + return false; + + while ((p_program = pat_get_program(p_section, j)) != NULL) { + j++; + /* check that the program number if not already in the table */ + if (pat_table_find_program(pp_sections, + patn_get_program(p_program)) != p_program) + return false; + } + } + + return true; +} + +static inline void pat_table_print(uint8_t **pp_sections, f_print pf_print, + void *opaque) +{ + uint8_t i_last_section = psi_table_get_lastsection(pp_sections); + uint8_t i; + + pf_print(opaque, "new PAT tsid=%hu version=%hhu", + psi_table_get_tableidext(pp_sections), + psi_table_get_version(pp_sections)); + + for (i = 0; i <= i_last_section; i++) { + uint8_t *p_section = psi_table_get_section(pp_sections, i); + const uint8_t *p_program; + int j = 0; + + while ((p_program = pat_get_program(p_section, j)) != NULL) { + j++; + pf_print(opaque, " * program number=%hu pid=%hu", + patn_get_program(p_program), + patn_get_pid(p_program)); + } + } + + pf_print(opaque, "end PAT"); +} + /***************************************************************************** * Program Map Table *****************************************************************************/ @@ -849,6 +972,9 @@ static inline bool pmt_validate(const uint8_t *p_pmt) || psi_get_tableid(p_pmt) != PMT_TABLE_ID) return false; + if (!psi_check_crc(p_pmt)) + return false; + if (i_section_size < PMT_HEADER_SIZE || i_section_size < PMT_HEADER_SIZE + pmt_get_desclength(p_pmt)) return false; diff --git a/mpeg/psi_print.h b/mpeg/psi_print.h new file mode 100644 index 0000000..ca5e73a --- /dev/null +++ b/mpeg/psi_print.h @@ -0,0 +1,172 @@ +/***************************************************************************** + * psi_print.h: ISO/IEC 13818-1 Program Stream Information (printing) + ***************************************************************************** + * Copyright (C) 2010 VideoLAN + * $Id: psi_print.h -1 $ + * + * 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. + *****************************************************************************/ + +/* + * Placed here for dependancy reasons + */ + +#ifndef __BITSTREAM_MPEG_PSI_PRINT_H__ +#define __BITSTREAM_MPEG_PSI_PRINT_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************************************************************** + * Descriptors list + *****************************************************************************/ +static inline void descs_print(uint8_t *p_descs, f_print pf_print, void *opaque) +{ + uint16_t j = 0; + uint8_t *p_desc; + + while ((p_desc = descs_get_desc(p_descs, j)) != NULL) { + j++; + + /* I am not proud of this */ + switch (desc_get_tag(p_desc)) { + case 0x05: + desc05_print(p_desc, pf_print, opaque); + break; + case 0x09: + desc09_print(p_desc, pf_print, opaque); + break; + case 0x0a: + desc0a_print(p_desc, pf_print, opaque); + break; + case 0x40: + desc40_print(p_desc, pf_print, opaque); + break; + case 0x48: + desc48_print(p_desc, pf_print, opaque); + break; + case 0x56: + desc56_print(p_desc, pf_print, opaque); + break; + case 0x59: + desc59_print(p_desc, pf_print, opaque); + break; + case 0x6a: + desc6a_print(p_desc, pf_print, opaque); + break; + default: + desc_print(p_desc, pf_print, opaque); + break; + } + } +} + +/***************************************************************************** + * Program Map Table + *****************************************************************************/ +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", + pmt_get_program(p_pmt), psi_get_version(p_pmt), + pmt_get_pcrpid(p_pmt)); + descs_print(pmt_get_descs(p_pmt), pf_print, opaque); + + while ((p_es = pmt_get_es(p_pmt, j)) != NULL) { + j++; + pf_print(opaque, " * ES pid=%hu streamtype=0x%hx", pmtn_get_pid(p_es), + pmtn_get_streamtype(p_es)); + descs_print(pmtn_get_descs(p_es), pf_print, opaque); + } + + pf_print(opaque, "end PMT"); +} + +/***************************************************************************** + * Network Information Table + *****************************************************************************/ +static inline void nit_table_print(uint8_t **pp_sections, f_print pf_print, + void *opaque) +{ + uint8_t i_last_section = psi_table_get_lastsection(pp_sections); + uint8_t i; + + pf_print(opaque, "new NIT %s networkid=%hu version=%hhu", + psi_table_get_tableid(pp_sections) == NIT_TABLE_ID_ACTUAL ? + "actual" : "other", + psi_table_get_tableidext(pp_sections), + psi_table_get_version(pp_sections)); + 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; + int j = 0; + + while ((p_ts = nit_get_ts(p_section, j)) != NULL) { + j++; + pf_print(opaque, " * ts tsid=%hu onid=%hu", + nitn_get_tsid(p_ts), nitn_get_onid(p_ts)); + descs_print(nitn_get_descs(p_ts), pf_print, opaque); + } + } + + pf_print(opaque, "end NIT"); +} + +/***************************************************************************** + * Service Description Table + *****************************************************************************/ +static inline void sdt_table_print(uint8_t **pp_sections, f_print pf_print, + void *opaque) +{ + 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", + psi_table_get_tableid(pp_sections) == SDT_TABLE_ID_ACTUAL ? + "actual" : "other", + psi_table_get_tableidext(pp_sections), + psi_table_get_version(pp_sections), + 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; + int j = 0; + + while ((p_service = sdt_get_service(p_section, j)) != NULL) { + j++; + pf_print(opaque, " * service sid=%hu eit%s%s running=%hhu%s", + sdtn_get_sid(p_service), + sdtn_get_eitschedule(p_service) ? " schedule" : "", + sdtn_get_eitpresent(p_service) ? " present" : "", + sdtn_get_running(p_service), + sdtn_get_ca(p_service) ? " scrambled" : ""); + descs_print(sdtn_get_descs(p_service), pf_print, opaque); + } + } + + pf_print(opaque, "end SDT"); +} + +#ifdef __cplusplus +} +#endif + +#endif