/***************************************************************************** * libmpegts.c : ***************************************************************************** * Copyright (C) 2010 Kieran Kunhya * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. *****************************************************************************/ #include "common.h" #include "codecs.h" #include "atsc/atsc.h" #include "cablelabs/cablelabs.h" #include "dvb/dvb.h" #include "hdmv/hdmv.h" #include "isdb/isdb.h" #include "smpte/smpte.h" #include "crc/crc.h" #include 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 }, { LIBMPEGTS_AUDIO_LATM, AUDIO_LATM }, { LIBMPEGTS_AUDIO_AC3, AUDIO_AC3 }, /* ATSC/Generic */ { LIBMPEGTS_AUDIO_AC3, PRIVATE_DATA }, /* DVB */ { LIBMPEGTS_AUDIO_EAC3, AUDIO_EAC3 }, /* ATSC/Generic */ { LIBMPEGTS_AUDIO_EAC3, PRIVATE_DATA }, /* DVB */ { LIBMPEGTS_AUDIO_LPCM, AUDIO_LPCM }, { LIBMPEGTS_AUDIO_DTS, AUDIO_DTS }, { LIBMPEGTS_AUDIO_DOLBY_LOSSLESS, AUDIO_DOLBY_LOSSLESS }, { LIBMPEGTS_AUDIO_DTS_HD, AUDIO_DTS_HD }, { LIBMPEGTS_AUDIO_DTS_HD_XLL, AUDIO_DTS_HD_XLL }, { LIBMPEGTS_AUDIO_EAC3_SECONDARY, AUDIO_EAC3_SECONDARY }, { LIBMPEGTS_AUDIO_DTS_HD_SECONDARY, AUDIO_DTS_HD_SECONDARY }, { LIBMPEGTS_SUB_PRESENTATION_GRAPHICS, SUB_PRESENTATION_GRAPHICS }, { LIBMPEGTS_SUB_INTERACTIVE_GRAPHICS, SUB_INTERACTIVE_GRAPHICS }, { LIBMPEGTS_SUB_TEXT, SUB_TEXT }, { LIBMPEGTS_AUDIO_302M, PRIVATE_DATA }, { LIBMPEGTS_DVB_SUB, PRIVATE_DATA }, { LIBMPEGTS_DVB_TELETEXT, PRIVATE_DATA }, { 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 }, }; /**** Descriptors ****/ /** MPEG-2 Systems Descriptors **/ /* Registration Descriptor */ void write_registration_descriptor( bs_t *s, int descriptor_tag, int descriptor_length, char *format_id ) { bs_write( s, 8, descriptor_tag ); // descriptor_tag bs_write( s, 8, descriptor_length ); // descriptor_length while( *format_id != '\0' ) bs_write( s, 8, *format_id++ ); // format_identifier } /* First loop of PMT Descriptors */ static void write_smoothing_buffer_descriptor( bs_t *s, ts_int_program_t *program ) { bs_write( s, 8, SMOOTHING_BUFFER_DESCRIPTOR_TAG ); // descriptor_tag bs_write( s, 8, 0x6 ); // descriptor_length bs_write( s, 2, 0x3 ); // reserved bs_write( s, 22, program->sb_leak_rate ); // sb_leak_rate bs_write( s, 2, 0x3 ); // reserved bs_write( s, 22, program->sb_size ); // sb_size } /* Second loop of PMT Descriptors */ #if 0 static void write_video_stream_descriptor( bs_t *s, ts_int_stream_t *stream ) { bs_write( s, 8, VIDEO_STREAM_DESCRIPTOR_TAG ); // descriptor_tag bs_write( s, 8, 0x04 ); // descriptor_length bs_write1( s, 0 ); // multiple_frame_rate_flag bs_write( s, 4, 0 ); // frame_rate_code FIXME bs_write1( s, 0 ); // MPEG_1_only_flag bs_write1( s, 0 ); // constrained_parameter_flag bs_write1( s, 0 ); // still_picture_flag bs_write( s, 8, 0 ); // profile_and_level_indication FIXME bs_write( s, 2, 0 ); // chroma_format FIXME bs_write1( s, 0 ); // frame_rate_extension_flag FIXME bs_write( s, 5, 0x1f ); // reserved } #endif static void write_avc_descriptor( bs_t *s, ts_int_program_t *program, ts_int_stream_t *stream ) { bs_write( s, 8, AVC_DESCRIPTOR_TAG ); // descriptor_tag bs_write( s, 8, 0x04 ); // descriptor_length bs_write( s, 8, avc_profiles[stream->mpegvideo_ctx->profile] ); // profile_idc bs_write1( s, stream->mpegvideo_ctx->profile == AVC_BASELINE ); // constraint_set0_flag bs_write1( s, stream->mpegvideo_ctx->profile <= AVC_MAIN ); // constraint_set1_flag bs_write1( s, 0 ); // constraint_set2_flag if( stream->mpegvideo_ctx->level == 9 && stream->mpegvideo_ctx->profile <= AVC_MAIN ) // level 1b bs_write1( s, 1 ); // constraint_set3_flag else if( stream->mpegvideo_ctx->profile == AVC_HIGH_10_INTRA || stream->mpegvideo_ctx->profile == AVC_CAVLC_444_INTRA || stream->mpegvideo_ctx->profile == AVC_HIGH_444_INTRA ) bs_write1( s, 1 ); // constraint_set3_flag else bs_write1( s, 0 ); // constraint_set3_flag bs_write1( s, 0 ); // constraint_set4_flag bs_write1( s, 0 ); // constraint_set5_flag bs_write( s, 2, 0 ); // reserved bs_write( s, 8, stream->mpegvideo_ctx->level & 0xff ); // level_idc bs_write( s, 1, 0 ); // AVC_still_present bs_write( s, 1, 0 ); // AVC_24_hour_picture_flag bs_write( s, 1, !program->is_3dtv ); // Frame_Packing_SEI_not_present_flag bs_write( s, 5, 0x1f ); // reserved } static void write_data_stream_alignment_descriptor( bs_t *s ) { bs_write( s, 8, DATA_STREAM_ALIGNMENT_DESCRIPTOR_TAG ); // descriptor_tag bs_write( s, 8, 1 ); // descriptor_length // FIXME make UK-DTG compliant bs_write( s, 8, 1 ); // alignment_type } static void write_mpeg2_aac_descriptor( bs_t *s, ts_int_stream_t *stream ) { bs_write( s, 8, MPEG2_AAC_AUDIO_DESCRIPTOR ); // descriptor_tag bs_write( s, 8, 0x3 ); // descriptor_length bs_write( s, 8, stream->aac_profile ); // MPEG-2_AAC_profile bs_write( s, 8, stream->aac_channel_map ); // MPEG-2_AAC_channel_configuration bs_write( s, 8, 0 ); // MPEG-2_AAC_additional_information (anybody use this?) } /* AC-3 Descriptor for DVB and Blu-Ray */ static void write_ac3_descriptor( ts_writer_t *w, bs_t *s, int e_ac3 ) { if( w->ts_type == TS_TYPE_BLU_RAY ) bs_write( s, 8, HDMV_AC3_DESCRIPTOR_TAG ); // descriptor_tag else if( e_ac3 ) bs_write( s, 8, DVB_EAC3_DESCRIPTOR_TAG ); // descriptor_tag else bs_write( s, 8, DVB_AC3_DESCRIPTOR_TAG ); // descriptor_tag bs_write( s, 8, 1 ); // descriptor_length /* does anything need these set? */ bs_write1( s, 0 ); // component_type_flag bs_write1( s, 0 ); // bsid_flag bs_write1( s, 0 ); // mainid_flag bs_write1( s, 0 ); // asvc_flag if( e_ac3 ) { bs_write1( s, 0 ); // mixinfoexists bs_write1( s, 0 ); // substream1_flag bs_write1( s, 0 ); // substream2_flag bs_write1( s, 0 ); // substream3_flag } else bs_write( s, 4, 0 ); // reserved } static void write_iso_lang_descriptor( bs_t *s, ts_int_stream_t *stream ) { bs_write( s, 8, ISO_693_LANGUAGE_DESCRIPTOR_TAG ); // descriptor_tag bs_write( s, 8, 0x4 ); // descriptor_length for( int i = 0; i < 3; i++ ) bs_write( s, 8, stream->lang_code[i] ); bs_write(s, 8, stream->audio_type ); // audio_type } /** Misc descriptors **/ static void write_opus_descriptor( bs_t *s, ts_int_stream_t *stream ) { 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) + 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) + (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 ****/ static void add_to_buffer( buffer_t *buffer ) { buffer->cur_buf += TS_PACKET_SIZE * 8; } static void drip_buffer( ts_writer_t *w, ts_int_program_t *program, int rx, buffer_t *buffer, double next_pcr ) { int iters; double offset; /* Although this uses floating point arithmetic, the values are backed by integers * Transport buffer fullness does not need to be exact */ double cur_pcr = get_pcr_double( w, 0 ); if( buffer->last_byte_removal_time == 0.0 ) { buffer->last_byte_removal_time = cur_pcr; buffer->cur_buf -= 8; } iters = floor( (next_pcr - buffer->last_byte_removal_time) / (8.0 / rx) ); buffer->cur_buf -= 8*iters; /* Avoid compounded error from floating point addition by making calculations relative to next_pcr */ offset = next_pcr - (buffer->last_byte_removal_time + 8.0 * iters / rx); buffer->last_byte_removal_time = next_pcr - offset; buffer->cur_buf = MAX( buffer->cur_buf, 0 ); } static int write_adaptation_field( ts_writer_t *w, bs_t *s, ts_int_program_t *program, ts_int_pes_t *pes, int write_pcr, int flags, int stuffing, int discontinuity ) { int private_data_flag, write_dvb_au, random_access, priority; int start = bs_pos( s ); uint8_t temp[512], temp2[256]; bs_t q, r; private_data_flag = write_dvb_au = random_access = priority = 0; if( pes && ( pes->data == pes->cur_pos ) ) { ts_int_stream_t *stream = pes->stream; random_access = pes->random_access; if( IS_VIDEO( stream ) ) { if( !write_pcr ) random_access = 0; if( stream->dvb_au ) private_data_flag = write_dvb_au = 1; } priority = pes->priority; pes->random_access = 0; /* don't write this flag again */ } /* initialise temporary bitstream */ bs_init( &q, temp, 256 ); if( flags ) { int64_t pcr = get_pcr_int( w, 7 ); /* 7 bytes until end of PCR field */ bs_write1( &q, discontinuity ); // discontinuity_indicator bs_write1( &q, random_access ); // random_access_indicator bs_write1( &q, priority ); // elementary_stream_priority_indicator bs_write1( &q, write_pcr ); // PCR_flag bs_write1( &q, 0 ); // OPCR_flag bs_write1( &q, 0 ); // splicing_point_flag bs_write1( &q, private_data_flag ); // transport_private_data_flag bs_write1( &q, 0 ); // adaptation_field_extension_flag if( write_pcr ) { uint64_t base, extension; int64_t mod = (int64_t)1 << 33; program->last_pcr = pcr; base = (pcr / 300) % mod; extension = pcr % 300; // program_clock_reference_base bs_write32( &q, base >> 1 ); bs_write1( &q, (base & 1) ); // reserved bs_write( &q, 6, 0x3f ); // program_clock_reference_extension bs_write( &q, 8, (extension >> 1) & 0xff ); bs_write1( &q, (extension & 1 ) ); } } if( private_data_flag ) { /* initialise another temporary bitstream */ bs_init( &r, temp2, 128 ); if( write_dvb_au ) write_dvb_au_information( &r, pes ); bs_flush ( &r ); bs_write( &q, 8, bs_pos( &r ) >> 3 ); // transport_private_data_length write_bytes( &q, temp2, bs_pos( &r ) >> 3 ); } for( int i = 0; i < stuffing; i++ ) bs_write( &q, 8, 0xff ); bs_flush( &q ); bs_write( s, 8, bs_pos( &q ) >> 3 ); // adaptation_field_length write_bytes( s, temp, bs_pos( &q ) >> 3 ); return (bs_pos( s ) - start) >> 3; } static int write_pcr_empty( ts_writer_t *w, ts_int_program_t *program, int first ) { bs_t *s = &w->out.bs; write_packet_header( w, s, 0, program->pcr_stream->pid, ADAPT_FIELD_ONLY, &program->pcr_stream->cc ); int stuffing = 184 - 6 - 2; /* pcr, flags and length */ write_adaptation_field( w, s, program, NULL, 1, 1, stuffing, first ); add_to_buffer( &program->pcr_stream->tb ); if( increase_pcr( w, 1, 0 ) < 0 ) return -1; return 0; } /**** PSI ****/ static int write_pat( ts_writer_t *w ) { int start; bs_t *s = &w->out.bs; write_packet_header( w, s, 1, PAT_PID, PAYLOAD_ONLY, &w->pat_cc ); bs_write( s, 8, 0 ); // pointer field start = bs_pos( s ); bs_write( s, 8, PAT_TID ); // table_id bs_write1( s, 1 ); // section_syntax_indicator bs_write1( s, 0 ); // '0' bs_write( s, 2, 0x03 ); // reserved` // FIXME when multiple programs are allowed do this properly int section_length = w->num_programs * 4 + w->network_pid * 4 + 9; bs_write( s, 12, section_length & 0x3ff ); bs_write( s, 16, w->ts_id & 0xffff ); // transport_stream_id bs_write( s, 2, 0x03 ); // reserved bs_write( s, 5, w->pat_version ); // version_number bs_write1( s, 1 ); // current_next_indicator bs_write( s, 8, 0 ); // section_number bs_write( s, 8, 0 ); // last_section_number if( w->network_pid ) { bs_write( s, 16, 0 ); // program_number bs_write( s, 3, 0x07 ); // reserved bs_write( s, 13, w->network_pid & 0x1fff ); // network_PID } for( int i = 0; i < w->num_programs; i++ ) { bs_write( s, 16, w->programs[i]->program_num & 0xffff ); // program_number bs_write( s, 3, 0x07 ); // reserved bs_write( s, 13, w->programs[i]->pmt.pid & 0x1fff ); // program_map_PID } bs_flush( s ); write_crc( s, start ); // -40 to include header and pointer field write_padding( s, start - 40 ); add_to_buffer( &w->tb ); if( increase_pcr( w, 1, 0 ) < 0 ) return -1; return 0; } static int eject_queued_pmt( ts_writer_t *w, ts_int_program_t *program, bs_t *s ) { uint8_t **temp; write_bytes( s, program->pmt_packets[0], TS_PACKET_SIZE ); if( program->num_queued_pmt > 1 ) 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*) ); program->pmt_packets = temp; program->num_queued_pmt--; add_to_buffer( &w->tb ); if( increase_pcr( w, 1, 0 ) < 0 ) return -1; return 0; }; static int write_pmt( ts_writer_t *w, ts_int_program_t *program ) { int start; bs_t *s = &w->out.bs; uint8_t pmt_buf[2048] = {0}, temp[2048] = {0}, temp1[2048] = {0}; bs_t o, p, q; int section_length; uint8_t **temp2; /* this should never happen */ if( program->num_queued_pmt ) return eject_queued_pmt( w, program, s ); start = bs_pos( s ); write_packet_header( w, s, 1, program->pmt.pid, PAYLOAD_ONLY, &program->pmt.cc ); bs_write( s, 8, 0 ); // pointer field bs_init( &o, pmt_buf, 2048 ); bs_write( &o, 8, PMT_TID ); // table_id = program_map_section bs_write1( &o, 1 ); // section_syntax_indicator bs_write1( &o, 0 ); // '0' bs_write( &o, 2, 0x3 ); // reserved bs_init( &p, temp, 2048 ); bs_write( &p, 16, program->program_num & 0xffff ); // program_number bs_write( &p, 2, 0x3 ); // reserved bs_write( &p, 5, program->pmt_version ); // version_number bs_write1( &p, 1 ); // current_next_indicator bs_write( &p, 8, 0 ); // section_number bs_write( &p, 8, 0 ); // last_section_number bs_write( &p, 3, 0x7 ); // reserved bs_write( &p, 13, program->pcr_stream[0].pid & 0x1fff ); // PCR PID bs_write( &p, 4, 0xf ); // reserved /* setup temporary bitstream context */ bs_init( &q, temp1, 2048 ); if( w->ts_type == TS_TYPE_ATSC ) { write_registration_descriptor( &q, REGISTRATION_DESCRIPTOR_TAG, 4, "GA94" ); write_smoothing_buffer_descriptor( &q, program ); } else if( w->ts_type == TS_TYPE_CABLELABS ) { write_registration_descriptor( &q, REGISTRATION_DESCRIPTOR_TAG, 4, "SCTE" ); if( program->is_3dtv ) write_cablelabs_3d_descriptor( &q ); } 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 ); bs_write( &p, 12, bs_pos( &q ) >> 3 ); // program_info_length write_bytes( &p, temp1, bs_pos( &q ) >> 3 ); for( int i = 0; i < program->num_streams; i++ ) { ts_int_stream_t *stream = program->streams[i]; bs_write( &p, 8, stream->stream_type & 0xff ); // stream_type bs_write( &p, 3, 0x7 ); // reserved bs_write( &p, 13, stream->pid & 0x1fff ); // elementary_PID bs_write( &p, 4, 0xf ); // reserved /* reset temporary bitstream context for streams loop */ bs_init( &q, temp1, 512 ); if( stream->stream_format != LIBMPEGTS_ANCILLARY_RDD11 && stream->stream_format != LIBMPEGTS_DATA_SCTE35 ) write_data_stream_alignment_descriptor( &q ); if( stream->dvb_au ) { if( w->ts_type == TS_TYPE_DVB ) write_adaptation_field_data_descriptor( &q, AU_INFORMATION_DATA_FIELD ); else if( w->ts_type == TS_TYPE_CABLELABS ) write_scte_adaptation_descriptor( &q ); } if( stream->write_lang_code ) write_iso_lang_descriptor( &q, stream ); if( stream->has_stream_identifier ) write_stream_identifier_descriptor( &q, stream->stream_identifier ); if( stream->stream_format == LIBMPEGTS_VIDEO_MPEG2 ) { if( w->ts_type == TS_TYPE_BLU_RAY ) write_hdmv_video_registration_descriptor( &q, stream ); } else if( stream->stream_format == LIBMPEGTS_VIDEO_AVC ) { write_avc_descriptor( &q, program, stream ); 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 ) { // TODO } else if( stream->stream_format == LIBMPEGTS_AUDIO_ADTS || stream->stream_format == LIBMPEGTS_AUDIO_LATM ) { /* strictly speaking in DVB only LATM is allowed for MPEG-4 AAC audio. ADTS is commonly used however */ if( !stream->aac_is_mpeg4 ) write_mpeg2_aac_descriptor( &q, stream ); else if( w->ts_type == TS_TYPE_DVB ) write_aac_descriptor( &q, stream ); } else if( stream->stream_format == LIBMPEGTS_AUDIO_AC3 ) { write_registration_descriptor( &q, REGISTRATION_DESCRIPTOR_TAG, 4, "AC-3" ); if( stream->atsc_ac3_ctx && ( w->ts_type == TS_TYPE_ATSC || w->ts_type == TS_TYPE_CABLELABS ) ) write_atsc_ac3_descriptor( &q, stream->atsc_ac3_ctx ); else write_ac3_descriptor( w, &q, 0 ); } else if( stream->stream_format == LIBMPEGTS_AUDIO_EAC3 || stream->stream_format == LIBMPEGTS_AUDIO_EAC3_SECONDARY ) write_ac3_descriptor( w, &q, 1 ); else if( stream->stream_format == LIBMPEGTS_AUDIO_DTS ) { // TODO } else if( stream->stream_format == LIBMPEGTS_AUDIO_302M ) 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 ) write_teletext_descriptor( &q, stream, 0 ); else if( stream->stream_format == LIBMPEGTS_DVB_VBI ) { write_vbi_descriptor( &q, stream ); if( stream->num_dvb_ttx ) write_teletext_descriptor( &q, stream, 1 ); } else if( stream->stream_format == LIBMPEGTS_ANCILLARY_RDD11 ) write_registration_descriptor( &q, REGISTRATION_DESCRIPTOR_TAG, 4, "LU-A" ); else if( stream->stream_format == LIBMPEGTS_ANCILLARY_2038 ) { 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 /* Optional descriptor(s) here */ bs_flush( &q ); bs_write( &p, 12, bs_pos( &q ) >> 3 ); // ES_info_length write_bytes( &p, temp1, bs_pos( &q ) >> 3 ); } /* section length includes crc */ section_length = (bs_pos( &p ) >> 3) + 4; bs_write( &o, 12, section_length & 0x3ff ); /* write main chunk into pmt array */ bs_flush( &p ); write_bytes( &o, temp, bs_pos( &p ) >> 3 ); /* take crc of the whole program map section */ bs_flush( &o ); write_crc( &o, 0 ); int length = bs_pos( &o ) >> 3; int bytes_left = TS_PACKET_SIZE - ((bs_pos( s ) - start) >> 3); bs_flush( &o ); write_bytes( s, pmt_buf, MIN( bytes_left, length ) ); bs_flush( s ); write_padding( s, start ); add_to_buffer( &w->tb ); if( increase_pcr( w, 1, 0 ) < 0 ) return -1; int pos = MIN( bytes_left, length ); length -= pos; bytes_left = 184; /* queue up pmt packets for spaced output */ while( length > 0 ) { bs_t z; temp2 = realloc( program->pmt_packets, (program->num_queued_pmt + 1) * sizeof(uint8_t*)); if( !temp2 ) { fprintf( stderr, "malloc failed" ); return -1; } program->pmt_packets = temp2; program->pmt_packets[program->num_queued_pmt] = malloc( TS_PACKET_SIZE ); if( !program->pmt_packets[program->num_queued_pmt] ) { fprintf( stderr, "malloc failed" ); return -1; } bs_init( &z, program->pmt_packets[program->num_queued_pmt], 188 ); write_packet_header( w, &z, 0, program->pmt.pid, PAYLOAD_ONLY, &program->pmt.cc ); write_bytes( &z, &pmt_buf[pos], MIN( bytes_left, length ) ); bs_flush( &z ); write_padding( &z, 0 ); pos += MIN( bytes_left, length ); length -= MIN( bytes_left, length ); program->num_queued_pmt++; } return 0; } 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 || !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; write_pat( w ); // FIXME handle failure write_pmt( w, program ); // FIXME handle failure } cur_pcr = get_pcr_int( w, 0 ); if( w->sdt && ( cur_pcr - w->last_sdt >= w->sdt_period * 27000LL || !w->last_sdt ) ) { w->last_sdt = cur_pcr; write_sdt( w ); } } /* DVB / Blu-Ray Service Information */ static int write_sit( ts_writer_t *w ) { int start; int len = 0; // FIXME bs_t *s = &w->out.bs; write_packet_header( w, s, 1, SIT_PID, PAYLOAD_ONLY, &w->sit->cc ); bs_write( s, 8, 0 ); // pointer field start = bs_pos( s ); bs_write( s, 8, SIT_TID ); // table_id bs_write1( s, 1 ); // section_syntax_indicator bs_write1( s, 1 ); // DVB_reserved_future_use bs_write( s, 2, 0x03 ); // ISO_reserved // FIXME do length properly bs_write( s, 12, 11 + len ); // section_length bs_write( s, 16, 0xffff ); // DVB_reserved_future_use bs_write( s, 2, 0x03 ); // ISO_reserved bs_write( s, 5, w->sit->version_number ); // version_number bs_write1( s, 1 ); // current_next_indicator bs_write( s, 8, 0 ); // section_number bs_write( s, 8, 0 ); // last_section_number bs_write( s, 4, 0x0f ); // DVB_reserved_for_future_use bs_write( s, 12, len ); // transmission_info_loop_length if( w->ts_type == TS_TYPE_BLU_RAY ) write_partial_ts_descriptor( w, s ); for( int i = 0; i < w->num_programs; i++ ) { bs_write( s, 16, w->programs[i]->program_num & 0xffff ); // service_id (equivalent to program_number) bs_write1( s, 1 ); // DVB_reserved_future_use bs_write( s, 3, 0x07 ); // running_status bs_write( s, 12, 0 ); // service_loop_length } bs_flush( s ); write_crc( s, start ); // -40 to include header and pointer field write_padding( s, start - 40 ); if( increase_pcr( w, 1, 0 ) < 0 ) return -1; return 0; } static void write_timestamp( bs_t *s, uint64_t timestamp ) { bs_write( s, 3, (timestamp >> 30) & 0x07 ); // timestamp [32..30] bs_write1( s, 1 ); // marker_bit bs_write( s, 8, (timestamp >> 22) & 0xff ); // timestamp [29..15] bs_write( s, 7, (timestamp >> 15) & 0x7f ); // timestamp [29..15] bs_write1( s, 1 ); // marker_bit bs_write( s, 8, (timestamp >> 7) & 0xff ); // timestamp [14..0] bs_write( s, 7, timestamp & 0x7f ); // timestamp [14..0] bs_write1( s, 1 ); // marker_bit } static int write_pes( ts_writer_t *w, ts_int_program_t *program, ts_frame_t *in_frame, ts_int_pes_t *out_pes ) { bs_t s, q; uint8_t temp[1024]; int header_size, total_size; int64_t mod = (int64_t)1 << 33; if( out_pes->dts > out_pes->pts ) fprintf( stderr, "\nError: DTS > PTS\n" ); bs_init( &s, out_pes->data, in_frame->size + 200 ); ts_int_stream_t *stream = out_pes->stream; bs_write( &s, 24, 1 ); // packet_start_code_prefix bs_write( &s, 8, stream->stream_id ); // stream_id /* Initialise temp buffer */ bs_init( &q, temp, 1024 ); bs_write( &q, 2, 0x2 ); // '10' bs_write( &q, 2, 0 ); // PES_scrambling_control bs_write1( &q, 0 ); // PES_priority bs_write1( &q, stream->stream_format != LIBMPEGTS_ANCILLARY_RDD11 ); // data_alignment_indicator bs_write1( &q, 1 ); // copyright bs_write1( &q, 1 ); // original_or_copy int same_timestamps = out_pes->dts == out_pes->pts; bs_write( &q, 2, 0x02 + !same_timestamps ); // pts_dts_flags bs_write1( &q, 0 ); // ESCR_flag bs_write1( &q, 0 ); // ES_rate_flag 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, stream->stream_format == LIBMPEGTS_VIDEO_DIRAC ); // PES_extension_flag 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) else bs_write( &q, 8, 0x0a ); // PES_header_data_length (PTS and DTS) bs_write( &q, 4, 0x02 + !same_timestamps ); // '0010' or '0011' write_timestamp( &q, out_pes->pts % mod ); // PTS if( !same_timestamps ) { bs_write( &q, 4, 1 ); // '0001' 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 ) { /* Total PES header is 45 bytes but don't count 6 bytes for the startcode, stream_id and length */ int num_stuffing = 45 - 6 - (bs_pos( &q ) >> 3); for( int i = 0; i < num_stuffing; i++ ) bs_write( &q, 8, 0xff ); } 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 || stream->stream_format == LIBMPEGTS_VIDEO_DIRAC ) bs_write( &s, 16, 0 ); // PES_packet_length else bs_write( &s, 16, total_size ); // PES_packet_length write_bytes( &s, temp, bs_pos( &q ) >> 3 ); header_size = bs_pos( &s ) >> 3; write_bytes( &s, in_frame->data, in_frame->size ); bs_flush( &s ); out_pes->size = out_pes->bytes_left = bs_pos( &s ) >> 3; out_pes->cur_pos = out_pes->data; return header_size; } static int write_null_packet( ts_writer_t *w ) { int start; int cc = 0; bs_t *s = &w->out.bs; start = bs_pos( s ); write_packet_header( w, s, 0, NULL_PID, PAYLOAD_ONLY, &cc ); write_padding( s, start ); if( increase_pcr( w, 1, 0 ) < 0 ) return -1; return 0; } static int check_bitstream( ts_writer_t *w ) { if( w->out.bs.p_end - w->out.bs.p < 18800 ) { bs_flush( &w->out.bs ); uint8_t *bs_bak = w->out.p_bitstream; w->out.i_bitstream += 100000; uint8_t *temp2 = realloc( w->out.p_bitstream, w->out.i_bitstream ); if( !temp2 ) { fprintf( stderr, "realloc failed\n" ); return -1; } w->out.p_bitstream = temp2; intptr_t delta = w->out.p_bitstream - bs_bak; w->out.bs.p_start += delta; w->out.bs.p += delta; w->out.bs.p_end = w->out.p_bitstream + w->out.i_bitstream; bs_realign( &w->out.bs ); } 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) ); if( !w ) { fprintf( stderr, "Malloc failed\n" ); return NULL; } return w; } int ts_setup_transport_stream( ts_writer_t *w, ts_main_t *params ) { // TODO check for PID collisions, add MPTS support if( params->ts_type < TS_TYPE_GENERIC || params->ts_type > TS_TYPE_BLU_RAY ) { fprintf( stderr, "Invalid Transport Stream type.\n" ); return -1; } if( params->num_programs > 1 ) { fprintf( stderr, "Multiple program transport streams are not yet supported.\n" ); return -1; } if( !params->cbr && params->num_programs > 1 ) { fprintf( stderr, "Multiple program transport streams cannot be variable bitrate.\n" ); return -1; } if( params->network_pid && ( params->network_pid < 0x10 || params->network_pid == 0x1fff ) ) { fprintf( stderr, "Invalid network_PID.\n" ); return -1; } if( !params->muxrate ) { fprintf( stderr, "Muxrate must be nonzero\n" ); return -1; } BOOLIFY( params->cbr ); BOOLIFY( params->legacy_constraints ); int internal_pcr_pid, video_stream; internal_pcr_pid = video_stream = 0; ts_int_program_t *cur_program = calloc( 1, sizeof(*cur_program) ); if( !cur_program ) { fprintf( stderr, "Malloc failed\n" ); return -1; } w->ts_type = params->ts_type; w->num_programs = 1; w->programs[0] = cur_program; cur_program->pmt.pid = params->programs[0].pmt_pid; cur_program->program_num = params->programs[0].program_num; cur_program->is_3dtv = params->programs[0].is_3dtv; cur_program->sb_leak_rate = params->programs[0].sb_leak_rate; cur_program->sb_size = params->programs[0].sb_size; cur_program->video_dts = -1; cur_program->sdt_ctx.service_type = params->programs[0].sdt.service_type; if( params->programs[0].sdt.service_name ) { cur_program->sdt_ctx.service_name = malloc( strlen( params->programs[0].sdt.service_name ) + 1 ); if( !cur_program->sdt_ctx.service_name ) { fprintf( stderr, "Malloc failed\n" ); return -1; } strcpy( cur_program->sdt_ctx.service_name, params->programs[0].sdt.service_name ); } if( params->programs[0].sdt.provider_name ) { cur_program->sdt_ctx.provider_name = malloc( strlen( params->programs[0].sdt.provider_name ) + 1 ); if( !cur_program->sdt_ctx.provider_name ) { fprintf( stderr, "Malloc failed\n" ); return -1; } strcpy( cur_program->sdt_ctx.provider_name, params->programs[0].sdt.provider_name ); } for( int i = 0; i < params->programs[0].num_streams; i++ ) { ts_stream_t *stream_in = ¶ms->programs[0].streams[i]; if( stream_in->stream_format == LIBMPEGTS_VIDEO_MPEG2 || stream_in->stream_format == LIBMPEGTS_VIDEO_AVC ) { if( !video_stream ) video_stream = 1; else { fprintf( stderr, "Multiple video streams not allowed\n" ); return -1; } } ts_int_stream_t *cur_stream = calloc( 1, sizeof(*cur_stream) ); if( !cur_stream ) { fprintf( stderr, "Malloc failed\n" ); return -1; } cur_stream->pid = stream_in->pid; cur_stream->stream_format = stream_in->stream_format; for( int j = 0; stream_type_table[j][0] != 0; j++ ) { 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 = stream_type_table[j][1]; break; } } if( !cur_stream->stream_type ) { fprintf( stderr, "Unsupported Stream Format\n" ); free( cur_stream ); return -1; } if( stream_in->write_lang_code ) { cur_stream->write_lang_code = 1; memcpy( cur_stream->lang_code, stream_in->lang_code, 4 ); } cur_stream->audio_type = stream_in->audio_type; if( cur_stream->pid == params->programs[0].pcr_pid ) { cur_program->pcr_stream = cur_stream; internal_pcr_pid = 1; } cur_stream->stream_id = stream_in->stream_id; /* Ignored in video streams */ cur_stream->max_frame_size = stream_in->audio_frame_size; if( stream_in->has_stream_identifier ) { cur_stream->has_stream_identifier = 1; cur_stream->stream_identifier = stream_in->stream_identifier & 0xff; } cur_stream->dvb_au = stream_in->dvb_au; cur_stream->dvb_au_frame_rate = stream_in->dvb_au_frame_rate; cur_stream->hdmv_frame_rate = stream_in->hdmv_frame_rate; cur_stream->hdmv_aspect_ratio = stream_in->hdmv_aspect_ratio; cur_stream->hdmv_video_format = stream_in->hdmv_video_format; 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 || cur_stream->stream_format == LIBMPEGTS_AUDIO_OPUS ) { /* use the defaults */ cur_stream->rx = MISC_AUDIO_RXN; cur_stream->mb.buf_size = MISC_AUDIO_BS; } else if( cur_stream->stream_format == LIBMPEGTS_AUDIO_AC3 || cur_stream->stream_format == LIBMPEGTS_AUDIO_EAC3 ) { 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++; } /* create separate PCR stream if necessary */ if( !internal_pcr_pid ) { ts_int_stream_t *pcr_stream = calloc( 1, sizeof(*pcr_stream) ); if( !pcr_stream ) return -1; pcr_stream->pid = params->programs[0].pcr_pid; cur_program->pcr_stream = pcr_stream; } update_ts_params( w, params ); w->network_pid = params->network_pid; 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->pcr_start = TS_START * TS_CLOCK; // FIXME realloc if necessary w->out.i_bitstream = w->ts_muxrate >> 3; w->out.p_bitstream = calloc( 1, w->out.i_bitstream ); if( !w->out.p_bitstream ) return -1; w->pcr_list_alloced = 50000; w->pcr_list = malloc( w->pcr_list_alloced * sizeof(int64_t) ); if( !w->pcr_list ) return -1; 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 ) { int bs_mux, bs_oh; int level_idx = -1; ts_int_stream_t *stream = find_stream( w, pid ); if( !stream ) { fprintf( stderr, "Invalid PID\n" ); return -1; } if( !( stream->stream_format == LIBMPEGTS_VIDEO_MPEG2 || stream->stream_format == LIBMPEGTS_VIDEO_AVC ) ) { fprintf( stderr, "PID is not an MPEG video stream\n" ); return -1; } if( stream->stream_format == LIBMPEGTS_VIDEO_MPEG2 ) { if( level < LIBMPEGTS_MPEG2_LEVEL_LOW || level > LIBMPEGTS_MPEG2_LEVEL_HIGHP ) { fprintf( stderr, "Invalid MPEG-2 Level\n" ); return -1; } if( profile < LIBMPEGTS_MPEG2_PROFILE_SIMPLE || profile > LIBMPEGTS_MPEG2_PROFILE_422 ) { fprintf( stderr, "Invalid MPEG-2 Profile\n" ); return -1; } for( int i = 0; mpeg2_levels[i].level != 0; i++ ) if( level == mpeg2_levels[i].level && profile == mpeg2_levels[i].profile ) { level_idx = i; break; } if( level_idx == -1 ) { fprintf( stderr, "Invalid MPEG-2 Level/Profile combination.\n" ); return -1; } } else if( stream->stream_format == LIBMPEGTS_VIDEO_AVC ) { for( int i = 0; avc_levels[i].level_idc != 0; i++ ) if( level == avc_levels[i].level_idc ) { level_idx = i; break; } if( level_idx == -1 ) { fprintf( stderr, "Invalid AVC Level\n" ); return -1; } if( profile < AVC_BASELINE || profile > AVC_CAVLC_444_INTRA ) { fprintf( stderr, "Invalid AVC Profile\n" ); return -1; } } if( !stream->mpegvideo_ctx ) { stream->mpegvideo_ctx = calloc( 1, sizeof(mpegvideo_stream_ctx_t) ); if( !stream->mpegvideo_ctx ) { fprintf( stderr, "Malloc failed\n" ); return -1; } } stream->mpegvideo_ctx->level = level; stream->mpegvideo_ctx->profile = profile; if( stream->stream_format == LIBMPEGTS_VIDEO_MPEG2 ) { bs_mux = 0.004 * mpeg2_levels[level_idx].bitrate; bs_oh = 1.0 * mpeg2_levels[level_idx].bitrate/750.0; stream->rx = 1.2 * mpeg2_levels[level_idx].bitrate; stream->eb.buf_size = vbv_bufsize; if( level == LIBMPEGTS_MPEG2_LEVEL_LOW || level == LIBMPEGTS_MPEG2_LEVEL_MAIN ) { stream->mb.buf_size = bs_mux + bs_oh + mpeg2_levels[level_idx].vbv - vbv_bufsize; stream->rbx = mpeg2_levels[level_idx].bitrate; } else { stream->mb.buf_size = bs_mux + bs_oh; stream->rbx = MIN( 1.05 * vbv_maxrate, mpeg2_levels[level_idx].bitrate ); } } else if( stream->stream_format == LIBMPEGTS_VIDEO_AVC ) { int factor = (float)nal_factor[stream->mpegvideo_ctx->profile] * 1.2; int bitrate = avc_levels[level_idx].bitrate * factor; bs_mux = 0.004 * MAX( bitrate, 2000000 ); bs_oh = 1.0 * MAX( bitrate, 2000000 )/750.0; stream->mb.buf_size = bs_mux + bs_oh; stream->eb.buf_size = avc_levels[level_idx].cpb * factor; stream->rx = bitrate; stream->rbx = bitrate; } return 0; } int ts_setup_mpeg2_aac_stream( ts_writer_t *w, int pid, int profile, int channel_map ) { if( w->ts_type == TS_TYPE_BLU_RAY ) { fprintf( stderr, "AAC not allowed in Blu-Ray\n" ); return -1; } ts_int_stream_t *stream = find_stream( w, pid ); if( !stream ) { fprintf( stderr, "Invalid PID\n" ); return -1; } if( profile < 0 || profile > 1 ) { fprintf( stderr, "Invalid AAC profile\n" ); return -1; } if( channel_map < 0 || channel_map > 7 ) { fprintf( stderr, "Invalid AAC channel map\n" ); return -1; } stream->aac_profile = profile; stream->aac_is_mpeg4 = 0; stream->aac_channel_map = channel_map; /* channel map index is pretty much correct so there's no point in writing a LUT */ int num_channels = channel_map == LIBMPEGTS_MPEG2_AAC_5_POINT_1_CHANNEL ? 5 : channel_map; for( int i = 0; aac_buffers[i].max_channels != 0; i++ ) { if( num_channels <= aac_buffers[i].max_channels ) { stream->rx = aac_buffers[i].rxn; stream->mb.buf_size = aac_buffers[i].bsn; } } return 0; } int ts_setup_mpeg4_aac_stream( ts_writer_t *w, int pid, int profile_and_level, int num_channels ) { if( w->ts_type == TS_TYPE_BLU_RAY ) { fprintf( stderr, "AAC not allowed in Blu-Ray\n" ); return -1; } if( profile_and_level <= 0 ) { fprintf( stderr, "Invalid Profile and Level value\n" ); return -1; } if( num_channels <= 0 || num_channels > 48 ) { fprintf( stderr, "Invalid number of channels\n" ); return -1; } ts_int_stream_t *stream = find_stream( w, pid ); if( !stream ) { fprintf( stderr, "Invalid PID\n" ); return -1; } stream->aac_profile = profile_and_level; stream->aac_is_mpeg4 = 1; for( int i = 0; aac_buffers[i].max_channels != 0; i++ ) { if( num_channels <= aac_buffers[i].max_channels ) { stream->rx = aac_buffers[i].rxn; stream->mb.buf_size = aac_buffers[i].bsn; } } return 0; }; int ts_setup_opus_stream( ts_writer_t *w, int pid, int channel_map ) { ts_int_stream_t *stream = find_stream( w, pid ); if( !stream ) { fprintf( stderr, "Invalid PID\n" ); return -1; } stream->opus_channel_map = channel_map; return 0; } int ts_setup_dvb_subtitles( ts_writer_t *w, int pid, int has_dds, int num_subtitles, ts_dvb_sub_t *subtitles ) { if( w->ts_type == TS_TYPE_BLU_RAY ) { fprintf( stderr, "DVB Subtitles not allowed in Blu-Ray\n" ); return -1; } ts_int_stream_t *stream = find_stream( w, pid ); if( !stream ) { fprintf( stderr, "Invalid PID\n" ); return -1; } if( !subtitles || !num_subtitles ) { fprintf( stderr, "Invalid Number of subtitles\n" ); return -1; } if( stream->dvb_sub_ctx ) free( stream->dvb_sub_ctx ); stream->dvb_sub_ctx = calloc( 1, num_subtitles * sizeof(ts_dvb_sub_t) ); if( !stream->dvb_sub_ctx ) return -1; stream->num_dvb_sub = num_subtitles; memcpy( stream->dvb_sub_ctx, subtitles, num_subtitles * sizeof(ts_dvb_sub_t) ); /* Display Definition Segment has different buffer sizes */ if( has_dds ) { stream->tb.buf_size = DVB_SUB_DDS_TB_SIZE; stream->rx = DVB_SUB_DDS_RXN; stream->mb.buf_size = DVB_SUB_DDS_MB_SIZE; } else { stream->rx = DVB_SUB_RXN; stream->mb.buf_size = DVB_SUB_MB_SIZE; } return 0; } int ts_setup_dvb_teletext( ts_writer_t *w, int pid, int num_teletexts, ts_dvb_ttx_t *teletexts ) { if( w->ts_type == TS_TYPE_BLU_RAY ) { fprintf( stderr, "Teletext not allowed in Blu-Ray\n" ); return -1; } ts_int_stream_t *stream = find_stream( w, pid ); if( !stream ) { fprintf( stderr, "Invalid PID\n" ); return -1; } if( !teletexts || !num_teletexts ) { fprintf( stderr, "Invalid Number of teletexts\n" ); return -1; } if( stream->dvb_ttx_ctx ) free( stream->dvb_ttx_ctx ); stream->dvb_ttx_ctx = calloc( 1, num_teletexts * sizeof(ts_dvb_ttx_t) ); if( !stream->dvb_ttx_ctx ) return -1; stream->num_dvb_ttx = num_teletexts; memcpy( stream->dvb_ttx_ctx, teletexts, num_teletexts * sizeof(ts_dvb_ttx_t) ); stream->tb.buf_size = TELETEXT_T_BS; stream->rx = TELETEXT_RXN; stream->mb.buf_size = TELETEXT_BTTX; return 0; } int ts_setup_dvb_vbi( ts_writer_t *w, int pid, int num_vbis, ts_dvb_vbi_t *vbis ) { if( w->ts_type == TS_TYPE_BLU_RAY ) { fprintf( stderr, "VBI not allowed in Blu-Ray\n" ); return -1; } ts_int_stream_t *stream = find_stream( w, pid ); if( !stream ) { fprintf( stderr, "Invalid PID\n" ); return -1; } if( !vbis || !num_vbis ) { fprintf( stderr, "Invalid Number of VBI services\n" ); return -1; } if( stream->dvb_vbi_ctx ) free( stream->dvb_vbi_ctx ); stream->dvb_vbi_ctx = calloc( 1, num_vbis * sizeof(ts_dvb_vbi_t) ); if( !stream->dvb_vbi_ctx ) return -1; stream->num_dvb_vbi = num_vbis; memcpy( stream->dvb_vbi_ctx, vbis, num_vbis * sizeof(ts_dvb_vbi_t) ); for( int i = 0; i < stream->num_dvb_vbi; i++ ) { stream->dvb_vbi_ctx[i].lines = calloc( 1, vbis[i].num_lines * sizeof(ts_dvb_vbi_line_t) ); if( !stream->dvb_vbi_ctx[i].lines ) { fprintf( stderr, "Malloc failed\n" ); for( int j = 0; i < stream->num_dvb_vbi; j++ ) { if( stream->dvb_vbi_ctx[j].lines ) free( stream->dvb_vbi_ctx[j].lines ); } free( stream->dvb_vbi_ctx ); return -1; } memcpy( stream->dvb_vbi_ctx[i].lines, vbis[i].lines, vbis[i].num_lines * sizeof(ts_dvb_vbi_line_t) ); } if( w->ts_type == TS_TYPE_CABLELABS || w->ts_type == TS_TYPE_ATSC ) { stream->rx = SCTE_VBI_RXN; stream->mb.buf_size = SCTE_VBI_MB_SIZE; } else { /* DVB-VBI uses teletext T-STD */ stream->tb.buf_size = TELETEXT_T_BS; stream->rx = TELETEXT_RXN; stream->mb.buf_size = TELETEXT_BTTX; } return 0; } int ts_setup_sdt( ts_writer_t *w ) { w->sdt = calloc( 1, sizeof(*w->sdt) ); if( !w->sdt ) { fprintf( stderr, "malloc failed\n" ); return -1; } return 0; } void ts_remove_sdt( ts_writer_t *w ) { free( w->sdt ); w->sdt = NULL; } int ts_write_frames( ts_writer_t *w, ts_frame_t *frames, int num_frames, uint8_t **out, int *len, int64_t **pcr_list ) { ts_int_program_t *program = w->programs[0]; ts_int_stream_t *stream; int initial_queued_pes = w->num_buffered_frames; ts_int_pes_t **queued_pes; ts_int_pes_t **new_pes; int stuffing, flags, pkt_bytes_left, write_pcr, write_adapt_field, adapt_field_len, pes_start, running; uint8_t temp[200]; bs_t q; bs_t *s = &w->out.bs; /* earliest arrival time that the pes packet can arrive */ int64_t cur_pcr = 0; w->num_pcrs = 0; bs_init( s, w->out.p_bitstream, w->out.i_bitstream ); if( num_frames < 0 ) { fprintf( stderr, "Invalid number of frames\n" ); return -1; } if( num_frames ) { ts_int_pes_t **tmp = realloc( w->buffered_frames, (w->num_buffered_frames+num_frames) * sizeof(w->buffered_frames[0]) ); if( !tmp ) { fprintf( stderr, "Malloc failed\n" ); return -1; } w->buffered_frames = tmp; new_pes = &w->buffered_frames[w->num_buffered_frames]; w->num_buffered_frames += num_frames; } queued_pes = w->buffered_frames; for( int i = 0; i < num_frames; i++ ) { stream = find_stream( w, frames[i].pid ); if( !stream ) { fprintf( stderr, "PID %i not found for frame %i\n", frames[i].pid, i ); return -1; } /* Codec specific parameters */ if( stream->stream_format == LIBMPEGTS_VIDEO_MPEG2 || stream->stream_format == LIBMPEGTS_VIDEO_AVC ) { if( !stream->mpegvideo_ctx ) { fprintf( stderr, "MPEG video stream needs additional information. Call ts_setup_mpegvideo_stream \n" ); return -1; } program->video_dts = frames[i].dts; } else if( stream->stream_format == LIBMPEGTS_DVB_SUB ) { if( !stream->dvb_sub_ctx ) { fprintf( stderr, "DVB subtitle stream needs additional information. Call ts_setup_dvb_subtitles \n" ); return -1; } } else if( stream->stream_format == LIBMPEGTS_DVB_TELETEXT ) { if( !stream->dvb_ttx_ctx ) { fprintf( stderr, "DVB Teletext stream needs additional information. Call ts_setup_dvb_teletext \n" ); return -1; } } else if( stream->stream_format == LIBMPEGTS_DVB_VBI ) { if( !stream->dvb_vbi_ctx ) { fprintf( stderr, "DVB VBI stream needs additional information. Call ts_setup_dvb_vbi \n" ); return -1; } } // TODO more new_pes[i] = calloc( 1, sizeof(ts_int_pes_t) ); if( !new_pes[i] ) { fprintf( stderr, "Malloc failed\n" ); return -1; } 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 * 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 * 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 ) new_pes[i]->initial_arrival_time = 0; /* FIXME: is this right? */ else if( stream->stream_format == LIBMPEGTS_DVB_VBI && ( w->ts_type == TS_TYPE_CABLELABS || w->ts_type == TS_TYPE_ATSC ) ) new_pes[i]->initial_arrival_time = (new_pes[i]->dts - 3003) * 300; /* SCTE-127 VBI is always in terms of NTSC */ else if( stream->stream_format == LIBMPEGTS_DVB_VBI ) new_pes[i]->initial_arrival_time = (new_pes[i]->dts - 3600) * 300; else new_pes[i]->initial_arrival_time = (new_pes[i]->dts - stream->max_frame_size) * 300; /* earliest that a frame can arrive */ if( !IS_VIDEO( stream ) ) new_pes[i]->final_arrival_time = new_pes[i]->dts * 300; /* probe the first normal looking ac3 frame if extra data is needed */ if( !stream->atsc_ac3_ctx && stream->stream_format == LIBMPEGTS_AUDIO_AC3 && ( w->ts_type == TS_TYPE_CABLELABS || w->ts_type == TS_TYPE_ATSC ) && frames[i].size > 100 && frames[i].data[0] == 0xb && frames[i].data[1] == 0x77 ) { stream->atsc_ac3_ctx = calloc( 1, sizeof(ts_atsc_ac3_info) ); if( !stream->atsc_ac3_ctx ) { fprintf( stderr, "Malloc failed\n" ); return -1; } parse_ac3_frame( stream->atsc_ac3_ctx, frames[i].data ); } /* 512 bytes is more than enough for pes overhead */ new_pes[i]->data = malloc( frames[i].size + 512 ); if( !new_pes[i]->data ) { fprintf( stderr, "Malloc failed\n" ); return -1; } /* 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( !w->lowlatency && !initial_queued_pes ) { out = NULL; *len = 0; *pcr_list = NULL; return 0; } write_pcr = 0; running = 1; if( !w->first_input ) { if( write_pcr_empty( w, program, 1 ) < 0 ) return -1; w->first_input = 1; } int video_found = 0, start = 0; int64_t pcr_stop = 0; cur_pcr = get_pcr_int( w, 0 ); if( w->lowlatency ) { /* Find the latest arrival time in the batch of packets delivered */ for( int i = 0; i < w->num_buffered_frames; i++ ) { 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 */ } } } while( cur_pcr < pcr_stop ) { //printf("\n pcr_stop %"PRIi64" cur_pcr %"PRIi64" \n", pcr_stop, cur_pcr ); ts_int_pes_t *pes = NULL; write_adapt_field = adapt_field_len = write_pcr = 0; pkt_bytes_left = 184; if( check_bitstream( w ) < 0 ) return -1; /* write any queued PMT packets */ if( program->num_queued_pmt && w->tb.cur_buf == 0.0 ) { eject_queued_pmt( w, program, s ); cur_pcr = get_pcr_int( w, 0 ); continue; } // FIXME at low bitrates this might need tweaking int need_pcr = check_pcr( w, program ); /* 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; if( (!pes || queued_pes[i]->dts < pes->dts) && !IS_VIDEO( stream ) ) { int total_packets = (queued_pes[i]->size + 183) / 184; int packets_left = (queued_pes[i]->bytes_left + 183) / 184; double drip_rate = (double)total_packets/ ( queued_pes[i]->final_arrival_time - queued_pes[i]->initial_arrival_time ); double remaining_drip_rate = (double)packets_left / (queued_pes[i]->final_arrival_time - cur_pcr); /* exclude video packets */ if( cur_pcr >= queued_pes[i]->initial_arrival_time && stream->tb.cur_buf == 0.0 && ( drip_rate < remaining_drip_rate || queued_pes[i]->final_arrival_time < cur_pcr ) ) { pes = queued_pes[i]; } } } } /* See if we can write a video packet if non-audio packets can't be written. */ if( !pes || need_pcr ) { for( int i = 0; i < w->num_buffered_frames; i++ ) { stream = queued_pes[i]->stream; if( IS_VIDEO( stream ) ) { int total_packets = (queued_pes[i]->size + 183) / 184; int packets_left = (queued_pes[i]->bytes_left + 183) / 184; double drip_rate = (double)total_packets / ( queued_pes[i]->final_arrival_time - queued_pes[i]->initial_arrival_time ); double remaining_drip_rate = (double)packets_left / (queued_pes[i]->final_arrival_time - cur_pcr ); /* Write a video packet anyway if we can put a PCR on it */ if( cur_pcr >= queued_pes[i]->initial_arrival_time && stream->tb.cur_buf == 0.0 && ( drip_rate < remaining_drip_rate || queued_pes[i]->final_arrival_time < cur_pcr || need_pcr ) ) { pes = queued_pes[i]; break; } } } } if( pes ) { stream = pes->stream; pes_start = pes->data == pes->cur_pos; /* flag if packet contains pes header */ if( pcr_stop < cur_pcr ) fprintf( stderr, "\n pcr_stop is less than pcr pid: %i pcr_stop: %"PRIi64" pcr: %"PRIi64" \n", pes->stream->pid, pcr_stop, cur_pcr ); // FIXME complain less if( pes->dts * 300 < cur_pcr ) fprintf( stderr, "\n dts is less than pcr pid: %i dts: %"PRIi64" pcr: %"PRIi64" \n", pes->stream->pid, pes->dts*300, cur_pcr ); bs_init( &q, temp, 150 ); if( program->pcr_stream == stream && pes_start ) write_adapt_field = 1; if( check_pcr( w, program ) ) { if( program->pcr_stream == stream ) { /* piggyback pcr on this stream */ write_adapt_field = write_pcr = 1; } else if( write_pcr_empty( w, program, 0 ) < 0 ) return -1; } #if 0 if( IS_VIDEO( stream ) && pes_start ) { printf("\n last pcr delta %"PRIi64" \n", get_pcr( w, 0 ) - stream->last_pkt_pcr ); } #endif stream->last_pkt_pcr = cur_pcr; if( write_adapt_field ) { adapt_field_len = write_adaptation_field( w, &q, program, pes, write_pcr, 1, 0, 0 ); pkt_bytes_left -= adapt_field_len; } /* DVB AU_Information is large so consider this case */ // FIXME consider cablelabs legacy if( !adapt_field_len && pes_start && stream->dvb_au ) { adapt_field_len = write_adaptation_field( w, &q, program, pes, 0, 1, 0, 0 ); pkt_bytes_left -= adapt_field_len; } // TODO CableLabs legacy if( pes->bytes_left >= pkt_bytes_left ) { 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, 1, 0, 0 ); write_bytes( s, pes->cur_pos, pkt_bytes_left ); pes->cur_pos += pkt_bytes_left; pes->bytes_left -= pkt_bytes_left; add_to_buffer( &stream->tb ); if( increase_pcr( w, 1, 0 ) < 0 ) return -1; } else { /* stuff the last packet with an oversized adaptation field */ stuffing = pkt_bytes_left - pes->bytes_left; flags = 1; /* special case where the adaptation_field_length byte is the stuffing */ // FIXME except for cablelabs legacy 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; } else if( stuffing && !adapt_field_len ) { adapt_field_len = 2; 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 ); 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 ) return -1; } if( pes->bytes_left == 0 ) { /* eject the current pes from the queue */ for( int i = 0; i < w->num_buffered_frames; i++ ) { if( queued_pes[i] == pes ) { w->num_buffered_frames--; memmove( &queued_pes[i], &queued_pes[i+1], (w->num_buffered_frames-i) * sizeof(queued_pes) ); break; } } free( pes->data ); free( pes ); } } else /* no packets can be written */ { if( check_pcr( w, program ) ) { if( write_pcr_empty( w, program, 0 ) < 0 ) return -1; } else if( w->cbr ) { if( write_null_packet( w ) < 0 ) return -1; } else if( increase_pcr( w, 1, 1 ) < 0 ) return -1; /* write imaginary packet in capped vbr mode */ } cur_pcr = get_pcr_int( w, 0 ); } bs_flush( s ); *out = w->out.p_bitstream; *len = bs_pos( s ) >> 3; *pcr_list = w->pcr_list; // TODO if it's the final packet write blu-ray overflows // TODO count bits here return 0; } int ts_delete_stream( ts_writer_t *w, int pid ) { // TODO return 0; } int ts_close_writer( ts_writer_t *w ) { for( int i = 0; i < w->num_programs; i++ ) { for( int j = 0; j < w->programs[i]->num_streams; j++ ) { // TODO free other stuff if( w->programs[i]->streams[j]->mpegvideo_ctx ) free( w->programs[i]->streams[j]->mpegvideo_ctx ); if( w->programs[i]->streams[j]->lpcm_ctx ) free( w->programs[i]->streams[j]->lpcm_ctx ); if( w->programs[i]->streams[j]->atsc_ac3_ctx ) free( w->programs[i]->streams[j]->atsc_ac3_ctx ); if( w->programs[i]->streams[j]->dvb_sub_ctx ) free( w->programs[i]->streams[j]->dvb_sub_ctx ); if( w->programs[i]->streams[j]->dvb_ttx_ctx ) free( w->programs[i]->streams[j]->dvb_ttx_ctx ); if( w->programs[i]->streams[j]->dvb_vbi_ctx ) free( w->programs[i]->streams[j]->dvb_vbi_ctx ); free( w->programs[i]->streams[j] ); } for( int j = 0; j < w->programs[i]->num_queued_pmt; j++ ) free( w->programs[i]->pmt_packets[j] ); if( w->programs[i]->pmt_packets ) free( w->programs[i]->pmt_packets ); if( w->programs[i]->sdt_ctx.service_name ) free( w->programs[i]->sdt_ctx.service_name ); if( w->programs[i]->sdt_ctx.provider_name ) free( w->programs[i]->sdt_ctx.provider_name ); free( w->programs[i] ); } for( int i = 0; i < w->num_buffered_frames; i++ ) { free( w->buffered_frames[i]->data ); free( w->buffered_frames[i] ); } free( w->buffered_frames ); if( w->sdt ) free( w->sdt ); if( w->pcr_list ) free( w->pcr_list ); if( w->out.p_bitstream ) free( w->out.p_bitstream ); free( w ); return 0; } void write_packet_header( ts_writer_t *w, bs_t *s, int start, int pid, int adapt_field, int *cc ) { if( w->ts_type == TS_TYPE_BLU_RAY ) { // tp_extra_header bs_write( s, 2, 0 ); // copy_permission_indicator bs_write( s, 30, 0 ); // arrival_time_stamp FIXME } bs_write( s, 8, 0x47 ); // sync byte bs_write1( s, 0 ); // transport_error_indicator bs_write1( s, start ); // payload_unit_start_indicator bs_write1( s, 0 ); // transport_priority (not usually used) bs_write( s, 5, (pid >> 8) & 0x1f ); // PID bs_write( s, 8, pid & 0xff ); // PID bs_write( s, 2, 0 ); // transport_scrambling_control bs_write( s, 2, adapt_field & 0x03 ); if( adapt_field == ADAPT_FIELD_ONLY ) bs_write( s, 4, (*cc - 1) & 0xf ); // continuity counter else bs_write( s, 4, (*cc)++ & 0xf ); // continuity counter } int write_padding( bs_t *s, int start ) { bs_flush( s ); uint8_t *p_start = s->p_start; int padding_bytes = TS_PACKET_SIZE - (bs_pos( s ) - start) / 8; memset( s->p, 0xff, padding_bytes ); s->p += padding_bytes; bs_init( s, s->p, s->p_end - s->p ); s->p_start = p_start; return padding_bytes; } void write_bytes( bs_t *s, uint8_t *bytes, int length ) { bs_flush( s ); uint8_t *p_start = s->p_start; memcpy( s->p, bytes, length ); s->p += length; bs_init( s, s->p, s->p_end - s->p ); s->p_start = p_start; } int increase_pcr( ts_writer_t *w, int num_packets, int imaginary ) { int64_t *temp; int64_t pcr; // TODO do this for all programs ts_int_program_t *program = w->programs[0]; 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++ ) { /* 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; if( !imaginary ) { if( w->num_pcrs >= w->pcr_list_alloced ) { temp = realloc( w->pcr_list, w->pcr_list_alloced * 2 * sizeof(int64_t) ); if( !temp ) return -1; w->pcr_list_alloced <<= 1; w->pcr_list = temp; } pcr = get_pcr_int( w, 0 ); w->pcr_list[w->num_pcrs++] = pcr; } return 0; } void write_crc( bs_t *s, int start ) { uint8_t *p_start = s->p_start; int pos = (bs_pos( s ) - start) >> 3; uint32_t crc = crc_32( s->p - pos, pos ); bs_init( s, s->p, s->p_end - s->p ); s->p_start = p_start; bs_write32( s, crc ); } ts_int_stream_t *find_stream( ts_writer_t *w, int pid ) { for( int i = 0; i < w->programs[0]->num_streams; i++ ) { if( pid == w->programs[0]->streams[i]->pid ) return w->programs[0]->streams[i]; } return NULL; }