From 57197d858cef5fb544154ff88b9c29360e183da1 Mon Sep 17 00:00:00 2001 From: Christophe Massiot Date: Sat, 5 Sep 2015 17:28:21 +0200 Subject: [PATCH] add SCT-35 support --- examples/dvb_gen_si.c | 64 +++ examples/dvb_print_si.c | 65 ++- examples/dvb_print_si.output.txt | 29 +- examples/dvb_print_si.output.xml | 32 +- scte/35.h | 732 +++++++++++++++++++++++++++++++ scte/35_print.h | 296 +++++++++++++ 6 files changed, 1189 insertions(+), 29 deletions(-) create mode 100644 scte/35.h create mode 100644 scte/35_print.h diff --git a/examples/dvb_gen_si.c b/examples/dvb_gen_si.c index a2e9308..b5e696c 100644 --- a/examples/dvb_gen_si.c +++ b/examples/dvb_gen_si.c @@ -2,9 +2,11 @@ * dvb_gen_si.c: Generate SI tables and descriptors ***************************************************************************** * Copyright (c) 2011 Unix Solutions Ltd. + * Copyright (c) 2015 VideoLAN * * Authors: * Georgi Chorbadzhiyski + * Christophe Massiot * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -38,6 +40,8 @@ #include #include #include +#include +#include uint16_t tsid = 10000; uint16_t sid = 20000; @@ -45,6 +49,7 @@ uint16_t event_id = 30000; uint16_t onid = 40000; uint16_t pmt_pid = 100; +uint16_t scte35_pid = 200; time_t ts_0 = 1234567890; time_t ts_1 = 1; @@ -2959,6 +2964,12 @@ static void generate_pmt(void) { descs_set_length(desc_loop, desc - desc_loop - DESCS_HEADER_SIZE); } + pmt_n = pmt_get_es(pmt, pmt_n_counter++); + pmtn_init(pmt_n); + pmtn_set_streamtype(pmt_n, PMT_STREAMTYPE_SCTE_35); + pmtn_set_pid(pmt_n, scte35_pid); + pmtn_set_desclength(pmt_n, 0); + // Set transport_stream_loop length pmt_n = pmt_get_es(pmt, pmt_n_counter); // Get last service pmt_set_length(pmt, pmt_n - pmt_get_es(pmt, 0) + pmt_get_desclength(pmt)); @@ -3073,6 +3084,58 @@ static void generate_sit(void) { free(sit); } +/* SCTE 35 Splice Information Table */ +static void generate_scte35(void) { + uint8_t *scte35 = psi_allocate(); + + // Generate empty section + scte35_init(scte35); + psi_set_length(scte35, PSI_MAX_SIZE); + scte35_set_pts_adjustment(scte35, 0); + scte35_null_init(scte35); + scte35_set_desclength(scte35, 0); + psi_set_length(scte35, + scte35_get_descl(scte35) + PSI_CRC_SIZE - scte35 - PSI_HEADER_SIZE); + psi_set_crc(scte35); + output_psi_section(scte35, scte35_pid, &cc); + + // Generate insert section + scte35_init(scte35); + psi_set_length(scte35, PSI_MAX_SIZE); + scte35_set_pts_adjustment(scte35, 0); + scte35_insert_init(scte35, + SCTE35_INSERT_HEADER2_SIZE + + SCTE35_SPLICE_TIME_HEADER_SIZE + SCTE35_SPLICE_TIME_TIME_SIZE + + SCTE35_BREAK_DURATION_HEADER_SIZE + SCTE35_INSERT_FOOTER_SIZE); + scte35_insert_set_cancel(scte35, false); + scte35_insert_set_event_id(scte35, 4242); + scte35_insert_set_out_of_network(scte35, true); + scte35_insert_set_program_splice(scte35, true); + scte35_insert_set_duration(scte35, true); + scte35_insert_set_splice_immediate(scte35, false); + + uint8_t *splice_time = scte35_insert_get_splice_time(scte35); + scte35_splice_time_init(splice_time); + scte35_splice_time_set_time_specified(splice_time, true); + scte35_splice_time_set_pts_time(splice_time, 270000000); + + uint8_t *duration = scte35_insert_get_break_duration(scte35); + scte35_break_duration_init(duration); + scte35_break_duration_set_auto_return(duration, true); + scte35_break_duration_set_duration(duration, 27000000); + + scte35_insert_set_unique_program_id(scte35, 2424); + scte35_insert_set_avail_num(scte35, 0); + scte35_insert_set_avails_expected(scte35, 0); + scte35_set_desclength(scte35, 0); + psi_set_length(scte35, + scte35_get_descl(scte35) + PSI_CRC_SIZE - scte35 - PSI_HEADER_SIZE); + psi_set_crc(scte35); + output_psi_section(scte35, scte35_pid, &cc); + + free(scte35); +} + int main(void) { generate_pat(); @@ -3088,6 +3151,7 @@ int main(void) generate_pmt(); generate_dit(); generate_sit(); + generate_scte35(); return EXIT_SUCCESS; } diff --git a/examples/dvb_print_si.c b/examples/dvb_print_si.c index a2583be..e7d8e7e 100644 --- a/examples/dvb_print_si.c +++ b/examples/dvb_print_si.c @@ -1,7 +1,7 @@ /***************************************************************************** * dvb_print_si.c: Prints tables from a TS file ***************************************************************************** - * Copyright (C) 2010-2011 VideoLAN + * Copyright (C) 2010-2015 VideoLAN * * Authors: Christophe Massiot * @@ -41,6 +41,8 @@ #include #include #include +#include +#include /***************************************************************************** * Local declarations @@ -108,14 +110,17 @@ enum tables_t { TABLE_DIT, TABLE_SIT, TABLE_PMT, + TABLE_SCTE35, TABLE_END }; static const char * const ppsz_all_tables[TABLE_END] = { "pat", "cat", "tsdt", "nit", "bat", "sdt", "eit", "tot", "tdt", "rst", - "dit", "sit", "pmt" + "dit", "sit", "pmt", "scte35" }; static bool pb_print_table[TABLE_END]; +static void handle_pmt_es(uint8_t *p_pmt, bool b_select); + /***************************************************************************** * print_wrapper *****************************************************************************/ @@ -288,6 +293,7 @@ static void handle_pat(void) 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; + handle_pmt_es(pp_sids[i_pmt]->p_current_pmt, false); free(pp_sids[i_pmt]->p_current_pmt); pp_sids[i_pmt]->p_current_pmt = NULL; psi_table_free(pp_sids[i]->pp_eit_sections); @@ -451,6 +457,28 @@ static void handle_tsdt_section(uint16_t i_pid, uint8_t *p_section) /***************************************************************************** * handle_pmt *****************************************************************************/ +static void handle_pmt_es(uint8_t *p_pmt, bool b_select) +{ + int j = 0; + uint8_t *p_es; + while ((p_es = pmt_get_es(p_pmt, j)) != NULL) { + uint16_t i_pid = pmtn_get_pid(p_es); + uint8_t i_type = pmtn_get_streamtype(p_es); + j++; + + switch (i_type) { + case PMT_STREAMTYPE_SCTE_35: + if (b_select) + p_pids[i_pid].i_psi_refcount++; + else + p_pids[i_pid].i_psi_refcount--; + break; + default: + break; + } + } +} + static void handle_pmt(uint16_t i_pid, uint8_t *p_pmt) { uint16_t i_sid = pmt_get_program(p_pmt); @@ -514,8 +542,12 @@ static void handle_pmt(uint16_t i_pid, uint8_t *p_pmt) return; } + if (p_sid->p_current_pmt) + handle_pmt_es(p_sid->p_current_pmt, false); + free(p_sid->p_current_pmt); p_sid->p_current_pmt = p_pmt; + handle_pmt_es(p_pmt, true); if (pb_print_table[TABLE_PMT]) pmt_print(p_pmt, print_wrapper, NULL, iconv_wrapper, NULL, @@ -902,6 +934,31 @@ static void handle_sit_section(uint16_t i_pid, uint8_t *p_sit) free(p_sit); } +/***************************************************************************** + * handle_scte35 + *****************************************************************************/ +static void handle_scte35_section(uint16_t i_pid, uint8_t *p_scte35) +{ + if (!scte35_validate(p_scte35)) { + switch (i_print_type) { + case PRINT_XML: + printf("\n", + i_pid); + break; + default: + printf("invalid SCTE35 section received on PID %hu\n", i_pid); + } + free(p_scte35); + return; + } + + if (pb_print_table[TABLE_SCTE35] && + scte35_get_command_type(p_scte35) != SCTE35_NULL_COMMAND) + scte35_print(p_scte35, print_wrapper, NULL, i_print_type); + + free(p_scte35); +} + /***************************************************************************** * handle_section *****************************************************************************/ @@ -974,6 +1031,10 @@ static void handle_section(uint16_t i_pid, uint8_t *p_section) handle_eit_section(i_pid, p_section); break; + case SCTE35_TABLE_ID: + handle_scte35_section(i_pid, p_section); + break; + default: free( p_section ); break; diff --git a/examples/dvb_print_si.output.txt b/examples/dvb_print_si.output.txt index 2772124..7b2cd5a 100644 --- a/examples/dvb_print_si.output.txt +++ b/examples/dvb_print_si.output.txt @@ -70,9 +70,9 @@ new NIT actual networkid=40000 version=1 - mobile_handover handover_type=1 origin_type=0 nid=41000 initial_sid=21000 * ts tsid=10300 onid=40300 end NIT -new BAT networkid=40000 version=0 +new BAT bouquetid=40000 version=0 end BAT -new BAT networkid=40000 version=1 +new BAT bouquetid=40000 version=1 - desc 47 bouquetname="Test Bouquet Name" - desc 5c multilingual_bouquet_name code="eng" bouquetname="M Bouquet" - desc 5c multilingual_bouquet_name code="fre" bouquetname="M Bouquet" @@ -141,12 +141,12 @@ new SDT actual tsid=10000 version=1 onid=40000 - desc 4b nvod_reference tsid=10200 onid=40200 sid=20200 - desc 5f private_data specifier=0xaabbccdd end SDT -new EIT tableid=0x4e type=actual_pf service_id=20000 version=0 tsid=10000 onid=40000 seg_last_sec_number=0 last_table_id=0x00 +new EIT tableid=0x4e type=actual_pf service_id=20000 version=0 section=0/0 tsid=10000 onid=40000 end EIT -new EIT tableid=0x4e type=actual_pf service_id=20000 version=1 tsid=10000 onid=40000 seg_last_sec_number=0 last_table_id=0x00 - * EVENT id=30000 start_time=1234567890 start_time_dec="2009-02-13 23:31:30 UTC" duration=86399 duration_dec=23:59:59 running_status=2 free_CA_mode=0 +new EIT tableid=0x4e type=actual_pf service_id=20000 version=1 section=0/0 tsid=10000 onid=40000 + * EVENT id=30000 start_time=1234567890 start_time_dec="2009-02-13 23:31:30 UTC" duration=86399 duration_dec=23:59:59 running=2 free_CA=0 - desc 4d short_event lang=eng event_name="Major TV event" text="The event of the century!" - * EVENT id=30100 start_time=1 start_time_dec="1970-01-01 00:00:01 UTC" duration=3600 duration_dec=01:00:00 running_status=1 free_CA_mode=0 + * EVENT id=30100 start_time=1 start_time_dec="1970-01-01 00:00:01 UTC" duration=3600 duration_dec=01:00:00 running=1 free_CA=0 - desc 4a linkage tsid=10700 onid=40700 sid=20700 linkage=0x0d linkage_txt="event linkage" - event_linkage target_event_id=31000 target_listed=1 event_simulcast=1 - desc 4e extended_event desc_number=0 last_desc_number=0 lang=eng text="Wow, what an event!" @@ -155,7 +155,7 @@ new EIT tableid=0x4e type=actual_pf service_id=20000 version=1 tsid=10000 onid=4 - extended_event_item description="Rating" text="***++" - desc 4d short_event lang=eng event_name="Major TV event" text="The event of the century!" - desc 61 short_smoothing_buffer sb_size=1 sb_leak_rate=10 - * EVENT id=30200 start_time=999999999 start_time_dec="2001-09-09 01:46:39 UTC" duration=7200 duration_dec=02:00:00 running_status=0 free_CA_mode=0 + * EVENT id=30200 start_time=999999999 start_time_dec="2001-09-09 01:46:39 UTC" duration=7200 duration_dec=02:00:00 running=0 free_CA=0 - desc 4a linkage tsid=10800 onid=40800 sid=20800 linkage=0x0e linkage_txt="extended event linkage" - extended_event_linkage target_event_id=31000 target_listed=0 event_simulcast=1 link_type=1 link_type_txt="HD" target_id_type=3 target_id_type_txt="use user_defined_id" onid_id_flag=1 service_id_flag=1 user_defined_id=7878 target_tsid=0 target_onid=0 target_service_id=0 - extended_event_linkage target_event_id=32000 target_listed=1 event_simulcast=1 link_type=0 link_type_txt="SD" target_id_type=0 target_id_type_txt="use tsid" onid_id_flag=1 service_id_flag=1 user_defined_id=0 target_tsid=0 target_onid=42000 target_service_id=22000 @@ -172,7 +172,7 @@ new EIT tableid=0x4e type=actual_pf service_id=20000 version=1 tsid=10000 onid=4 - desc 55 parental_rating country_code=BUL rating=24 rating_txt="unknown" - desc 4d short_event lang=eng event_name="Major TV event" text="The event of the century!" - desc 4f time_shifted_service reference_sid=22000 reference_event_id=32000 - * EVENT id=30300 start_time=999999999 start_time_dec="2001-09-09 01:46:39 UTC" duration=7200 duration_dec=02:00:00 running_status=4 free_CA_mode=1 + * EVENT id=30300 start_time=999999999 start_time_dec="2001-09-09 01:46:39 UTC" duration=7200 duration_dec=02:00:00 running=4 free_CA=1 - desc 54 content content_l1=2 content_l2=4 user=78 - desc 54 content content_l1=6 content_l2=8 user=177 - desc 4d short_event lang=eng event_name="Major TV event" text="The event of the century!" @@ -205,9 +205,9 @@ end TOT new RST end RST new RST - * status tsid="10000" onid="40000" service_id="20000" event_id="30000" running_status="1" - * status tsid="10100" onid="40100" service_id="20100" event_id="30100" running_status="2" - * status tsid="10200" onid="40200" service_id="20200" event_id="30200" running_status="3" + * status tsid="10000" onid="40000" service_id="20000" event_id="30000" running="1" + * status tsid="10100" onid="40100" service_id="20100" event_id="30100" running="2" + * status tsid="10200" onid="40200" service_id="20200" event_id="30200" running="3" end RST new PMT program=20000 version=0 pcrpid=110 end PMT @@ -332,6 +332,7 @@ new PMT program=20000 version=1 pcrpid=110 - desc 42 stuffing length=4 - desc 2b mpeg2_aac_audio profile=0x12 channel_config=0x05 additional_info=0x00 - desc 7c aac profile_and_level="0x14" aac_type_flag="1" aac_type="0x1f" + * ES pid=200 streamtype=0x86 streamtype_txt="SCTE 35 Splice Information Table" end PMT new DIT transition_flag=1 end DIT @@ -341,9 +342,11 @@ new SIT version=0 end SIT new SIT version=1 - desc 63 partial_transport_stream peak_rate=5000 min_overall_smoothing_rate=7000 max_overall_smoothing_buffer=1000 - * SERVICE sid=20000 running_status=1 + * SERVICE sid=20000 running=1 - desc 05 registration identifier=TEST - desc 05 registration identifier=TEST - desc 05 registration identifier=TEST - * SERVICE sid=21000 running_status=3 + * SERVICE sid=21000 running=3 end SIT +new SCTE35 command=5 command_str=insert pts_adjustment=0 event_id=4242 cancel=false out_of_network=true program_splice=true splice_time=270000000 auto_return=true duration=27000000 unique_program_id=2424 +end SCTE35 diff --git a/examples/dvb_print_si.output.xml b/examples/dvb_print_si.output.xml index 779c74e..9de4947 100644 --- a/examples/dvb_print_si.output.xml +++ b/examples/dvb_print_si.output.xml @@ -120,9 +120,9 @@ - + - + @@ -248,15 +248,15 @@ - + - - + + - + @@ -276,7 +276,7 @@ - + @@ -307,7 +307,7 @@ - + @@ -350,9 +350,9 @@ - - - + + + @@ -603,10 +603,12 @@ - + + + @@ -616,7 +618,7 @@ - + @@ -627,7 +629,9 @@ - + + + diff --git a/scte/35.h b/scte/35.h new file mode 100644 index 0000000..c579089 --- /dev/null +++ b/scte/35.h @@ -0,0 +1,732 @@ +/***************************************************************************** + * 35.h: SCTE 35 Digital Program Insertion Cueing Message for Cable + ***************************************************************************** + * Copyright (C) 2015 VideoLAN + * + * Authors: Christophe Massiot + * + * 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: + * - SCTE 35 2013 (Digital Program Insertion Cueing Message for Cable) + */ + +#ifndef __BITSTREAM_SCTE_35_H__ +#define __BITSTREAM_SCTE_35_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************************************************************** + * Splice Information Table + *****************************************************************************/ +#define SCTE35_TABLE_ID 0xfc +#define SCTE35_HEADER_SIZE (PSI_HEADER_SIZE + 11) +#define SCTE35_HEADER2_SIZE 2 + +static inline void scte35_init(uint8_t *p_scte35) +{ + psi_set_tableid(p_scte35, SCTE35_TABLE_ID); + psi_init(p_scte35, false); + p_scte35[1] &= ~0x40; /* private indicator */ + p_scte35[3] = 0; + p_scte35[4] = 0; + p_scte35[9] = 0; + p_scte35[10] = 0xff; + p_scte35[11] = 0xf0; +} + +static inline uint8_t scte35_get_protocol(const uint8_t *p_scte35) +{ + return p_scte35[3]; +} + +static inline void scte35_set_protocol(uint8_t *p_scte35, uint8_t i_version) +{ + p_scte35[3] = i_version; +} + +static inline bool scte35_is_encrypted(const uint8_t *p_scte35) +{ + return !!(p_scte35[4] & 0x80); +} + +static inline void scte35_set_encrypted(uint8_t *p_scte35, bool b_encrypted) +{ + if (b_encrypted) + p_scte35[4] |= 0x80; + else + p_scte35[4] &= ~0x80; +} + +static inline uint64_t scte35_get_pts_adjustment(const uint8_t *p_scte35) +{ + return ((uint64_t)(p_scte35[4] & 0x1) << 32) | + (p_scte35[5] << 24) | (p_scte35[6] << 16) | + (p_scte35[7] << 8) | p_scte35[8]; +} + +static inline void scte35_set_pts_adjustment(uint8_t *p_scte35, + uint64_t i_adjustment) +{ + p_scte35[4] &= ~0x1; + p_scte35[4] |= (i_adjustment >> 32) & 0x1; + p_scte35[5] = (i_adjustment >> 24) & 0xff; + p_scte35[6] = (i_adjustment >> 16) & 0xff; + p_scte35[7] = (i_adjustment >> 8) & 0xff; + p_scte35[8] = i_adjustment & 0xff; +} + +static inline uint16_t scte35_get_command_length(const uint8_t *p_scte35) +{ + return ((p_scte35[11] & 0xf) << 8) | p_scte35[12]; +} + +static inline void scte35_set_command_length(uint8_t *p_scte35, + uint16_t i_length) +{ + p_scte35[11] &= ~0xf; + p_scte35[11] |= (i_length >> 8) & 0xf; + p_scte35[12] = i_length & 0xff; +} + +static inline uint8_t scte35_get_command_type(const uint8_t *p_scte35) +{ + return p_scte35[13]; +} + +static inline void scte35_set_command_type(uint8_t *p_scte35, uint8_t i_type) +{ + p_scte35[13] = i_type; +} + +static inline uint8_t *scte35_get_command(const uint8_t *p_scte35) +{ + return (uint8_t *)p_scte35 + SCTE35_HEADER_SIZE; +} + +static inline uint16_t scte35_get_desclength(const uint8_t *p_scte35) +{ + uint16_t i_command_length = scte35_get_command_length(p_scte35); + if (i_command_length == 0xfff) + return 0; + const uint8_t *pi_desclength = scte35_get_command(p_scte35) + + i_command_length; + return (pi_desclength[0] << 8) | pi_desclength[1]; +} + +static inline void scte35_set_desclength(uint8_t *p_scte35, uint16_t i_length) +{ + uint8_t *pi_desclength = scte35_get_command(p_scte35) + + scte35_get_command_length(p_scte35); + pi_desclength[0] = (i_length >> 8) & 0xff; + pi_desclength[1] = i_length & 0xff; +} + +static inline uint8_t *scte35_get_descl(const uint8_t *p_scte35) +{ + uint16_t i_command_length = scte35_get_command_length(p_scte35); + if (i_command_length == 0xfff) + return NULL; + return scte35_get_command(p_scte35) + i_command_length + + SCTE35_HEADER2_SIZE; +} + +/***************************************************************************** + * Splice Information Table - splice_time structure + *****************************************************************************/ +#define SCTE35_SPLICE_TIME_HEADER_SIZE 1 +#define SCTE35_SPLICE_TIME_TIME_SIZE 4 + +static inline void scte35_splice_time_init(uint8_t *p_splice_time) +{ + p_splice_time[0] = 0x7f; +} + +static inline bool scte35_splice_time_has_time_specified(const uint8_t *p_splice_time) +{ + return !!(p_splice_time[0] & 0x80); +} + +static inline void scte35_splice_time_set_time_specified(uint8_t *p_splice_time, + bool b_time_specified) +{ + if (b_time_specified) + p_splice_time[0] |= 0x80; + else + p_splice_time[0] &= ~0x80; +} + +static inline uint64_t scte35_splice_time_get_pts_time(const uint8_t *p_splice_time) +{ + return ((uint64_t)(p_splice_time[0] & 0x1) << 32) | + ((uint64_t)p_splice_time[1] << 24) | + ((uint64_t)p_splice_time[2] << 16) | + ((uint64_t)p_splice_time[3] << 8) | + (uint64_t)p_splice_time[4]; +} + +static inline void scte35_splice_time_set_pts_time(uint8_t *p_splice_time, + uint64_t i_pts_time) +{ + p_splice_time[0] &= ~0x1; + p_splice_time[0] |= (i_pts_time >> 32) & 0x1; + p_splice_time[1] = (i_pts_time >> 24) & 0xff; + p_splice_time[2] = (i_pts_time >> 16) & 0xff; + p_splice_time[3] = (i_pts_time >> 8) & 0xff; + p_splice_time[4] = i_pts_time & 0xff; +} + +static inline uint8_t scte35_splice_time_size(const uint8_t *p_splice_time) +{ + return SCTE35_SPLICE_TIME_HEADER_SIZE + + (scte35_splice_time_has_time_specified(p_splice_time) ? + SCTE35_SPLICE_TIME_TIME_SIZE : 0); +} + +/***************************************************************************** + * Splice Information Table - break_duration structure + *****************************************************************************/ +#define SCTE35_BREAK_DURATION_HEADER_SIZE 5 + +static inline void scte35_break_duration_init(uint8_t *p_break_duration) +{ + p_break_duration[0] = 0xff; +} + +static inline bool scte35_break_duration_has_auto_return(const uint8_t *p_break_duration) +{ + return !!(p_break_duration[0] & 0x80); +} + +static inline void scte35_break_duration_set_auto_return(uint8_t *p_break_duration, bool b_auto_return) +{ + if (b_auto_return) + p_break_duration[0] |= 0x80; + else + p_break_duration[0] &= ~0x80; +} + +static inline uint64_t scte35_break_duration_get_duration(const uint8_t *p_break_duration) +{ + return ((uint64_t)(p_break_duration[0] & 0x1) << 32) | + ((uint64_t)p_break_duration[1] << 24) | + ((uint64_t)p_break_duration[2] << 16) | + ((uint64_t)p_break_duration[3] << 8) | + (uint64_t)p_break_duration[4]; +} + +static inline void scte35_break_duration_set_duration(uint8_t *p_break_duration, + uint64_t i_duration) +{ + p_break_duration[0] &= ~0x1; + p_break_duration[0] |= (i_duration >> 32) & 0x1; + p_break_duration[1] = (i_duration >> 24) & 0xff; + p_break_duration[2] = (i_duration >> 16) & 0xff; + p_break_duration[3] = (i_duration >> 8) & 0xff; + p_break_duration[4] = i_duration & 0xff; +} + +/***************************************************************************** + * Splice Information Table - null command + *****************************************************************************/ +#define SCTE35_NULL_COMMAND 0 +#define SCTE35_NULL_HEADER_SIZE 0 + +static inline void scte35_null_init(uint8_t *p_scte35) +{ + scte35_init(p_scte35); + scte35_set_command_type(p_scte35, SCTE35_NULL_COMMAND); + scte35_set_command_length(p_scte35, SCTE35_NULL_HEADER_SIZE); + scte35_set_desclength(p_scte35, 0); +} + +static inline bool scte35_null_validate(const uint8_t *p_scte35) +{ + return true; +} + +/***************************************************************************** + * Splice Information Table - schedule command + *****************************************************************************/ +#define SCTE35_SCHEDULE_COMMAND 4 + +/* TODO Not implemented (useless) */ + +/***************************************************************************** + * Splice Information Table - insert command + *****************************************************************************/ +#define SCTE35_INSERT_COMMAND 5 +#define SCTE35_INSERT_HEADER_SIZE 5 +#define SCTE35_INSERT_HEADER2_SIZE 1 +#define SCTE35_INSERT_COMPONENT_COUNT_SIZE 1 +#define SCTE35_INSERT_COMPONENT_HEADER_SIZE 1 +#define SCTE35_INSERT_FOOTER_SIZE 4 + +static inline void scte35_insert_init(uint8_t *p_scte35, uint16_t i_length) +{ + scte35_init(p_scte35); + scte35_set_command_type(p_scte35, SCTE35_INSERT_COMMAND); + scte35_set_command_length(p_scte35, + SCTE35_INSERT_HEADER_SIZE + i_length); + scte35_set_desclength(p_scte35, 0); + + uint8_t *p_command = scte35_get_command(p_scte35); + p_command[4] = 0xff; +} + +static inline uint32_t scte35_insert_get_event_id(const uint8_t *p_scte35) +{ + const uint8_t *p_command = scte35_get_command(p_scte35); + return (p_command[0] << 24) | (p_command[1] << 16) | + (p_command[2] << 8) | p_command[3]; +} + +static inline void scte35_insert_set_event_id(uint8_t *p_scte35, + uint32_t i_event_id) +{ + uint8_t *p_command = scte35_get_command(p_scte35); + p_command[0] = (i_event_id >> 24) & 0xff; + p_command[1] = (i_event_id >> 16) & 0xff; + p_command[2] = (i_event_id >> 8) & 0xff; + p_command[3] = i_event_id & 0xff; +} + +static inline bool scte35_insert_has_cancel(const uint8_t *p_scte35) +{ + const uint8_t *p_command = scte35_get_command(p_scte35); + return !!(p_command[4] & 0x80); +} + +static inline void scte35_insert_set_cancel(const uint8_t *p_scte35, + bool b_cancel) +{ + uint8_t *p_command = scte35_get_command(p_scte35); + if (b_cancel) + p_command[4] |= 0x80; + else { + p_command[4] &= ~0x80; + p_command[5] = 0xff; + } +} + +static inline bool scte35_insert_has_out_of_network(const uint8_t *p_scte35) +{ + const uint8_t *p_command = scte35_get_command(p_scte35); + return !!(p_command[5] & 0x80); +} + +static inline void scte35_insert_set_out_of_network(const uint8_t *p_scte35, + bool b_out_of_network) +{ + uint8_t *p_command = scte35_get_command(p_scte35); + if (b_out_of_network) + p_command[5] |= 0x80; + else + p_command[5] &= ~0x80; +} + +static inline bool scte35_insert_has_program_splice(const uint8_t *p_scte35) +{ + const uint8_t *p_command = scte35_get_command(p_scte35); + return !!(p_command[5] & 0x40); +} + +static inline void scte35_insert_set_program_splice(const uint8_t *p_scte35, + bool b_program_splice) +{ + uint8_t *p_command = scte35_get_command(p_scte35); + if (b_program_splice) + p_command[5] |= 0x40; + else + p_command[5] &= ~0x40; +} + +static inline bool scte35_insert_has_duration(const uint8_t *p_scte35) +{ + const uint8_t *p_command = scte35_get_command(p_scte35); + return !!(p_command[5] & 0x20); +} + +static inline void scte35_insert_set_duration(const uint8_t *p_scte35, + bool b_duration) +{ + uint8_t *p_command = scte35_get_command(p_scte35); + if (b_duration) + p_command[5] |= 0x20; + else + p_command[5] &= ~0x20; +} + +static inline bool scte35_insert_has_splice_immediate(const uint8_t *p_scte35) +{ + const uint8_t *p_command = scte35_get_command(p_scte35); + return !!(p_command[5] & 0x10); +} + +static inline void scte35_insert_set_splice_immediate(const uint8_t *p_scte35, + bool b_splice_immediate) +{ + uint8_t *p_command = scte35_get_command(p_scte35); + if (b_splice_immediate) + p_command[5] |= 0x10; + else + p_command[5] &= ~0x10; +} + +static inline uint8_t *scte35_insert_get_splice_time(const uint8_t *p_scte35) +{ + uint8_t *p_command = scte35_get_command(p_scte35); + return p_command + SCTE35_INSERT_HEADER_SIZE + SCTE35_INSERT_HEADER2_SIZE; +} + +static inline uint8_t scte35_insert_get_component_count(const uint8_t *p_scte35) +{ + uint8_t *p_command = scte35_get_command(p_scte35); + return p_command[SCTE35_INSERT_HEADER_SIZE + SCTE35_INSERT_HEADER2_SIZE]; +} + +static inline void scte35_insert_set_component_count(uint8_t *p_scte35, + uint8_t i_component_count) +{ + uint8_t *p_command = scte35_get_command(p_scte35); + p_command[SCTE35_INSERT_HEADER_SIZE + SCTE35_INSERT_HEADER2_SIZE] = + i_component_count; +} + +static inline uint8_t scte35_insert_component_get_component_tag(const uint8_t *p_component) +{ + return p_component[0]; +} + +static inline void scte35_insert_component_set_component_tag(uint8_t *p_component, uint8_t i_component_tag) +{ + p_component[0] = i_component_tag; +} + +static inline uint8_t *scte35_insert_component_get_splice_time(const uint8_t *p_component) +{ + return (uint8_t *)p_component + SCTE35_INSERT_COMPONENT_HEADER_SIZE; +} + +static inline uint8_t *scte35_insert_get_component(const uint8_t *p_scte35, + uint8_t n) +{ + uint16_t i_section_size = psi_get_length(p_scte35) + PSI_HEADER_SIZE + - PSI_CRC_SIZE; + bool b_splice_immediate = scte35_insert_has_splice_immediate(p_scte35); + uint8_t *p_scte35_n = scte35_get_command(p_scte35) + + SCTE35_INSERT_HEADER_SIZE + SCTE35_INSERT_HEADER2_SIZE + + SCTE35_INSERT_COMPONENT_COUNT_SIZE; + if (p_scte35_n - p_scte35 > i_section_size) + return NULL; + + while (n) { + if (p_scte35_n + SCTE35_INSERT_COMPONENT_HEADER_SIZE - p_scte35 > + i_section_size) + return NULL; + p_scte35_n += SCTE35_INSERT_COMPONENT_HEADER_SIZE + + (b_splice_immediate ? + scte35_splice_time_size( + scte35_insert_component_get_splice_time(p_scte35_n)) : + 0); + n--; + } + if (p_scte35_n - p_scte35 >= i_section_size) return NULL; + return p_scte35_n; +} + +static inline uint8_t *scte35_insert_get_break_duration(const uint8_t *p_scte35) +{ + if (!scte35_insert_has_program_splice(p_scte35)) + return scte35_insert_get_component(p_scte35, + scte35_insert_get_component_count(p_scte35) + 1); + + if (scte35_insert_has_splice_immediate(p_scte35)) + return scte35_get_command(p_scte35) + SCTE35_INSERT_HEADER_SIZE + + SCTE35_INSERT_HEADER2_SIZE; + + uint8_t *p_splice_time = scte35_insert_get_splice_time(p_scte35); + return p_splice_time + scte35_splice_time_size(p_splice_time); +} + +static inline uint8_t *scte35_insert_get_footer(const uint8_t *p_scte35) +{ + return scte35_insert_get_break_duration(p_scte35) + + (scte35_insert_has_duration(p_scte35) ? + SCTE35_BREAK_DURATION_HEADER_SIZE : 0); +} + +static inline uint16_t scte35_insert_get_unique_program_id(const uint8_t *p_scte35) +{ + uint8_t *p_footer = scte35_insert_get_footer(p_scte35); + return (p_footer[0] << 8) | p_footer[1]; +} + +static inline void scte35_insert_set_unique_program_id(uint8_t *p_scte35, + uint16_t i_unique_program_id) +{ + uint8_t *p_footer = scte35_insert_get_footer(p_scte35); + p_footer[0] = (i_unique_program_id >> 8) & 0xff; + p_footer[1] = i_unique_program_id & 0xff; +} + +static inline uint8_t scte35_insert_get_avail_num(const uint8_t *p_scte35) +{ + uint8_t *p_footer = scte35_insert_get_footer(p_scte35); + return p_footer[2]; +} + +static inline void scte35_insert_set_avail_num(uint8_t *p_scte35, + uint8_t i_avail_num) +{ + uint8_t *p_footer = scte35_insert_get_footer(p_scte35); + p_footer[2] = i_avail_num; +} + +static inline uint8_t scte35_insert_get_avails_expected(const uint8_t *p_scte35) +{ + uint8_t *p_footer = scte35_insert_get_footer(p_scte35); + return p_footer[3]; +} + +static inline void scte35_insert_set_avails_expected(uint8_t *p_scte35, + uint8_t i_avails_expected) +{ + uint8_t *p_footer = scte35_insert_get_footer(p_scte35); + p_footer[3] = i_avails_expected; +} + +static inline bool scte35_insert_validate(const uint8_t *p_scte35) +{ + size_t i_length = scte35_get_command_length(p_scte35); + if (i_length < SCTE35_INSERT_HEADER_SIZE) + return false; + + if (scte35_insert_has_cancel(p_scte35)) + return i_length <= SCTE35_INSERT_HEADER_SIZE; + if (i_length < SCTE35_INSERT_HEADER_SIZE + SCTE35_INSERT_HEADER2_SIZE) + return false; + + if (scte35_insert_has_program_splice(p_scte35)) { + if (scte35_insert_has_splice_immediate(p_scte35)) { + if (scte35_insert_has_duration(p_scte35)) + return i_length >= SCTE35_INSERT_HEADER_SIZE + + SCTE35_INSERT_HEADER2_SIZE + + SCTE35_BREAK_DURATION_HEADER_SIZE + + SCTE35_INSERT_FOOTER_SIZE; + return i_length >= SCTE35_INSERT_HEADER_SIZE + + SCTE35_INSERT_HEADER2_SIZE + + SCTE35_INSERT_FOOTER_SIZE; + } + + if (i_length < SCTE35_INSERT_HEADER_SIZE + SCTE35_INSERT_HEADER2_SIZE + + SCTE35_SPLICE_TIME_HEADER_SIZE || + i_length < SCTE35_INSERT_HEADER_SIZE + SCTE35_INSERT_HEADER2_SIZE + + scte35_splice_time_size( + scte35_insert_get_splice_time(p_scte35))) + return false; + + if (scte35_insert_has_duration(p_scte35)) + return i_length >= SCTE35_INSERT_HEADER_SIZE + + SCTE35_INSERT_HEADER2_SIZE + + scte35_splice_time_size(scte35_insert_get_splice_time(p_scte35)) + + SCTE35_BREAK_DURATION_HEADER_SIZE + + SCTE35_INSERT_FOOTER_SIZE; + return i_length >= SCTE35_INSERT_HEADER_SIZE + + SCTE35_INSERT_HEADER2_SIZE + + scte35_splice_time_size(scte35_insert_get_splice_time(p_scte35)) + + SCTE35_INSERT_FOOTER_SIZE; + } + + if (i_length < SCTE35_INSERT_HEADER_SIZE + SCTE35_INSERT_HEADER2_SIZE + + SCTE35_INSERT_COMPONENT_COUNT_SIZE) + return false; + + const uint8_t *p_command = scte35_get_command(p_scte35); + const uint8_t *p_end = scte35_insert_get_component(p_scte35, + scte35_insert_get_component_count(p_scte35) + 1); + if (p_end == NULL) + return false; + + if (scte35_insert_has_duration(p_scte35)) + return i_length >= p_end + SCTE35_BREAK_DURATION_HEADER_SIZE + + SCTE35_INSERT_FOOTER_SIZE - p_command; + return i_length >= p_end + SCTE35_INSERT_FOOTER_SIZE - p_command; +} + +/***************************************************************************** + * Splice Information Table - time_signal command + *****************************************************************************/ +#define SCTE35_TIME_SIGNAL_COMMAND 6 +#define SCTE35_TIME_SIGNAL_HEADER_SIZE SCTE35_SPLICE_TIME_HEADER_SIZE +#define SCTE35_TIME_SIGNAL_TIME_SIZE SCTE35_SPLICE_TIME_TIME_SIZE + +static inline void scte35_time_signal_init(uint8_t *p_scte35, uint16_t i_length) +{ + scte35_init(p_scte35); + scte35_set_command_type(p_scte35, SCTE35_TIME_SIGNAL_COMMAND); + scte35_set_command_length(p_scte35, + SCTE35_TIME_SIGNAL_HEADER_SIZE + i_length); + scte35_set_desclength(p_scte35, 0); +} + +static inline uint8_t *scte35_time_signal_get_splice_time(const uint8_t *p_scte35) +{ + return scte35_get_command(p_scte35); +} + +static inline bool scte35_time_signal_validate(const uint8_t *p_scte35) +{ + return scte35_get_command_length(p_scte35) >= + scte35_splice_time_size(scte35_time_signal_get_splice_time(p_scte35)); +} + +/***************************************************************************** + * Splice Information Table - bandwidth_reservation command + *****************************************************************************/ +#define SCTE35_BANDWIDTH_RESERVATION_COMMAND 7 +#define SCTE35_BANDWIDTH_RESERVATION_HEADER_SIZE 0 + +static inline void scte35_bandwidth_reservation_init(uint8_t *p_scte35) +{ + scte35_init(p_scte35); + scte35_set_command_type(p_scte35, SCTE35_BANDWIDTH_RESERVATION_COMMAND); + scte35_set_command_length(p_scte35, + SCTE35_BANDWIDTH_RESERVATION_HEADER_SIZE); + scte35_set_desclength(p_scte35, 0); +} + +static inline bool scte35_bandwidth_reservation_validate(const uint8_t *p_scte35) +{ + return true; +} + +/***************************************************************************** + * Splice Information Table - private command + *****************************************************************************/ +#define SCTE35_PRIVATE_COMMAND 0xff +#define SCTE35_PRIVATE_HEADER_SIZE 4 + +static inline void scte35_private_init(uint8_t *p_scte35, uint16_t i_length) +{ + scte35_init(p_scte35); + scte35_set_command_type(p_scte35, SCTE35_PRIVATE_COMMAND); + scte35_set_command_length(p_scte35, + SCTE35_PRIVATE_HEADER_SIZE + i_length); + scte35_set_desclength(p_scte35, 0); +} + +static inline uint32_t scte35_private_get_identifier(const uint8_t *p_scte35) +{ + const uint8_t *p_command = scte35_get_command(p_scte35); + return (p_command[0] << 24) | (p_command[1] << 16) | + (p_command[2] << 8) | p_command[3]; +} + +static inline void scte35_private_set_identifier(uint8_t *p_scte35, + uint32_t i_identifier) +{ + uint8_t *p_command = scte35_get_command(p_scte35); + p_command[0] = (i_identifier >> 24) & 0xff; + p_command[1] = (i_identifier >> 16) & 0xff; + p_command[2] = (i_identifier >> 8) & 0xff; + p_command[3] = i_identifier & 0xff; +} + +static inline bool scte35_private_validate(const uint8_t *p_scte35) +{ + return scte35_get_command_length(p_scte35) >= SCTE35_PRIVATE_HEADER_SIZE; +} + +/***************************************************************************** + * Splice Information Table validation + *****************************************************************************/ +static inline bool scte35_validate(const uint8_t *p_scte35) +{ + if (psi_get_syntax(p_scte35) || + psi_get_tableid(p_scte35) != SCTE35_TABLE_ID) + return false; + + if (!psi_check_crc(p_scte35)) + return false; + if (scte35_get_protocol(p_scte35)) + return false; + + uint16_t i_section_size = psi_get_length(p_scte35) + PSI_HEADER_SIZE + - PSI_CRC_SIZE; + if (i_section_size < SCTE35_HEADER_SIZE) + return false; + + uint16_t i_command_length = scte35_get_command_length(p_scte35); + if (i_command_length != 0xfff && + (i_section_size < SCTE35_HEADER_SIZE + i_command_length + + SCTE35_HEADER2_SIZE || + i_section_size < SCTE35_HEADER_SIZE + i_command_length + + SCTE35_HEADER2_SIZE + + scte35_get_desclength(p_scte35))) + return false; + + switch (scte35_get_command_type(p_scte35)) { + case SCTE35_NULL_COMMAND: + if (!scte35_null_validate(p_scte35)) + return false; + break; + case SCTE35_INSERT_COMMAND: + if (!scte35_insert_validate(p_scte35)) + return false; + break; + case SCTE35_TIME_SIGNAL_COMMAND: + if (!scte35_time_signal_validate(p_scte35)) + return false; + break; + case SCTE35_BANDWIDTH_RESERVATION_COMMAND: + if (!scte35_bandwidth_reservation_validate(p_scte35)) + return false; + break; + case SCTE35_PRIVATE_COMMAND: + if (!scte35_private_validate(p_scte35)) + return false; + break; + default: + break; + } + + if (i_command_length != 0xfff && + !descl_validate(scte35_get_descl(p_scte35), + scte35_get_desclength(p_scte35))) + return false; + + return true; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/scte/35_print.h b/scte/35_print.h new file mode 100644 index 0000000..e8324fc --- /dev/null +++ b/scte/35_print.h @@ -0,0 +1,296 @@ +/***************************************************************************** + * 35_print.h: SCTE 35 Digital Program Insertion Cueing Message for Cable + * (printing) + ***************************************************************************** + * Copyright (C) 2015 VideoLAN + * + * Authors: Christophe Massiot + * + * 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_SCTE_35_PRINT_H__ +#define __BITSTREAM_SCTE_35_PRINT_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************************************************************** + * Splice Information Table + *****************************************************************************/ +static inline const char *scte35_get_command_type_txt(uint8_t i_type) { + switch (i_type) { + case SCTE35_NULL_COMMAND: return "null"; + case SCTE35_SCHEDULE_COMMAND: return "schedule"; + case SCTE35_INSERT_COMMAND: return "insert"; + case SCTE35_TIME_SIGNAL_COMMAND: return "time_signal"; + case SCTE35_BANDWIDTH_RESERVATION_COMMAND: return "bandwidth_reservation"; + case SCTE35_PRIVATE_COMMAND: return "private"; + default: return "reserved"; + } +} + +static inline void scte35_null_print(const uint8_t *p_scte35, + f_print pf_print, void *print_opaque, print_type_t i_print_type) +{ + uint64_t i_pts_adjustment = scte35_get_pts_adjustment(p_scte35); + + switch (i_print_type) { + case PRINT_XML: + pf_print(print_opaque, + "", + SCTE35_NULL_COMMAND, i_pts_adjustment); + break; + default: + pf_print(print_opaque, + "new SCTE35 command=%"PRIu8" command_str=null pts_adjustment=%"PRIu64, + SCTE35_NULL_COMMAND, i_pts_adjustment); + } +} + +static inline void scte35_insert_print(const uint8_t *p_scte35, + f_print pf_print, void *print_opaque, print_type_t i_print_type) +{ + uint64_t i_pts_adjustment = scte35_get_pts_adjustment(p_scte35); + uint32_t i_event_id = scte35_insert_get_event_id(p_scte35); + + if (scte35_insert_has_cancel(p_scte35)) { + switch (i_print_type) { + case PRINT_XML: + pf_print(print_opaque, + "", + SCTE35_INSERT_COMMAND, i_pts_adjustment, i_event_id); + break; + default: + pf_print(print_opaque, + "new SCTE35 command=%"PRIu8" command_str=insert pts_adjustment=%"PRIu64" event_id=%"PRIu32" cancel=true", + SCTE35_INSERT_COMMAND, i_pts_adjustment, i_event_id); + } + return; + } + + bool b_out_of_network = scte35_insert_has_out_of_network(p_scte35); + bool b_program_splice = scte35_insert_has_program_splice(p_scte35); + bool b_duration = scte35_insert_has_duration(p_scte35); + bool b_splice_immediate = scte35_insert_has_splice_immediate(p_scte35); + uint16_t i_unique_program_id = scte35_insert_get_unique_program_id(p_scte35); + + char psz_duration[256]; + psz_duration[0] = '\0'; + psz_duration[255] = '\0'; + if (b_duration) { + const uint8_t *p_break_duration = + scte35_insert_get_break_duration(p_scte35); + bool b_auto_return = + scte35_break_duration_has_auto_return(p_break_duration); + uint64_t i_duration = + scte35_break_duration_get_duration(p_break_duration); + + switch (i_print_type) { + case PRINT_XML: + snprintf(psz_duration, 255, + " auto_return=\"%d\" duration=\"%"PRIu64"\"", + b_auto_return ? 1 : 0, i_duration); + break; + default: + snprintf(psz_duration, 255, + " auto_return=%s duration=%"PRIu64"", + b_auto_return ? "true" : "false", i_duration); + } + } + + char psz_splice_time[256]; + psz_splice_time[0] = '\0'; + psz_splice_time[255] = '\0'; + if (b_splice_immediate) { + switch (i_print_type) { + case PRINT_XML: + snprintf(psz_splice_time, 255, " splice_time=\"immediate\""); + break; + default: + snprintf(psz_splice_time, 255, " splice_time=immediate"); + } + } else { + const uint8_t *p_splice_time = NULL; + if (b_program_splice) + p_splice_time = scte35_insert_get_splice_time(p_scte35); + else if (scte35_insert_get_component_count(p_scte35)) + p_splice_time = scte35_insert_component_get_splice_time( + scte35_insert_get_component(p_scte35, 0)); + + if (p_splice_time == NULL || + !scte35_splice_time_has_time_specified(p_splice_time)) { + switch (i_print_type) { + case PRINT_XML: + snprintf(psz_splice_time, 255, " splice_time=\"undefined\""); + break; + default: + snprintf(psz_splice_time, 255, " splice_time=undefined"); + } + } else { + uint64_t i_pts_time = + scte35_splice_time_get_pts_time(p_splice_time); + + switch (i_print_type) { + case PRINT_XML: + snprintf(psz_splice_time, 255, " splice_time=\"%"PRIu64"\"", + i_pts_time); + break; + default: + snprintf(psz_splice_time, 255, " splice_time=%"PRIu64"", + i_pts_time); + } + } + } + + switch (i_print_type) { + case PRINT_XML: + pf_print(print_opaque, + "", + SCTE35_INSERT_COMMAND, i_pts_adjustment, i_event_id, + b_out_of_network ? 1 : 0, b_program_splice ? 1 : 0, + psz_splice_time, psz_duration, i_unique_program_id); + break; + default: + pf_print(print_opaque, + "new SCTE35 command=%"PRIu8" command_str=insert pts_adjustment=%"PRIu64" event_id=%"PRIu32" cancel=false out_of_network=%s program_splice=%s%s%s unique_program_id=%"PRIu16, + SCTE35_INSERT_COMMAND, i_pts_adjustment, i_event_id, + b_out_of_network ? "true" : "false", + b_program_splice ? "true" : "false", + psz_splice_time, psz_duration, i_unique_program_id); + } +} + +static inline void scte35_time_signal_print(const uint8_t *p_scte35, + f_print pf_print, void *print_opaque, print_type_t i_print_type) +{ + uint64_t i_pts_adjustment = scte35_get_pts_adjustment(p_scte35); + const uint8_t *p_splice_time = scte35_time_signal_get_splice_time(p_scte35); + + switch (i_print_type) { + case PRINT_XML: + if (scte35_splice_time_has_time_specified(p_splice_time)) + pf_print(print_opaque, + "", + SCTE35_TIME_SIGNAL_COMMAND, i_pts_adjustment, + scte35_splice_time_get_pts_time(p_splice_time)); + else + pf_print(print_opaque, + "", + SCTE35_TIME_SIGNAL_COMMAND, i_pts_adjustment); + break; + default: + if (scte35_splice_time_has_time_specified(p_splice_time)) + pf_print(print_opaque, + "new SCTE35 command=%"PRIu8" command_str=time_signal pts_adjustment=%"PRIu64" splice_time=%"PRIu64, + SCTE35_TIME_SIGNAL_COMMAND, i_pts_adjustment, + scte35_splice_time_get_pts_time(p_splice_time)); + else + pf_print(print_opaque, + "new SCTE35 command=%"PRIu8" command_str=time_signal pts_adjustment=%"PRIu64" splice_time=undefined", + SCTE35_TIME_SIGNAL_COMMAND, i_pts_adjustment); + } +} + +static inline void scte35_private_print(const uint8_t *p_scte35, + f_print pf_print, void *print_opaque, print_type_t i_print_type) +{ + uint64_t i_pts_adjustment = scte35_get_pts_adjustment(p_scte35); + uint32_t i_identifier = scte35_private_get_identifier(p_scte35); + + switch (i_print_type) { + case PRINT_XML: + pf_print(print_opaque, + "", + SCTE35_PRIVATE_COMMAND, i_pts_adjustment, i_identifier); + break; + default: + pf_print(print_opaque, + "new SCTE35 command=%"PRIu8" command_str=private pts_adjustment=%"PRIu64" identifier=%"PRIu32, + SCTE35_PRIVATE_COMMAND, i_pts_adjustment, i_identifier); + } +} + +static inline void scte35_print(const uint8_t *p_scte35, + f_print pf_print, void *print_opaque, print_type_t i_print_type) +{ + uint8_t i_type = scte35_get_command_type(p_scte35); + bool done = false; + + switch (i_type) { + case SCTE35_NULL_COMMAND: + scte35_null_print(p_scte35, pf_print, print_opaque, i_print_type); + done = true; + break; + case SCTE35_INSERT_COMMAND: + scte35_insert_print(p_scte35, pf_print, print_opaque, i_print_type); + done = true; + break; + case SCTE35_TIME_SIGNAL_COMMAND: + scte35_time_signal_print(p_scte35, pf_print, print_opaque, i_print_type); + done = true; + break; + case SCTE35_PRIVATE_COMMAND: + scte35_private_print(p_scte35, pf_print, print_opaque, i_print_type); + done = true; + break; + default: + break; + } + + if (!done) { + uint64_t i_pts_adjustment = scte35_get_pts_adjustment(p_scte35); + + switch (i_print_type) { + case PRINT_XML: + pf_print(print_opaque, + "", + i_type, scte35_get_command_type_txt(i_type), i_pts_adjustment); + break; + default: + pf_print(print_opaque, + "new SCTE35 command=%"PRIu8" command_str=%s pts_adjustment=%"PRIu64, + i_type, scte35_get_command_type_txt(i_type), i_pts_adjustment); + } + } + + /* TODO: print descriptors */ + + switch (i_print_type) { + case PRINT_XML: + pf_print(print_opaque, ""); + break; + default: + pf_print(print_opaque, "end SCTE35"); + } +} + +#ifdef __cplusplus +} +#endif + +#endif