diff --git a/README b/README index 517b04b..e05f076 100644 --- a/README +++ b/README @@ -57,6 +57,7 @@ Supported SI tables * Running Status Table (RST) * Stuffing Table (ST) * Discontinuity Information Table (DIT) + * Selection Information Table (SIT) Supported MPEG descriptors diff --git a/TODO b/TODO index de3f0b1..70feae4 100644 --- a/TODO +++ b/TODO @@ -9,8 +9,6 @@ so if you like something just do it and send a patch. - Descriptor 0x26 metadata_descriptor - Descriptor 0x29 IPMP_descriptor (defined in ISO/IEC 13818-11, MPEG-2 IPMP) -- Add support for Selection Information Table (SIT). - - Add generators and example usage for these DVB descriptors: - Descriptor 0x4a: Linkage descriptor - Descriptor 0x6a: AC-3 descriptor diff --git a/dvb/si.h b/dvb/si.h index a578ffd..558f3e8 100644 --- a/dvb/si.h +++ b/dvb/si.h @@ -49,5 +49,6 @@ #include #include #include +#include #endif diff --git a/dvb/si/sit.h b/dvb/si/sit.h new file mode 100644 index 0000000..bff785b --- /dev/null +++ b/dvb/si/sit.h @@ -0,0 +1,192 @@ +/***************************************************************************** + * sit.h: ISO/IEC 13818-1 Selection Information Table (SIT) + ***************************************************************************** + * Copyright (C) 2009-2010 VideoLAN + * + * Authors: Christophe Massiot + * Georgi Chorbadzhiyski + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +/* + * Normative references: + * - ISO/IEC 13818-1:2007(E) (MPEG-2 Systems) + */ + +#ifndef __BITSTREAM_MPEG_SIT_H__ +#define __BITSTREAM_MPEG_SIT_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************************************************************** + * Selection Information Table + *****************************************************************************/ +#define SIT_PID 0x1f +#define SIT_TABLE_ID 0x7f +#define SIT_HEADER_SIZE (PSI_HEADER_SIZE_SYNTAX1 + 2) +#define SIT_SERVICE_SIZE 4 + +static inline void sit_init(uint8_t *p_sit) +{ + psi_init(p_sit, true); + psi_set_tableid(p_sit, SIT_TABLE_ID); + p_sit[1] |= 0xe0; + psi_set_tableidext(p_sit, 0xffff); + psi_set_section(p_sit, 0); + psi_set_lastsection(p_sit, 0); +} + +static inline void sit_set_length(uint8_t *p_sit, uint16_t i_sit_length) +{ + psi_set_length(p_sit, SIT_HEADER_SIZE + PSI_CRC_SIZE - PSI_HEADER_SIZE + + i_sit_length); +} + +static inline uint16_t sit_get_desclength(const uint8_t *p_sit) +{ + return ((p_sit[8] & 0x0f) << 8) | p_sit[9]; +} + +static inline void sit_set_desclength(uint8_t *p_sit, uint16_t i_length) +{ + p_sit[8] = ((i_length >> 8) & 0xff) | 0xf0; + p_sit[9] = i_length & 0xff; +} + +static inline uint8_t *sit_get_descs(uint8_t *p_sit) +{ + return &p_sit[8]; +} + +static inline void sitn_init(uint8_t *p_sit_n) +{ + p_sit_n[2] = 0x80; +} + +static inline uint16_t sitn_get_sid(const uint8_t *p_sit_n) +{ + return (p_sit_n[0] << 8) | p_sit_n[1]; +} + +static inline void sitn_set_sid(uint8_t *p_sit_n, uint16_t i_sid) +{ + p_sit_n[0] = (i_sid >> 8) & 0xff; + p_sit_n[1] = i_sid & 0xff; +} + +static inline uint8_t sitn_get_running_status(const uint8_t *p_sit_n) +{ + return (p_sit_n[2] & 0x70) >> 4; +} + +static inline void sitn_set_running_status(uint8_t *p_sit_n, uint8_t i_running_status) +{ + p_sit_n[2] = (p_sit_n[2] &~ 0x70) | ((i_running_status & 0x07) << 4); +} + +static inline void sitn_set_desclength(uint8_t *p_sit_n, uint16_t i_length) +{ + p_sit_n[2] = ((i_length >> 8) & 0xff) | (p_sit_n[2] & 0xf0); + p_sit_n[3] = i_length & 0xff; +} + +static inline uint16_t sitn_get_desclength(const uint8_t *p_sit_n) +{ + return ((p_sit_n[2] & 0xf) << 8) | p_sit_n[3]; +} + +static inline uint8_t *sitn_get_descs(uint8_t *p_sit_n) +{ + return &p_sit_n[2]; +} + +static inline uint8_t *sit_get_service(uint8_t *p_sit, uint8_t n) +{ + uint16_t i_section_size = psi_get_length(p_sit) + PSI_HEADER_SIZE + - PSI_CRC_SIZE; + uint8_t *p_sit_n = p_sit + SIT_HEADER_SIZE + sit_get_desclength(p_sit); + if (p_sit_n - p_sit > i_section_size) return NULL; + + while (n) { + if (p_sit_n + SIT_SERVICE_SIZE - p_sit > i_section_size) return NULL; + p_sit_n += SIT_SERVICE_SIZE + sitn_get_desclength(p_sit_n); + n--; + } + if (p_sit_n - p_sit >= i_section_size) return NULL; + return p_sit_n; +} + +static inline bool sit_validate_service(const uint8_t *p_sit, const uint8_t *p_sit_n, + uint16_t i_desclength) +{ + uint16_t i_section_size = psi_get_length(p_sit) + PSI_HEADER_SIZE + - PSI_CRC_SIZE; + return (p_sit_n + SIT_SERVICE_SIZE + i_desclength + <= p_sit + i_section_size); +} + +static inline bool sit_validate(const uint8_t *p_sit) +{ + uint16_t i_section_size = psi_get_length(p_sit) + PSI_HEADER_SIZE + - PSI_CRC_SIZE; + const uint8_t *p_sit_n; + + if (!psi_get_syntax(p_sit) || psi_get_section(p_sit) + || psi_get_lastsection(p_sit) + || psi_get_tableid(p_sit) != SIT_TABLE_ID) + return false; + + if (!psi_check_crc(p_sit)) + return false; + + if (i_section_size < SIT_HEADER_SIZE + || i_section_size < SIT_HEADER_SIZE + sit_get_desclength(p_sit)) + return false; + + if (!descs_validate(sit_get_descs((uint8_t *)p_sit))) + return false; + + p_sit_n = p_sit + SIT_HEADER_SIZE + sit_get_desclength(p_sit); + + while (p_sit_n + SIT_SERVICE_SIZE - p_sit <= i_section_size + && p_sit_n + SIT_SERVICE_SIZE + sitn_get_desclength(p_sit_n) - p_sit + <= i_section_size) { + if (!descs_validate(sitn_get_descs((uint8_t *)p_sit_n))) + return false; + + p_sit_n += SIT_SERVICE_SIZE + sitn_get_desclength(p_sit_n); + } + + return (p_sit_n - p_sit == i_section_size); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dvb/si/sit_print.h b/dvb/si/sit_print.h new file mode 100644 index 0000000..53c9903 --- /dev/null +++ b/dvb/si/sit_print.h @@ -0,0 +1,106 @@ +/***************************************************************************** + * sit_print.h: ISO/IEC 13818-1 Selection Information Table (printing) + ***************************************************************************** + * Copyright (C) 2010 VideoLAN + * + * Authors: Christophe Massiot + * Georgi Chorbadzhiyski + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#ifndef __BITSTREAM_MPEG_SIT_PRINT_H__ +#define __BITSTREAM_MPEG_SIT_PRINT_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +static inline void sit_print(uint8_t *p_sit, + f_print pf_print, void *print_opaque, + f_iconv pf_iconv, void *iconv_opaque, + print_type_t i_print_type) +{ + uint8_t *p_service; + uint8_t j = 0; + + switch (i_print_type) { + case PRINT_XML: + pf_print(print_opaque, "", + psi_get_version(p_sit), + !psi_get_current(p_sit) ? 0 : 1); + break; + default: + pf_print(print_opaque, "new SIT version=%hhu%s", + psi_get_version(p_sit), + !psi_get_current(p_sit) ? " (next)" : ""); + } + + descs_print(sit_get_descs(p_sit), pf_print, print_opaque, + pf_iconv, iconv_opaque, i_print_type); + + while ((p_service = sit_get_service(p_sit, j)) != NULL) { + j++; + switch (i_print_type) { + case PRINT_XML: + pf_print(print_opaque, "", + sitn_get_sid(p_service), + sitn_get_running_status(p_service) + ); + break; + default: + pf_print(print_opaque, " * SERVICE sid=%hu running_status=%u", + sitn_get_sid(p_service), + sitn_get_running_status(p_service) + ); + } + + descs_print(sitn_get_descs(p_service), pf_print, print_opaque, + pf_iconv, iconv_opaque, i_print_type); + + switch (i_print_type) { + case PRINT_XML: + pf_print(print_opaque, ""); + break; + default: + break; + } + } + + switch (i_print_type) { + case PRINT_XML: + pf_print(print_opaque, ""); + break; + default: + pf_print(print_opaque, "end SIT"); + } +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dvb/si_print.h b/dvb/si_print.h index 499f314..9f1f4d2 100644 --- a/dvb/si_print.h +++ b/dvb/si_print.h @@ -37,5 +37,6 @@ #include #include #include +#include #endif diff --git a/examples/dvb_gen_si.c b/examples/dvb_gen_si.c index 2f79930..42cd2d1 100644 --- a/examples/dvb_gen_si.c +++ b/examples/dvb_gen_si.c @@ -1827,6 +1827,95 @@ static void generate_dit(void) { free(dit); } +/* Selection Information Table (SIT) */ +static void generate_sit(void) { + uint8_t *sit = psi_allocate(); + uint8_t *sit_n; + uint8_t sit_n_counter; + uint8_t *desc_loop, *desc; + uint8_t desc_counter; + + // Generate empty SIT + sit_init(sit); + psi_set_version(sit, 0); + psi_set_current(sit); + sit_set_length(sit, 0); + sit_set_desclength(sit, 0); + psi_set_crc(sit); + output_psi_section(sit, SIT_PID, &cc); + + // Add elementary streams + sit_init(sit); + psi_set_version(sit, 1); + psi_set_current(sit); + sit_set_length(sit, 0); + sit_set_desclength(sit, 0); + + { + // Add descriptors to program descriptors + sit_set_length(sit, PSI_MAX_SIZE); + sit_set_desclength(sit, DESCS_MAX_SIZE); + + desc_counter = 0; + desc_loop = sit_get_descs(sit); + + desc = descs_get_desc(desc_loop, desc_counter++); + build_desc05(desc); + + // Finish descriptor generation + desc = descs_get_desc(desc_loop, desc_counter); // Get next descriptor pos + sit_set_desclength(sit, desc - desc_loop - DESCS_HEADER_SIZE); + sit_set_length(sit, sit_get_desclength(sit)); + } + + { + sit_set_length(sit, PSI_MAX_SIZE); // This needed so sit_get_es works + + // Process elementary streams + sit_n_counter = 0; + + sit_n = sit_get_service(sit, sit_n_counter++); + sitn_init(sit_n); + sitn_set_sid(sit_n, sid); + sitn_set_running_status(sit_n, 1); + sitn_set_desclength(sit_n, 0); + { + // Add descriptors to transport_stream_n + desc_counter = 0; + desc_loop = sitn_get_descs(sit_n); + descs_set_length(desc_loop, DESCS_MAX_SIZE); // This is needed so descs_get_desc(x, n) works + + desc = descs_get_desc(desc_loop, desc_counter++); + build_desc05(desc); + + desc = descs_get_desc(desc_loop, desc_counter++); + build_desc05(desc); + + desc = descs_get_desc(desc_loop, desc_counter++); + build_desc05(desc); + + // Finish descriptor generation + desc = descs_get_desc(desc_loop, desc_counter); // Get next descriptor pos + descs_set_length(desc_loop, desc - desc_loop - DESCS_HEADER_SIZE); + } + + sit_n = sit_get_service(sit, sit_n_counter++); + sitn_init(sit_n); + sitn_set_sid(sit_n, sid + 1000); + sitn_set_running_status(sit_n, 3); + sitn_set_desclength(sit_n, 0); + + // Set transport_stream_loop length + sit_n = sit_get_service(sit, sit_n_counter); // Get last service + sit_set_length(sit, sit_n - sit_get_service(sit, 0) + sit_get_desclength(sit)); + } + + psi_set_crc(sit); + output_psi_section(sit, SIT_PID, &cc); + + free(sit); +} + int main(void) { generate_pat(); @@ -1841,6 +1930,7 @@ int main(void) generate_rst(); generate_pmt(); generate_dit(); + generate_sit(); return EXIT_SUCCESS; } diff --git a/examples/dvb_print_si.c b/examples/dvb_print_si.c index ac06a23..3c81460 100644 --- a/examples/dvb_print_si.c +++ b/examples/dvb_print_si.c @@ -758,6 +758,29 @@ static void handle_rst_section(uint16_t i_pid, uint8_t *p_rst) free(p_rst); } +/***************************************************************************** + * handle_sit + *****************************************************************************/ +static void handle_sit_section(uint16_t i_pid, uint8_t *p_sit) +{ + if (i_pid != SIT_PID || !sit_validate(p_sit)) { + switch (i_print_type) { + case PRINT_XML: + printf("\n", + i_pid); + break; + default: + printf("invalid SIT section received on PID %hu\n", i_pid); + } + free(p_sit); + return; + } + + sit_print(p_sit, print_wrapper, NULL, iconv_wrapper, NULL, i_print_type); + + free(p_sit); +} + /***************************************************************************** * handle_section *****************************************************************************/ @@ -822,6 +845,10 @@ static void handle_section(uint16_t i_pid, uint8_t *p_section) handle_dit_section(i_pid, p_section); break; + case SIT_TABLE_ID: + handle_sit_section(i_pid, p_section); + break; + default: if (i_table_id == EIT_TABLE_ID_PF_ACTUAL || (i_table_id >= EIT_TABLE_ID_SCHED_ACTUAL_FIRST && @@ -925,6 +952,7 @@ int main(int i_argc, char **ppsz_argv) p_pids[TDT_PID].i_psi_refcount++; p_pids[RST_PID].i_psi_refcount++; p_pids[DIT_PID].i_psi_refcount++; + p_pids[SIT_PID].i_psi_refcount++; switch (i_print_type) { case PRINT_XML: