diff --git a/cablelabs/cablelabs.c b/cablelabs/cablelabs.c index 677d43f..afe86f8 100644 --- a/cablelabs/cablelabs.c +++ b/cablelabs/cablelabs.c @@ -38,3 +38,10 @@ void write_scte_adaptation_descriptor( bs_t *s ) bs_write( s, 8, SCTE_ADAPTATION_FIELD_DESCRIPTOR_TAG ); // descriptor_tag bs_write( s, 8, 0 ); // descriptor_length } + +void write_scte35_cue_identifier_descriptor( bs_t *s ) +{ + bs_write( s, 8, SCTE35_CUE_IDENTIFIER_DESCRIPTOR_TAG ); // descriptor_tag + bs_write( s, 8, 1 ); // descriptor_length + bs_write( s, 8, 0 ); // cue_stream_type +} \ No newline at end of file diff --git a/cablelabs/cablelabs.h b/cablelabs/cablelabs.h index 5a8986a..267f20c 100644 --- a/cablelabs/cablelabs.h +++ b/cablelabs/cablelabs.h @@ -24,8 +24,10 @@ /* Descriptors */ #define SCTE_ADAPTATION_FIELD_DESCRIPTOR_TAG 0x97 #define CABLELABS_3D_MPEG2_DESCRIPTOR_TAG 0xe8 +#define SCTE35_CUE_IDENTIFIER_DESCRIPTOR_TAG 0x8a void write_cablelabs_3d_descriptor( bs_t *s ); void write_scte_adaptation_descriptor( bs_t *s ); +void write_scte35_cue_identifier_descriptor( bs_t *s ); #endif diff --git a/common.h b/common.h index 5ec3197..88ba3c9 100644 --- a/common.h +++ b/common.h @@ -44,10 +44,13 @@ #define PRIVATE_SECTION 0x05 #define PRIVATE_DATA 0x06 +#define DATA_SCTE35 0x86 + #define TS_HEADER_SIZE 4 #define TS_PACKET_SIZE 188 #define TS_CLOCK 27000000LL #define TS_START 10 +#define TIMESTAMP_CLOCK 90000LL // arbitrary #define MAX_PROGRAMS 100 @@ -97,7 +100,7 @@ #define MIN(a,b) ( (a)<(b) ? (a) : (b) ) #define MAX(a,b) ( (a)>(b) ? (a) : (b) ) -#define IS_VIDEO(x) ( x->stream_format == LIBMPEGTS_VIDEO_MPEG2 || x->stream_format == LIBMPEGTS_VIDEO_AVC ) +#define IS_VIDEO(x) ( x->stream_format == LIBMPEGTS_VIDEO_MPEG2 || x->stream_format == LIBMPEGTS_VIDEO_AVC || x->stream_format == LIBMPEGTS_VIDEO_DIRAC ) /* Internal Program & Stream Structures */ typedef struct @@ -201,6 +204,9 @@ typedef struct int aac_profile; int aac_channel_map; + /* Opus */ + int opus_channel_map; + /* ATSC */ /* DVB */ @@ -284,12 +290,14 @@ struct ts_writer_t uint64_t bytes_written; uint64_t packets_written; + uint64_t pcr_start; int ts_type; int ts_id; int cbr; int ts_muxrate; + int lowlatency; int pat_cc; diff --git a/dvb/dvb.c b/dvb/dvb.c index 8a430c0..575cd73 100644 --- a/dvb/dvb.c +++ b/dvb/dvb.c @@ -80,7 +80,7 @@ void write_teletext_descriptor( bs_t *s, ts_int_stream_t *stream, int vbi ) for( int j = 0; j < 3; j++ ) bs_write( s, 8, teletext->lang_code[j] ); // ISO_639_language_code bs_write( s, 5, teletext->teletext_type ); // teletext_type - bs_write( s, 3, teletext->teletext_magazine_number ); // teletext_magazine_number + bs_write( s, 3, teletext->teletext_magazine_number == 8 ? 0 : teletext->teletext_magazine_number ); // teletext_magazine_number bs_write( s, 8, teletext->teletext_page_number ); // teletext_page_number } } @@ -127,11 +127,11 @@ static void write_service_descriptor( bs_t *s, int service_type, char *provider_ bs_write( s, 8, provider_name_length ); // service_provider_name_length while( *provider_name != '\0' ) - bs_write( s, 8, *provider_name++ ); + bs_write( s, 8, (unsigned char)*provider_name++ ); bs_write( s, 8, service_name_length ); // service_name_length while( *service_name != '\0' ) - bs_write( s, 8, *service_name++ ); + bs_write( s, 8, (unsigned char)*service_name++ ); } /* DVB Service Information */ diff --git a/dvb/dvb.h b/dvb/dvb.h index 9cc7c30..e881a64 100644 --- a/dvb/dvb.h +++ b/dvb/dvb.h @@ -32,6 +32,7 @@ #define DVB_ADAPTATION_FIELD_DATA_DESCRIPTOR 0x70 #define DVB_EAC3_DESCRIPTOR_TAG 0x7a #define DVB_AAC_DESCRIPTOR_TAG 0x7c +#define DVB_EXTENSION_DESCRIPTOR_TAG 0x7f /* PIDs */ #define SDT_PID 0x0011 diff --git a/libmpegts.c b/libmpegts.c index 6f6dd1d..5dd0ed5 100644 --- a/libmpegts.c +++ b/libmpegts.c @@ -29,10 +29,11 @@ #include "crc/crc.h" #include -static const int steam_type_table[27][2] = +static const int stream_type_table[30][2] = { { LIBMPEGTS_VIDEO_MPEG2, VIDEO_MPEG2 }, { LIBMPEGTS_VIDEO_AVC, VIDEO_AVC }, + { LIBMPEGTS_VIDEO_DIRAC, 0xd1 }, { LIBMPEGTS_AUDIO_MPEG1, AUDIO_MPEG1 }, { LIBMPEGTS_AUDIO_MPEG2, AUDIO_MPEG2 }, { LIBMPEGTS_AUDIO_ADTS, AUDIO_ADTS }, @@ -57,6 +58,8 @@ static const int steam_type_table[27][2] = { LIBMPEGTS_DVB_VBI, PRIVATE_DATA }, { LIBMPEGTS_ANCILLARY_RDD11, PRIVATE_DATA }, { LIBMPEGTS_ANCILLARY_2038, PRIVATE_DATA }, + { LIBMPEGTS_AUDIO_OPUS, PRIVATE_DATA }, + { LIBMPEGTS_DATA_SCTE35, DATA_SCTE35 }, { 0 }, }; @@ -186,30 +189,35 @@ static void write_iso_lang_descriptor( bs_t *s, ts_int_stream_t *stream ) bs_write(s, 8, stream->audio_type ); // audio_type } -/**** PCR functions ****/ -static int check_pcr( ts_writer_t *w, ts_int_program_t *program ) +/** Misc descriptors **/ +static void write_opus_descriptor( bs_t *s, ts_int_stream_t *stream ) { - // if the next packet written goes over the max pcr retransmit boundary, write the pcr in the next packet - double next_pkt_pcr = ((w->packets_written * TS_PACKET_SIZE) + (TS_PACKET_SIZE + 7)) * 8.0 / w->ts_muxrate - - (double)program->last_pcr / TS_CLOCK; - next_pkt_pcr += TS_START; - - if( next_pkt_pcr >= (double)w->pcr_period / 1000 ) - { - return 1; - } - - return 0; + bs_write( s, 8, DVB_EXTENSION_DESCRIPTOR_TAG ); // descriptor_tag + bs_write( s, 8, 0x2 ); // descriptor_length + bs_write( s, 8, 0x80 ); // descriptor_tag_extension (User defined) + bs_write( s, 8, stream->opus_channel_map ); // channel_config_code } +/**** PCR functions ****/ static int64_t get_pcr_int( ts_writer_t *w, double offset ) { - return (int64_t)((8.0 * (w->packets_written * TS_PACKET_SIZE + offset) / w->ts_muxrate) * TS_CLOCK + 0.5) + TS_START * TS_CLOCK; + return (int64_t)((8.0 * (w->packets_written * TS_PACKET_SIZE + offset) / w->ts_muxrate) * TS_CLOCK + 0.5) + w->pcr_start; } static double get_pcr_double( ts_writer_t *w, double offset ) { - return (8.0 * (w->packets_written * TS_PACKET_SIZE + offset) / w->ts_muxrate) + TS_START; + return (8.0 * (w->packets_written * TS_PACKET_SIZE + offset) / w->ts_muxrate) + (double)w->pcr_start / TS_CLOCK; +} + +static int check_pcr( ts_writer_t *w, ts_int_program_t *program ) +{ + // if the next packet written goes over the max pcr retransmit boundary, write the pcr in the next packet + int64_t next_pkt_pcr = get_pcr_int( w, (TS_PACKET_SIZE + 7) * 8 ) - program->last_pcr; + + if( next_pkt_pcr >= w->pcr_period * (TS_CLOCK/1000) ) + return 1; + + return 0; } /**** Buffer management ****/ @@ -404,11 +412,6 @@ static int eject_queued_pmt( ts_writer_t *w, ts_int_program_t *program, bs_t *s memmove( &program->pmt_packets[0], &program->pmt_packets[1], (program->num_queued_pmt-1) * sizeof(uint8_t*) ); temp = realloc( program->pmt_packets, (program->num_queued_pmt-1) * sizeof(uint8_t*) ); - if( !temp ) - { - fprintf( stderr, "malloc failed\n" ); - return -1; - } program->pmt_packets = temp; program->num_queued_pmt--; @@ -476,6 +479,14 @@ static int write_pmt( ts_writer_t *w, ts_int_program_t *program ) else if( w->ts_type == TS_TYPE_BLU_RAY ) write_registration_descriptor( &q, REGISTRATION_DESCRIPTOR_TAG, 4, "HDMV" ); + for( int i = 0; i < program->num_streams; i++ ) + { + ts_int_stream_t *stream = program->streams[i]; + + if( stream->stream_format == LIBMPEGTS_DATA_SCTE35 ) + write_registration_descriptor( &q, REGISTRATION_DESCRIPTOR_TAG, 4, "CUEI" ); + } + /* Optional descriptor(s) here */ bs_flush( &q ); @@ -494,7 +505,7 @@ static int write_pmt( ts_writer_t *w, ts_int_program_t *program ) /* reset temporary bitstream context for streams loop */ bs_init( &q, temp1, 512 ); - if( stream->stream_format != LIBMPEGTS_ANCILLARY_RDD11 ) + if( stream->stream_format != LIBMPEGTS_ANCILLARY_RDD11 && stream->stream_format != LIBMPEGTS_DATA_SCTE35 ) write_data_stream_alignment_descriptor( &q ); if( stream->dvb_au ) @@ -522,6 +533,8 @@ static int write_pmt( ts_writer_t *w, ts_int_program_t *program ) if( w->ts_type == TS_TYPE_BLU_RAY ) write_hdmv_video_registration_descriptor( &q, stream ); } + else if( stream->stream_format == LIBMPEGTS_VIDEO_DIRAC ) + write_registration_descriptor( &q, REGISTRATION_DESCRIPTOR_TAG, 4, "BBCD" ); else if( stream->stream_format == LIBMPEGTS_AUDIO_MPEG1 || stream->stream_format == LIBMPEGTS_AUDIO_MPEG2 ) { @@ -550,7 +563,7 @@ static int write_pmt( ts_writer_t *w, ts_int_program_t *program ) // TODO } else if( stream->stream_format == LIBMPEGTS_AUDIO_302M ) - write_registration_descriptor( &q, PRIVATE_DATA_DESCRIPTOR_TAG, 4, "BSSD" ); + write_registration_descriptor( &q, REGISTRATION_DESCRIPTOR_TAG, 4, "BSSD" ); else if( stream->stream_format == LIBMPEGTS_DVB_SUB ) write_dvb_subtitling_descriptor( &q, stream ); else if( stream->stream_format == LIBMPEGTS_DVB_TELETEXT ) @@ -562,12 +575,19 @@ static int write_pmt( ts_writer_t *w, ts_int_program_t *program ) write_teletext_descriptor( &q, stream, 1 ); } else if( stream->stream_format == LIBMPEGTS_ANCILLARY_RDD11 ) - write_registration_descriptor( &q, PRIVATE_DATA_DESCRIPTOR_TAG, 4, "LU-A" ); + write_registration_descriptor( &q, REGISTRATION_DESCRIPTOR_TAG, 4, "LU-A" ); else if( stream->stream_format == LIBMPEGTS_ANCILLARY_2038 ) { - write_registration_descriptor( &q, PRIVATE_DATA_DESCRIPTOR_TAG, 4, "VANC" ); + write_registration_descriptor( &q, REGISTRATION_DESCRIPTOR_TAG, 4, "VANC" ); write_anc_data_descriptor( &q ); } + else if( stream->stream_format == LIBMPEGTS_AUDIO_OPUS ) + { + write_registration_descriptor( &q, REGISTRATION_DESCRIPTOR_TAG, 4, "Opus" ); + write_opus_descriptor( &q, stream ); + } + else if( stream->stream_format == LIBMPEGTS_DATA_SCTE35 ) + write_scte35_cue_identifier_descriptor( &q ); // TODO other stream_type descriptors @@ -640,11 +660,11 @@ static int write_pmt( ts_writer_t *w, ts_int_program_t *program ) return 0; } -static void retransmit_psi_and_si( ts_writer_t *w, ts_int_program_t *program, int first ) +static void retransmit_psi_and_si( ts_writer_t *w, ts_int_program_t *program ) { // TODO make this work with multiple programs int64_t cur_pcr = get_pcr_int( w, 0 ); - if( cur_pcr - w->last_pat >= w->pat_period * 27000LL || first ) + if( cur_pcr - w->last_pat >= w->pat_period * 27000LL || !w->last_pat ) { /* Although it is not in line with the mux strategy it is good practice to write PAT and PMT together */ w->last_pat = cur_pcr; @@ -654,7 +674,7 @@ static void retransmit_psi_and_si( ts_writer_t *w, ts_int_program_t *program, in cur_pcr = get_pcr_int( w, 0 ); - if( w->sdt && ( cur_pcr - w->last_sdt >= w->sdt_period * 27000LL || first ) ) + if( w->sdt && ( cur_pcr - w->last_sdt >= w->sdt_period * 27000LL || !w->last_sdt ) ) { w->last_sdt = cur_pcr; write_sdt( w ); @@ -760,9 +780,11 @@ static int write_pes( ts_writer_t *w, ts_int_program_t *program, ts_frame_t *in_ bs_write1( &q, 0 ); // DSM_trick_mode_flag bs_write1( &q, 0 ); // additional_copy_info_flag bs_write1( &q, 0 ); // PES_CRC_flag - bs_write1( &q, 0 ); // PES_extension_flag + bs_write1( &q, stream->stream_format == LIBMPEGTS_VIDEO_DIRAC ); // PES_extension_flag - if( stream->stream_format == LIBMPEGTS_DVB_TELETEXT || stream->stream_format == LIBMPEGTS_DVB_VBI ) + if( stream->stream_format == LIBMPEGTS_VIDEO_DIRAC ) + bs_write( &q, 8, 0x08 ); // PES_header_data_length + else if( stream->stream_format == LIBMPEGTS_DVB_TELETEXT || stream->stream_format == LIBMPEGTS_DVB_VBI ) bs_write( &q, 8, 0x24 ); // PES_header_data_length else if( same_timestamps ) bs_write( &q, 8, 0x05 ); // PES_header_data_length (PTS only) @@ -779,6 +801,21 @@ static int write_pes( ts_writer_t *w, ts_int_program_t *program, ts_frame_t *in_ write_timestamp( &q, out_pes->dts % mod ); // DTS } + if( stream->stream_format == LIBMPEGTS_VIDEO_DIRAC ) + { + bs_write1( &q, 0 ); // PES_private_data_flag + bs_write1( &q, 0 ); // pack_header_field_flag + bs_write1( &q, 0 ); // program_packet_sequence_counter_flag + bs_write1( &q, 0 ); // P-STD_buffer_flag + bs_write( &q, 3, 0x0a ); // reserved + bs_write1( &q, 1 ); // PES_extension_flag_2 + + bs_write1( &q, 1 ); // marker_bit + bs_write( &q, 7, 1 ); // PES_header_data_length + bs_write1( &q, 0 ); // stream_id_extension_flag + bs_write( &q, 7, 0x60 ); // stream_id_extension + } + /* TTX and VBI require extra stuffing */ if( stream->stream_format == LIBMPEGTS_DVB_TELETEXT || stream->stream_format == LIBMPEGTS_DVB_VBI ) { @@ -791,7 +828,8 @@ static int write_pes( ts_writer_t *w, ts_int_program_t *program, ts_frame_t *in_ bs_flush( &q ); total_size = in_frame->size + (bs_pos( &q ) >> 3); - if( stream->stream_format == LIBMPEGTS_VIDEO_MPEG2 || stream->stream_format == LIBMPEGTS_VIDEO_AVC ) + if( stream->stream_format == LIBMPEGTS_VIDEO_MPEG2 || stream->stream_format == LIBMPEGTS_VIDEO_AVC || + stream->stream_format == LIBMPEGTS_VIDEO_DIRAC ) bs_write( &s, 16, 0 ); // PES_packet_length else bs_write( &s, 16, total_size ); // PES_packet_length @@ -852,6 +890,21 @@ static int check_bitstream( ts_writer_t *w ) return 0; } +/* set updatable ts parameters */ +static void update_ts_params( ts_writer_t *w, ts_main_t *params ) +{ + w->ts_muxrate = params->muxrate; + w->cbr = params->cbr; + w->legacy_constraints = params->legacy_constraints; + w->lowlatency = params->lowlatency; + + w->pcr_period = params->pcr_period ? params->pcr_period : PCR_MAX_RETRANS_TIME; + w->pat_period = params->pat_period ? params->pat_period : PAT_MAX_RETRANS_TIME; + w->sdt_period = params->sdt_period ? params->sdt_period : SDT_MAX_RETRANS_TIME; + + w->r_sys = MAX( R_SYS_DEFAULT, (double)w->ts_muxrate / 500 ); +} + ts_writer_t *ts_create_writer( void ) { ts_writer_t *w = calloc( 1, sizeof(*w) ); @@ -968,16 +1021,16 @@ int ts_setup_transport_stream( ts_writer_t *w, ts_main_t *params ) cur_stream->pid = stream_in->pid; cur_stream->stream_format = stream_in->stream_format; - for( int j = 0; steam_type_table[j][0] != 0; j++ ) + for( int j = 0; stream_type_table[j][0] != 0; j++ ) { - if( cur_stream->stream_format == steam_type_table[j][0] ) + if( cur_stream->stream_format == stream_type_table[j][0] ) { /* DVB AC-3 and EAC-3 are different */ if( w->ts_type == TS_TYPE_DVB && ( cur_stream->stream_format == LIBMPEGTS_AUDIO_AC3 || cur_stream->stream_format == LIBMPEGTS_AUDIO_EAC3 ) ) j++; - cur_stream->stream_type = steam_type_table[j][1]; + cur_stream->stream_type = stream_type_table[j][1]; break; } } @@ -1023,7 +1076,8 @@ int ts_setup_transport_stream( ts_writer_t *w, ts_main_t *params ) cur_stream->tb.buf_size = TB_SIZE; /* setup T-STD buffers when audio buffers sizes are independent of number of channels */ - if( cur_stream->stream_format == LIBMPEGTS_AUDIO_MPEG1 || cur_stream->stream_format == LIBMPEGTS_AUDIO_MPEG2 ) + if( cur_stream->stream_format == LIBMPEGTS_AUDIO_MPEG1 || cur_stream->stream_format == LIBMPEGTS_AUDIO_MPEG2 || + cur_stream->stream_format == LIBMPEGTS_AUDIO_OPUS ) { /* use the defaults */ cur_stream->rx = MISC_AUDIO_RXN; @@ -1034,6 +1088,30 @@ int ts_setup_transport_stream( ts_writer_t *w, ts_main_t *params ) cur_stream->rx = MISC_AUDIO_RXN; cur_stream->mb.buf_size = w->ts_type == TS_TYPE_ATSC || w->ts_type == TS_TYPE_CABLELABS ? AC3_BS_ATSC : AC3_BS_DVB; } + else if( cur_stream->stream_format == LIBMPEGTS_AUDIO_302M ) + { + /* Use some made up value because (surprise surprise) SMPTE hasn't defined it properly + * 7 bytes in 24-bit packing * 4 pairs * 48000 * 1.2 */ + cur_stream->rx = 7 * 4 * 48000 * 8 * 6 / 5; + cur_stream->mb.buf_size = SMPTE_302M_AUDIO_BS; + } + else if( cur_stream->stream_format == LIBMPEGTS_VIDEO_DIRAC ) + { +#define DIRAC_MAX_BITRATE 10000000 + int bitrate = DIRAC_MAX_BITRATE * 1.2; + int bs_mux = 0.004 * bitrate; + int bs_oh = 1.0 * bitrate / 50.0; + + cur_stream->mb.buf_size = bs_mux + bs_oh; + cur_stream->eb.buf_size = 10000000*8; + + cur_stream->rx = bitrate; + cur_stream->rbx = bitrate; + } + else if( cur_stream->stream_format == LIBMPEGTS_ANCILLARY_2038 ) + { + cur_stream->rx = 1.2 * 2500000; + } cur_program->streams[cur_program->num_streams] = cur_stream; cur_program->num_streams++; @@ -1049,21 +1127,15 @@ int ts_setup_transport_stream( ts_writer_t *w, ts_main_t *params ) cur_program->pcr_stream = pcr_stream; } - w->ts_id = params->ts_id; - w->ts_muxrate = params->muxrate; - w->cbr = params->cbr; + update_ts_params( w, params ); + w->network_pid = params->network_pid; - w->legacy_constraints = params->legacy_constraints; - - w->pcr_period = params->pcr_period ? params->pcr_period : PCR_MAX_RETRANS_TIME; - w->pat_period = params->pat_period ? params->pat_period : PAT_MAX_RETRANS_TIME; - w->sdt_period = params->sdt_period ? params->sdt_period : SDT_MAX_RETRANS_TIME; - w->network_id = params->network_id ? params->network_id : DEFAULT_NID; - + w->ts_id = params->ts_id; w->tb.buf_size = TB_SIZE; w->rx_sys = RX_SYS; - w->r_sys = MAX( R_SYS_DEFAULT, (double)w->ts_muxrate / 500 ); + + w->pcr_start = TS_START * TS_CLOCK; // FIXME realloc if necessary @@ -1081,6 +1153,17 @@ int ts_setup_transport_stream( ts_writer_t *w, ts_main_t *params ) return 0; } +void ts_update_transport_stream( ts_writer_t *w, ts_main_t *params ) +{ + int64_t cur_pcr = get_pcr_int( w, 0 ); + if( params->muxrate != w->ts_muxrate ) + { + update_ts_params( w, params ); + w->packets_written = 0; + w->pcr_start = cur_pcr; + } +} + /* Codec-specific features */ int ts_setup_mpegvideo_stream( ts_writer_t *w, int pid, int level, int profile, int vbv_maxrate, int vbv_bufsize, int frame_rate ) @@ -1287,24 +1370,8 @@ int ts_setup_mpeg4_aac_stream( ts_writer_t *w, int pid, int profile_and_level, i return 0; }; -int ts_setup_302m_stream( ts_writer_t *w, int pid, int bit_depth, int num_channels ) +int ts_setup_opus_stream( ts_writer_t *w, int pid, int channel_map ) { - if( w->ts_type == TS_TYPE_BLU_RAY ) - { - fprintf( stderr, "SMPTE 302M not allowed in Blu-Ray\n" ); - return -1; - } - else if( !(bit_depth == 16 || bit_depth == 20 || bit_depth == 24) ) - { - fprintf( stderr, "Invalid Bit Depth for SMPTE 302M\n" ); - return -1; - } - else if( (num_channels & 1) || num_channels <= 0 || num_channels > 8 ) - { - fprintf( stderr, "Invalid number of channels for SMPTE 302M\n" ); - return -1; - } - ts_int_stream_t *stream = find_stream( w, pid ); if( !stream ) @@ -1313,20 +1380,7 @@ int ts_setup_302m_stream( ts_writer_t *w, int pid, int bit_depth, int num_channe return -1; } - if( stream->lpcm_ctx ) - free( stream->lpcm_ctx ); - - stream->lpcm_ctx = calloc( 1, sizeof(lpcm_stream_ctx_t) ); - if( !stream->lpcm_ctx ) - return -1; - - stream->lpcm_ctx->bits_per_sample = bit_depth; - stream->lpcm_ctx->num_channels = num_channels; - - stream->mb.buf_size = SMPTE_302M_AUDIO_BS; - - /* 302M frame size is bit_depth / 4 + 1 */ - stream->rx = 1.2 * ((bit_depth >> 2) + 1) * SMPTE_302M_AUDIO_SR * 8; + stream->opus_channel_map = channel_map; return 0; } @@ -1601,18 +1655,20 @@ int ts_write_frames( ts_writer_t *w, ts_frame_t *frames, int num_frames, uint8_t new_pes[i]->stream = stream; new_pes[i]->random_access = !!frames[i].random_access; new_pes[i]->priority = !!frames[i].priority; - new_pes[i]->dts = frames[i].dts + TS_START * 90000LL; - new_pes[i]->pts = frames[i].pts + TS_START * 90000LL; + new_pes[i]->dts = frames[i].dts + TS_START * TIMESTAMP_CLOCK; + new_pes[i]->pts = frames[i].pts + TS_START * TIMESTAMP_CLOCK; if( IS_VIDEO( stream ) ) { new_pes[i]->frame_type = frames[i].frame_type; - new_pes[i]->initial_arrival_time = frames[i].cpb_initial_arrival_time + TS_START * 27000000LL; - new_pes[i]->final_arrival_time = frames[i].cpb_final_arrival_time + TS_START * 27000000LL; + new_pes[i]->initial_arrival_time = frames[i].cpb_initial_arrival_time + TS_START * TS_CLOCK; + new_pes[i]->final_arrival_time = frames[i].cpb_final_arrival_time + TS_START * TS_CLOCK; new_pes[i]->ref_pic_idc = frames[i].ref_pic_idc; new_pes[i]->write_pulldown_info = frames[i].write_pulldown_info; new_pes[i]->pic_struct = frames[i].pic_struct; } + else if( stream->stream_format == LIBMPEGTS_AUDIO_302M || stream->stream_format == LIBMPEGTS_DATA_SCTE35 || stream->stream_format == LIBMPEGTS_ANCILLARY_2038 ) + new_pes[i]->initial_arrival_time = (new_pes[i]->dts * 300) - frames[i].duration; else if( stream->stream_format == LIBMPEGTS_DVB_TELETEXT ) new_pes[i]->initial_arrival_time = (new_pes[i]->dts - 3600) * 300; /* Teletext is special because data can only stay in the buffer for 40ms */ else if( stream->stream_format == LIBMPEGTS_DVB_SUB ) @@ -1649,10 +1705,22 @@ int ts_write_frames( ts_writer_t *w, ts_frame_t *frames, int num_frames, uint8_t return -1; } - new_pes[i]->header_size = write_pes( w, program, &frames[i], new_pes[i] ); + /* Not technically a PES but put it through the same codepath */ + if ( stream->stream_format == LIBMPEGTS_DATA_SCTE35 ) + { + new_pes[i]->data[0] = 0; // pointer_field + memcpy( new_pes[i]->data+1, frames[i].data, frames[i].size ); + new_pes[i]->size = new_pes[i]->bytes_left = frames[i].size + 1; + new_pes[i]->cur_pos = new_pes[i]->data; + new_pes[i]->header_size = 0; + } + else + { + new_pes[i]->header_size = write_pes( w, program, &frames[i], new_pes[i] ); + } } - if( !initial_queued_pes ) + if( !w->lowlatency && !initial_queued_pes ) { out = NULL; *len = 0; @@ -1667,28 +1735,41 @@ int ts_write_frames( ts_writer_t *w, ts_frame_t *frames, int num_frames, uint8_t { if( write_pcr_empty( w, program, 1 ) < 0 ) return -1; - retransmit_psi_and_si( w, program, 1 ); w->first_input = 1; } - /* loop through and find the time when the second video packet in the queue can arrive */ - int video_found = 0; + + int video_found = 0, start = 0; int64_t pcr_stop = 0; cur_pcr = get_pcr_int( w, 0 ); - for( int i = 0; i < w->num_buffered_frames; i++ ) + if( w->lowlatency ) { - stream = queued_pes[i]->stream; - if( IS_VIDEO( stream ) ) + /* Find the latest arrival time in the batch of packets delivered */ + for( int i = 0; i < w->num_buffered_frames; i++ ) { - /* last frame is a special case - FIXME: is this acceptable in all use-cases? */ - if( !num_frames ) - pcr_stop = queued_pes[i]->dts; - else if( !video_found ) - video_found = 1; - else - pcr_stop = queued_pes[i]->initial_arrival_time; /* earliest that a frame can arrive */ + stream = queued_pes[i]->stream; + if( queued_pes[i]->final_arrival_time > pcr_stop ) + pcr_stop = queued_pes[i]->final_arrival_time; + } + } + else + { + /* loop through and find the time when the second video packet in the queue can arrive */ + for( int i = 0; i < w->num_buffered_frames; i++ ) + { + stream = queued_pes[i]->stream; + if( IS_VIDEO( stream ) ) + { + /* last frame is a special case - FIXME: is this acceptable in all use-cases? */ + if( !num_frames ) + pcr_stop = queued_pes[i]->dts; + else if( !video_found ) + video_found = 1; + else + pcr_stop = queued_pes[i]->initial_arrival_time; /* earliest that a frame can arrive */ + } } } @@ -1717,6 +1798,8 @@ int ts_write_frames( ts_writer_t *w, ts_frame_t *frames, int num_frames, uint8_t /* Check all the non-video packets first */ if( !need_pcr ) { + retransmit_psi_and_si( w, program ); + for( int i = 0; i < w->num_buffered_frames; i++ ) { stream = queued_pes[i]->stream; @@ -1834,7 +1917,12 @@ int ts_write_frames( ts_writer_t *w, ts_frame_t *frames, int num_frames, uint8_t /* special case where the adaptation_field_length byte is the stuffing */ // FIXME except for cablelabs legacy - if( stuffing == 1 && !adapt_field_len ) + + if( stream->stream_format == LIBMPEGTS_DATA_SCTE35 ) + { + adapt_field_len = 0; /* Theoretically could be made PCR */ + } + else if( stuffing == 1 && !adapt_field_len ) { stuffing = flags = 0; adapt_field_len = 1; @@ -1845,11 +1933,15 @@ int ts_write_frames( ts_writer_t *w, ts_frame_t *frames, int num_frames, uint8_t stuffing -= 2; /* 2 bytes for adaptation field in this case. NOTE: needs fixing if more private data added */ } + start = bs_pos( s ); write_packet_header( w, s, pes_start, stream->pid, PAYLOAD_ONLY + ((!!adapt_field_len)<<1), &stream->cc ); if( adapt_field_len ) write_adaptation_field( w, s, program, pes, write_pcr, flags, stuffing, 0 ); - write_bytes(s, pes->cur_pos, pes->bytes_left ); + write_bytes( s, pes->cur_pos, pes->bytes_left ); + if( stream->stream_format == LIBMPEGTS_DATA_SCTE35 ) + write_padding( s, start ); + pes->bytes_left = 0; add_to_buffer( &stream->tb ); if( increase_pcr( w, 1, 0 ) < 0 ) @@ -1872,8 +1964,6 @@ int ts_write_frames( ts_writer_t *w, ts_frame_t *frames, int num_frames, uint8_t free( pes->data ); free( pes ); } - - retransmit_psi_and_si( w, program, 0 ); } else /* no packets can be written */ { @@ -2026,12 +2116,16 @@ int increase_pcr( ts_writer_t *w, int num_packets, int imaginary ) // TODO do this for all programs ts_int_program_t *program = w->programs[0]; - double next_pcr = TS_START + (w->packets_written + num_packets) * 8.0 * TS_PACKET_SIZE / w->ts_muxrate; + double next_pcr = get_pcr_double( w, num_packets * TS_PACKET_SIZE ); /* buffer drip (TODO: all buffers?) */ drip_buffer( w, program, w->rx_sys, &w->tb, next_pcr ); for( int i = 0; i < program->num_streams; i++ ) { - drip_buffer( w, program, program->streams[i]->rx, &program->streams[i]->tb, next_pcr ); + /* SCTE35 is PSI so not part of T-STD */ + if( program->streams[i]->stream_format == LIBMPEGTS_DATA_SCTE35 ) + program->streams[i]->tb.cur_buf = 0; + else + drip_buffer( w, program, program->streams[i]->rx, &program->streams[i]->tb, next_pcr ); } w->packets_written += num_packets; diff --git a/libmpegts.h b/libmpegts.h index ff7415f..ea644a8 100644 --- a/libmpegts.h +++ b/libmpegts.h @@ -38,6 +38,7 @@ /* Generic */ #define LIBMPEGTS_VIDEO_MPEG2 1 #define LIBMPEGTS_VIDEO_AVC 2 +#define LIBMPEGTS_VIDEO_DIRAC 3 #define LIBMPEGTS_AUDIO_MPEG1 32 #define LIBMPEGTS_AUDIO_MPEG2 33 @@ -69,7 +70,8 @@ #define LIBMPEGTS_DVB_VBI 130 /* Misc */ - +#define LIBMPEGTS_AUDIO_OPUS 160 +#define LIBMPEGTS_DATA_SCTE35 161 /**** Stream IDs ****/ /* SMPTE 302M, AC3, DVB subtitles and Teletext use Private Stream 1 */ @@ -82,6 +84,9 @@ /* MPEG Video (all types): 0xe0-0xef */ #define LIBMPEGTS_STREAM_ID_MPEGVIDEO 0xe0 +/* Extended Stream-id */ +#define LIBMPEGTS_STREAM_ID_EXTENDED 0xfd + /**** Blu-Ray Information ****/ /* Blu-Ray Aspect Ratios */ #define LIBMPEGTS_HDMV_AR_4_3 2 @@ -311,6 +316,7 @@ typedef struct ts_main_t int muxrate; int cbr; int ts_type; + int lowlatency; int network_pid; @@ -329,6 +335,15 @@ typedef struct ts_main_t int ts_setup_transport_stream( ts_writer_t *w, ts_main_t *params ); +/* update transport stream + * + * muxrate is the only tested parameter + * + * TODO: implement versioning so other parameters can be updated + * + */ +void ts_update_transport_stream( ts_writer_t *w, ts_main_t *params ); + /**** Additional Codec-Specific functions ****/ /* Many formats require extra information. Setup the relevant information using the following functions */ @@ -412,6 +427,15 @@ int ts_setup_mpeg4_aac_stream( ts_writer_t *w, int pid, int profile_and_level, i int ts_setup_302m_stream( ts_writer_t *w, int pid, int bit_depth, int num_channels ); +/* Opus */ + +#define LIBMPEGTS_CHANNEL_CONFIG_DUAL_MONO 0x00 +#define LIBMPEGTS_CHANNEL_CONFIG_MONO 0x01 +#define LIBMPEGTS_CHANNEL_CONFIG_STEREO 0x02 + +int ts_setup_opus_stream( ts_writer_t *w, int pid, int channel_map ); + + /**** DVB Specific Information ****/ /* DVB Subtitles */ @@ -648,6 +672,7 @@ typedef struct int64_t cpb_final_arrival_time; int64_t dts; int64_t pts; + int64_t duration; /* SMPTE 302M only */ int random_access; int priority;