mirror of
				https://github.com/ossrs/srs.git
				synced 2025-03-09 15:49:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2256 lines
		
	
	
	
		
			62 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2256 lines
		
	
	
	
		
			62 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //
 | ||
| // Copyright (c) 2013-2021 Winlin
 | ||
| //
 | ||
| // SPDX-License-Identifier: MIT
 | ||
| //
 | ||
| /**
 | ||
| g++ -o ts_info ts_info.cc -g -O0 -ansi
 | ||
| */
 | ||
| #if 1
 | ||
| // for int64_t print using PRId64 format.
 | ||
| #ifndef __STDC_FORMAT_MACROS
 | ||
|     #define __STDC_FORMAT_MACROS
 | ||
| #endif
 | ||
| #include <inttypes.h>
 | ||
| 
 | ||
| #include <sys/types.h>
 | ||
| #include <sys/stat.h>
 | ||
| #include <fcntl.h>
 | ||
| #include <unistd.h>
 | ||
| #include <stdio.h>
 | ||
| #include <stdlib.h>
 | ||
| #include <string.h>
 | ||
| #include <assert.h>
 | ||
| 
 | ||
| #include <vector>
 | ||
| #include <map>
 | ||
| 
 | ||
| #define trace(msg, ...) printf(msg"\n", ##__VA_ARGS__);
 | ||
| #define srs_freep(p) delete p; p = NULL
 | ||
| #define srs_assert(p) assert(p)
 | ||
| #define srs_min(a, b) ((a)<(b)? (a):(b))
 | ||
| 
 | ||
| #endif
 | ||
| /**
 | ||
| ISO/IEC 13818-1:2000(E)
 | ||
| Introduction
 | ||
|     Intro. 1 Transport Stream
 | ||
|     Intro. 2 Program Stream
 | ||
|     Intro. 4 Packetized Elementary Stream
 | ||
| SECTION 2 – TECHNICAL ELEMENTS
 | ||
|     2.4 Transport Stream bitstream requirements
 | ||
|         2.4.1 Transport Stream coding structure and parameters
 | ||
|         2.4.2 Transport Stream system target decoder
 | ||
|         2.4.3 Specification of the Transport Stream syntax and semantics
 | ||
|             2.4.3.1 Transport Stream
 | ||
|             2.4.3.2 Transport Stream packet layer
 | ||
|             2.4.3.3 Semantic definition of fields in Transport Stream packet layer
 | ||
|             2.4.3.5 Semantic definition of fields in adaptation field
 | ||
|             2.4.3.6 PES packet
 | ||
|             2.4.3.7 Semantic definition of fields in PES packet
 | ||
|         2.4.4 Program specific information
 | ||
|             2.4.4.5 Semantic definition of fields in program association section
 | ||
|             2.4.4.6 Conditional access Table
 | ||
|     2.5 Program Stream bitstream requirements
 | ||
|     2.6 Program and program element descriptors
 | ||
|     2.7 Restrictions on the multiplexed stream semantics
 | ||
| Annex A CRC Decoder Model
 | ||
| */
 | ||
| #if 1
 | ||
| // Transport Stream packets are 188 bytes in length.
 | ||
| #define TS_PACKET_SIZE          188
 | ||
| 
 | ||
| enum TSPidTable
 | ||
| {
 | ||
|     // Program Association Table(see Table 2-25).
 | ||
|     TSPidTablePAT    = 0x00,
 | ||
|     // Conditional Access Table (see Table 2-27).
 | ||
|     TSPidTableCAT    = 0x01,
 | ||
|     // Transport Stream Description Table
 | ||
|     TSPidTableTSDT    = 0x02,
 | ||
|     // null packets (see Table 2-3)
 | ||
|     TSPidTableNULL    = 0x01FFF,
 | ||
| };
 | ||
| 
 | ||
| /*adaptation_field_control*/
 | ||
| /**
 | ||
| * Table 2-5 – Adaptation field control values, page 38.
 | ||
| */
 | ||
| enum TSAdaptionType
 | ||
| {
 | ||
|     // Reserved for future use by ISO/IEC
 | ||
|     TSAdaptionTypeReserved        = 0x00,
 | ||
|     // No adaptation_field, payload only
 | ||
|     TSAdaptionTypePayloadOnly     = 0x01,
 | ||
|     // Adaptation_field only, no payload
 | ||
|     TSAdaptionTypeAdaptionOnly     = 0x02,
 | ||
|     // Adaptation_field followed by payload
 | ||
|     TSAdaptionTypeBoth            = 0x03,
 | ||
| };
 | ||
| #endif
 | ||
| 
 | ||
| // Table 2-29 – Stream type assignments. page 66.
 | ||
| enum TSStreamType
 | ||
| {
 | ||
|     // ITU-T | ISO/IEC Reserved
 | ||
|     TSStreamTypeReserved            = 0x00,
 | ||
|     /*defined by ffmpeg*/
 | ||
|     TSStreamTypeVideoMpeg1             = 0x01,
 | ||
|     TSStreamTypeVideoMpeg2             = 0x02,
 | ||
|     TSStreamTypeAudioMpeg1             = 0x03,
 | ||
|     TSStreamTypeAudioMpeg2             = 0x04,
 | ||
|     TSStreamTypePrivateSection         = 0x05,
 | ||
|     TSStreamTypePrivateData         = 0x06,
 | ||
|     TSStreamTypeAudioAAC             = 0x0f,
 | ||
|     TSStreamTypeVideoMpeg4             = 0x10,
 | ||
|     TSStreamTypeVideoH264             = 0x1b,
 | ||
|     TSStreamTypeAudioAC3             = 0x81,
 | ||
|     TSStreamTypeAudioDTS             = 0x8a,
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
| * the actually parsed type.
 | ||
| */
 | ||
| enum TSPidType 
 | ||
| {
 | ||
|     TSPidTypeReserved = 0, // TSPidTypeReserved, nothing parsed, used reserved.
 | ||
|     
 | ||
|     TSPidTypePAT, // Program associtate table
 | ||
|     TSPidTypePMT, // Program map table.
 | ||
|     
 | ||
|     TSPidTypeVideo, // only for H264 video
 | ||
|     TSPidTypeAudio, // only for AAC audio
 | ||
| };
 | ||
| 
 | ||
| // forward declares.
 | ||
| class TSHeader;
 | ||
| class TSAdaptionField;
 | ||
| class TSPayload;
 | ||
| class TSPayloadReserved;
 | ||
| class TSPayloadPAT;
 | ||
| class TSPayloadPMT;
 | ||
| class TSPayloadPES;
 | ||
| class TSContext;
 | ||
| class TSMessage;
 | ||
| 
 | ||
| // TSPacket declares.
 | ||
| class TSPacket
 | ||
| {
 | ||
| public:
 | ||
|     TSHeader* header;
 | ||
|     TSAdaptionField* adaption_field;
 | ||
|     TSPayload* payload;
 | ||
|     
 | ||
|     TSPacket();
 | ||
|     virtual ~TSPacket();
 | ||
|     int demux(TSContext* ctx, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg);
 | ||
|     int finish();
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
| * 2.4.3.2 Transport Stream packet layer. page 36.
 | ||
| */
 | ||
| class TSHeader 
 | ||
| {
 | ||
| public:
 | ||
|     // 1B
 | ||
|     int8_t sync_byte; //8bits
 | ||
|     // 2B
 | ||
|     int8_t transport_error_indicator; //1bit
 | ||
|     int8_t payload_unit_start_indicator; //1bit
 | ||
|     int8_t transport_priority; //1bit
 | ||
|     TSPidTable pid; //13bits
 | ||
|     // 1B
 | ||
|     int8_t transport_scrambling_control; //2bits
 | ||
|     TSAdaptionType adaption_field_control; //2bits
 | ||
|     u_int8_t continuity_counter; //4bits
 | ||
|     
 | ||
|     TSHeader();
 | ||
|     virtual ~TSHeader();
 | ||
|     int get_size();
 | ||
|     int demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg);
 | ||
| };
 | ||
| 
 | ||
| // variant ts packet adation field. page 40.
 | ||
| class TSAdaptionField 
 | ||
| {
 | ||
| public:
 | ||
|     // 1B
 | ||
|     u_int8_t adaption_field_length; //8bits
 | ||
|     // 1B
 | ||
|     int8_t discontinuity_indicator; //1bit
 | ||
|     int8_t random_access_indicator; //1bit
 | ||
|     int8_t elementary_stream_priority_indicator; //1bit
 | ||
|     int8_t PCR_flag; //1bit
 | ||
|     int8_t OPCR_flag; //1bit
 | ||
|     int8_t splicing_point_flag; //1bit
 | ||
|     int8_t transport_private_data_flag; //1bit
 | ||
|     int8_t adaptation_field_extension_flag; //1bit
 | ||
|     
 | ||
|     // if PCR_flag, 6B
 | ||
|     int64_t program_clock_reference_base; //33bits
 | ||
|     //6bits reserved.
 | ||
|     int16_t program_clock_reference_extension; //9bits
 | ||
|     
 | ||
|     // if OPCR_flag, 6B
 | ||
|     int64_t original_program_clock_reference_base; //33bits
 | ||
|     //6bits reserved.
 | ||
|     int16_t original_program_clock_reference_extension; //9bits
 | ||
|     
 | ||
|     // if splicing_point_flag, 1B
 | ||
|     int8_t splice_countdown; //8bits
 | ||
|     
 | ||
|     // if transport_private_data_flag, 1+p[0] B
 | ||
|     u_int8_t transport_private_data_length; //8bits
 | ||
|     char* transport_private_data; //[transport_private_data_length]bytes
 | ||
|     
 | ||
|     // if adaptation_field_extension_flag, 2+x bytes
 | ||
|     u_int8_t adaptation_field_extension_length; //8bits
 | ||
|     int8_t ltw_flag; //1bit
 | ||
|     int8_t piecewise_rate_flag; //1bit
 | ||
|     int8_t seamless_splice_flag; //1bit
 | ||
|     //5bits reserved
 | ||
|     // if ltw_flag, 2B
 | ||
|     int8_t ltw_valid_flag; //1bit
 | ||
|     int16_t ltw_offset; //15bits
 | ||
|     // if piecewise_rate_flag, 3B
 | ||
|     //2bits reserved
 | ||
|     int32_t piecewise_rate; //22bits
 | ||
|     // if seamless_splice_flag, 5B
 | ||
|     int8_t splice_type; //4bits
 | ||
|     int8_t DTS_next_AU0; //3bits
 | ||
|     int8_t marker_bit0; //1bit
 | ||
|     int16_t DTS_next_AU1; //15bits
 | ||
|     int8_t marker_bit1; //1bit
 | ||
|     int16_t DTS_next_AU2; //15bits
 | ||
|     int8_t marker_bit2; //1bit
 | ||
|     // left bytes.
 | ||
|     char* af_ext_reserved;
 | ||
|     
 | ||
|     // left bytes.
 | ||
|     char* af_reserved;
 | ||
|     
 | ||
|     // user defined total adaption field size.
 | ||
|     int __field_size;
 | ||
|     // logic pcr/original_pcr
 | ||
|     int64_t pcr;
 | ||
|     int64_t original_pcr;
 | ||
|     
 | ||
|     TSAdaptionField();
 | ||
|     virtual ~TSAdaptionField();
 | ||
|     int get_size();
 | ||
|     int demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg);
 | ||
| };
 | ||
| 
 | ||
| // variant ts packet payload. 
 | ||
| // PES packet or PSI table.
 | ||
| // TSPayloadPAT: page 61.
 | ||
| class TSPayload 
 | ||
| {
 | ||
| public:
 | ||
|     /**
 | ||
|     * the size of payload(payload plush the 1byte pointer_field).
 | ||
|     */
 | ||
|     int size;
 | ||
|     int pointer_field_size;
 | ||
|     
 | ||
|     TSPidType type;
 | ||
|     
 | ||
|     /**
 | ||
|     * 2.4.4.2 Semantics definition of fields in pointer syntax
 | ||
|     */
 | ||
|     u_int8_t pointer_field;
 | ||
|     
 | ||
|     TSPayloadReserved* reserved;
 | ||
|     TSPayloadPAT* pat;
 | ||
|     TSPayloadPMT* pmt;
 | ||
|     TSPayloadPES* pes;
 | ||
|     
 | ||
|     /**
 | ||
|     * 2.4.3.6 PES packet. page 49.
 | ||
|     */
 | ||
|     
 | ||
|     TSPayload();
 | ||
|     virtual ~TSPayload();;
 | ||
|     void read_pointer_field(TSPacket* pkt, u_int8_t*& p);
 | ||
|     int demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg);
 | ||
| };
 | ||
| 
 | ||
| 
 | ||
| /**
 | ||
| * 2.4.4.3 Program association Table. page 61.
 | ||
| */
 | ||
| class TSPayloadPAT 
 | ||
| {
 | ||
| public:
 | ||
|     // 1B
 | ||
|     u_int8_t table_id; //8bits
 | ||
|     
 | ||
|     // 2B
 | ||
|     int8_t section_syntax_indicator; //1bit
 | ||
|     int8_t const0_value; //1bit
 | ||
|     // 2bits reserved. 
 | ||
|     u_int16_t section_length; //12bits
 | ||
|     
 | ||
|     // 2B
 | ||
|     u_int16_t transport_stream_id; //16bits
 | ||
|     
 | ||
|     // 1B
 | ||
|     // 2bits reerverd.
 | ||
|     int8_t version_number; //5bits
 | ||
|     int8_t current_next_indicator; //1bit
 | ||
|     
 | ||
|     // 1B
 | ||
|     u_int8_t section_number; //8bits
 | ||
|     
 | ||
|     // 1B
 | ||
|     u_int8_t last_section_number; //8bits
 | ||
|     
 | ||
|     // multiple 4B program data.
 | ||
|     // program_number 16bits
 | ||
|     // reserved 2bits
 | ||
|     // 13bits data: 0x1FFF
 | ||
|     // if program_number program_map_PID 13bits
 | ||
|     // else network_PID 13bytes.
 | ||
|     int program_size;
 | ||
|     int32_t* programs; //32bits
 | ||
|     
 | ||
|     // 4B
 | ||
|     int32_t CRC_32; //32bits
 | ||
|     
 | ||
|     TSPayloadPAT();
 | ||
|     virtual ~TSPayloadPAT();
 | ||
|     int demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg);
 | ||
| };
 | ||
| 
 | ||
| class TSPMTESInfo
 | ||
| {
 | ||
| public:
 | ||
|     // 1B
 | ||
|     u_int8_t stream_type; //8bits
 | ||
|     
 | ||
|     // 2B
 | ||
|     // 3bits reserved
 | ||
|     int16_t elementary_PID; //13bits
 | ||
|     
 | ||
|     // 2B
 | ||
|     // 4bits reserved
 | ||
|     int16_t ES_info_length; //12bits
 | ||
|     
 | ||
|     char* ES_info; //[ES_info_length] bytes.
 | ||
|     
 | ||
|     TSPMTESInfo();
 | ||
|     virtual ~TSPMTESInfo();
 | ||
| };
 | ||
| 
 | ||
| 
 | ||
| /**
 | ||
| * 2.4.4.8 Program Map Table. page 64.
 | ||
| */
 | ||
| class TSPayloadPMT
 | ||
| {
 | ||
| public:
 | ||
|     // 1B
 | ||
|     u_int8_t table_id; //8bits
 | ||
|     
 | ||
|     // 2B
 | ||
|     int8_t section_syntax_indicator; //1bit
 | ||
|     int8_t const0_value; //1bit
 | ||
|     // 2bits reserved. 
 | ||
|     u_int16_t section_length; //12bits
 | ||
|     
 | ||
|     // 2B
 | ||
|     u_int16_t program_number; //16bits
 | ||
|     
 | ||
|     // 1B
 | ||
|     // 2bits reerverd.
 | ||
|     int8_t version_number; //5bits
 | ||
|     int8_t current_next_indicator; //1bit
 | ||
|     
 | ||
|     // 1B
 | ||
|     u_int8_t section_number; //8bits
 | ||
|     
 | ||
|     // 1B
 | ||
|     u_int8_t last_section_number; //8bits
 | ||
|     
 | ||
|     // 2B
 | ||
|     // 2bits reserved.
 | ||
|     int16_t PCR_PID; //16bits
 | ||
|     
 | ||
|     // 2B
 | ||
|     // 4bits reserved.
 | ||
|     int16_t program_info_length; //12bits
 | ||
|     char* program_info_desc; //[program_info_length]bytes
 | ||
|     
 | ||
|     // array of TSPMTESInfo.
 | ||
|     std::vector<TSPMTESInfo*> ES_info; 
 | ||
|     
 | ||
|     // 4B
 | ||
|     int32_t CRC_32; //32bits
 | ||
|     
 | ||
|     TSPayloadPMT();
 | ||
|     virtual ~TSPayloadPMT();
 | ||
|     TSPMTESInfo* at(int index);
 | ||
|     int demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg);
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
| * Table 2-18 – Stream_id assignments. page 52.
 | ||
| */
 | ||
| enum TSPESStreamId
 | ||
| {
 | ||
|     PES_program_stream_map         = 0xbc, // 0b10111100
 | ||
|     PES_private_stream_1         = 0xbd, // 0b10111101
 | ||
|     PES_padding_stream            = 0xbe, // 0b10111110
 | ||
|     PES_private_stream_2        = 0xbf, // 0b10111111
 | ||
|     
 | ||
|     // 110x xxxx
 | ||
|     // ISO/IEC 13818-3 or ISO/IEC 11172-3 or ISO/IEC 13818-7 or ISO/IEC
 | ||
|     // 14496-3 audio stream number x xxxx
 | ||
|     // (stream_id>>5)&0x07 == PES_audio_prefix
 | ||
|     PES_audio_prefix            = 0x06, // 0b110
 | ||
|     
 | ||
|     // 1110 xxxx
 | ||
|     // ITU-T Rec. H.262 | ISO/IEC 13818-2 or ISO/IEC 11172-2 or ISO/IEC
 | ||
|     // 14496-2 video stream number xxxx
 | ||
|     // (stream_id>>4)&0x0f == PES_audio_prefix
 | ||
|     PES_video_prefix            = 0x0e, // 0b1110
 | ||
|     
 | ||
|     PES_ECM_stream                = 0xf0, // 0b11110000
 | ||
|     PES_EMM_stream                = 0xf1, // 0b11110001
 | ||
|     PES_DSMCC_stream            = 0xf2, // 0b11110010
 | ||
|     PES_13522_stream            = 0xf3, // 0b11110011
 | ||
|     PES_H_222_1_type_A            = 0xf4, // 0b11110100
 | ||
|     PES_H_222_1_type_B            = 0xf5, // 0b11110101
 | ||
|     PES_H_222_1_type_C            = 0xf6, // 0b11110110
 | ||
|     PES_H_222_1_type_D            = 0xf7, // 0b11110111
 | ||
|     PES_H_222_1_type_E            = 0xf8, // 0b11111000
 | ||
|     PES_ancillary_stream        = 0xf9, // 0b11111001
 | ||
|     PES_SL_packetized_stream    = 0xfa, // 0b11111010
 | ||
|     PES_FlexMux_stream            = 0xfb, // 0b11111011
 | ||
|     // reserved data stream
 | ||
|     // 1111 1100 … 1111 1110
 | ||
|     PES_program_stream_directory= 0xff, // 0b11111111
 | ||
| };
 | ||
| 
 | ||
| 
 | ||
| /**
 | ||
| * 2.4.3.7 Semantic definition of fields in PES packet. page 49.
 | ||
| */
 | ||
| class TSPayloadPES
 | ||
| {
 | ||
| public:
 | ||
|     // 3B
 | ||
|     int32_t packet_start_code_prefix; //24bits
 | ||
|     // 1B
 | ||
|     u_int8_t stream_id; //8bits
 | ||
|     // 2B
 | ||
|     u_int16_t PES_packet_length; //16bits
 | ||
| 
 | ||
|     // 1B
 | ||
|     // 2bits const '10'
 | ||
|     int8_t PES_scrambling_control; //2bits
 | ||
|     int8_t PES_priority; //1bit
 | ||
|     int8_t data_alignment_indicator; //1bit
 | ||
|     int8_t copyright; //1bit
 | ||
|     int8_t original_or_copy; //1bit
 | ||
|     
 | ||
|     // 1B
 | ||
|     int8_t PTS_DTS_flags; //2bits
 | ||
|     int8_t ESCR_flag; //1bit
 | ||
|     int8_t ES_rate_flag; //1bit
 | ||
|     int8_t DSM_trick_mode_flag; //1bit
 | ||
|     int8_t additional_copy_info_flag; //1bit
 | ||
|     int8_t PES_CRC_flag; //1bit
 | ||
|     int8_t PES_extension_flag; //1bit
 | ||
|     
 | ||
|     // 1B
 | ||
|     u_int8_t PES_header_data_length; //8bits
 | ||
|     
 | ||
|     int64_t pts; // 33bits
 | ||
|     int64_t dts; // 33bits
 | ||
|     
 | ||
|     int16_t ESCR_extension; //9bits
 | ||
|     int64_t ESCR_base; //33bits
 | ||
|     int32_t ES_rate; //22bits
 | ||
|     
 | ||
|     int8_t trick_mode_control; //3bits
 | ||
|     int8_t trick_mode_value; //5bits
 | ||
|     
 | ||
|     int8_t additional_copy_info; //7bits
 | ||
|     int16_t previous_PES_packet_CRC; //16bits
 | ||
|     
 | ||
|     int8_t PES_private_data_flag; //1bit
 | ||
|     int8_t pack_header_field_flag; //1bit
 | ||
|     int8_t program_packet_sequence_counter_flag; //1bit
 | ||
|     int8_t P_STD_buffer_flag; //1bit
 | ||
|     // reserved 3bits
 | ||
|     int8_t PES_extension_flag_2; //1bit
 | ||
|     
 | ||
|     // 16B
 | ||
|     char* PES_private_data; //128bits
 | ||
|     
 | ||
|     int8_t pack_field_length; //8bits
 | ||
|     char* pack_field; //[pack_field_length] bytes
 | ||
|                 
 | ||
|     int8_t program_packet_sequence_counter; //7bits
 | ||
|     int8_t MPEG1_MPEG2_identifier; //1bit
 | ||
|     int8_t original_stuff_length; //6bits
 | ||
|                 
 | ||
|     int8_t P_STD_buffer_scale; //1bit
 | ||
|     int16_t P_STD_buffer_size; //13bits
 | ||
|     
 | ||
|     int8_t PES_extension_field_length; //7bits
 | ||
|     char* PES_extension_field; //[PES_extension_field_length] bytes
 | ||
|     
 | ||
|     int stuffing_size;
 | ||
|     char* stuffing_byte;
 | ||
|     
 | ||
|     TSPayloadPES();
 | ||
|     virtual ~TSPayloadPES();
 | ||
|     int64_t decode_33bits_int(u_int8_t*& p, int64_t& temp);
 | ||
|     int64_t decode_33bits_int(int64_t& temp);
 | ||
|     int demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg);
 | ||
| };
 | ||
| 
 | ||
| class TSPayloadReserved 
 | ||
| {
 | ||
| public:
 | ||
|     int size;
 | ||
|     char* bytes;
 | ||
|     
 | ||
|     TSPayloadReserved();
 | ||
|     virtual ~TSPayloadReserved();
 | ||
|     int demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg);
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
| * logic ts pid.
 | ||
| */
 | ||
| struct TSPid
 | ||
| {
 | ||
|     TSPidType type;
 | ||
|     // page 66.
 | ||
|     TSStreamType stream_type;
 | ||
|     // page 36
 | ||
|     TSPidTable pid;
 | ||
|     // page 36
 | ||
|     u_int8_t continuity_counter;
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
| * logic audio/video message
 | ||
| */
 | ||
| class TSMessage
 | ||
| {
 | ||
| public:
 | ||
|     // 2.4.3.2 Transport Stream packet layer. page 36
 | ||
|     // the pid of PES packet.
 | ||
|     TSPidTable pid;
 | ||
|     
 | ||
|     // the type of pid.
 | ||
|     TSPidType type;
 | ||
|     // the type of stream, codec type.
 | ||
|     TSStreamType stream_type;
 | ||
|     // page 36
 | ||
|     u_int8_t continuity_counter;
 | ||
|     
 | ||
|     // 2.4.3.7 Semantic definition of fields in PES packet. page 49
 | ||
|     // PES packet header size plus data size.
 | ||
|     u_int16_t PES_packet_length; //16bits
 | ||
| 
 | ||
|     // the stream id.
 | ||
|     u_int8_t stream_id;
 | ||
|     
 | ||
|     // 2.4.3.7 Semantic definition of fields in PES packet. page 49.
 | ||
|     int32_t packet_start_code_prefix;
 | ||
|     
 | ||
|     int64_t pts; // 33bits
 | ||
|     int64_t dts; // 33bits
 | ||
|     int64_t pcr;
 | ||
|     
 | ||
|     // header size.
 | ||
|     int packet_header_size;
 | ||
|     
 | ||
|     // the parsed packet size.
 | ||
|     int parsed_packet_size;
 | ||
|     
 | ||
|     // total packet size.
 | ||
|     int packet_data_size;
 | ||
|     char* packet_data;
 | ||
|     
 | ||
|     // for avc.
 | ||
|     u_int8_t nal_ref_idc;
 | ||
|     u_int8_t nal_unit_type;
 | ||
|     
 | ||
|     TSMessage();
 | ||
|     virtual ~TSMessage();
 | ||
|     
 | ||
|     void append(u_int8_t*& p, int size);
 | ||
|     void detach(TSContext* ctx, TSMessage*& pmsg);
 | ||
|     
 | ||
|     bool is_video();
 | ||
| };
 | ||
| 
 | ||
| // ts context
 | ||
| class TSContext
 | ||
| {
 | ||
| public:
 | ||
|     /**
 | ||
|     * consumed pids.
 | ||
|     */
 | ||
|     int pid_size;
 | ||
|     TSPid* pids;
 | ||
|     int64_t ts_packet_count;
 | ||
|     std::map<TSPidTable, TSMessage*> msgs;
 | ||
|     
 | ||
|     TSContext();
 | ||
|     virtual ~TSContext();
 | ||
|     bool exists(TSPidTable pid);
 | ||
|     TSPid* get(TSPidTable pid);
 | ||
|     void push(TSPidTable pid, TSStreamType stream_type, TSPidType type, u_int8_t continuity_counter);
 | ||
|     
 | ||
|     TSMessage* get_msg(TSPidTable pid);
 | ||
|     void detach(TSMessage* msg);
 | ||
| };
 | ||
| 
 | ||
| TSContext::TSContext()
 | ||
| {
 | ||
|     pid_size = 0;
 | ||
|     ts_packet_count = 0;
 | ||
|     pids = NULL;
 | ||
| }
 | ||
| 
 | ||
| TSContext::~TSContext()
 | ||
| {
 | ||
|     srs_freep(pids);
 | ||
|     
 | ||
|     std::map<TSPidTable, TSMessage*>::iterator it;
 | ||
|     for (it = msgs.begin(); it != msgs.end(); ++it) {
 | ||
|         TSMessage* msg = it->second;
 | ||
|         srs_freep(msg);
 | ||
|     }
 | ||
|     msgs.clear();
 | ||
| }
 | ||
| 
 | ||
| bool TSContext::exists(TSPidTable pid)
 | ||
| {
 | ||
|     for (int i = 0; i < pid_size; i++) {
 | ||
|         if (pid == pids[i].pid) {
 | ||
|             return true;
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     return false;
 | ||
| }
 | ||
| 
 | ||
| TSPid* TSContext::get(TSPidTable pid)
 | ||
| {
 | ||
|     for (int i = 0; i < pid_size; i++) {
 | ||
|         if (pid == pids[i].pid) {
 | ||
|             return &pids[i];
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     return NULL;
 | ||
| }
 | ||
| 
 | ||
| void TSContext::push(TSPidTable pid, TSStreamType stream_type, TSPidType type, u_int8_t continuity_counter)
 | ||
| {
 | ||
|     TSPid* p = get(pid);
 | ||
|     
 | ||
|     if (!p) {
 | ||
|         p = new TSPid[pid_size + 1];
 | ||
|         memcpy(p, pids, sizeof(TSPid) * pid_size);
 | ||
|         
 | ||
|         p[pid_size] = (TSPid){type, stream_type, pid, continuity_counter};
 | ||
|         pid_size++;
 | ||
|         
 | ||
|         srs_freep(pids);
 | ||
|         pids = p;
 | ||
|     }
 | ||
|     
 | ||
|     p->continuity_counter = continuity_counter;
 | ||
| }
 | ||
| 
 | ||
| TSMessage* TSContext::get_msg(TSPidTable pid)
 | ||
| {
 | ||
|     if (msgs[pid] == NULL) {
 | ||
|         TSMessage* msg = new TSMessage();
 | ||
|         msg->pid = pid;
 | ||
|         msgs[pid] = msg;
 | ||
|     }
 | ||
|     
 | ||
|     return msgs[pid];
 | ||
| }
 | ||
| 
 | ||
| void TSContext::detach(TSMessage* msg)
 | ||
| {
 | ||
|     msgs[msg->pid] = NULL;
 | ||
| }
 | ||
| 
 | ||
| TSMessage::TSMessage()
 | ||
| {
 | ||
|     pid = TSPidTablePAT;
 | ||
|     type = TSPidTypeReserved;
 | ||
|     stream_type = TSStreamTypeReserved;
 | ||
|     stream_id = 0;
 | ||
|     packet_start_code_prefix = 0;
 | ||
|     pts = dts = pcr = 0;
 | ||
|     PES_packet_length = 0;
 | ||
|     packet_header_size = 0;
 | ||
|     parsed_packet_size = 0;
 | ||
|     packet_data_size = 0;
 | ||
|     packet_data = NULL;
 | ||
|     
 | ||
|     nal_ref_idc = 0;
 | ||
|     nal_unit_type = 0;
 | ||
| }
 | ||
| 
 | ||
| TSMessage::~TSMessage()
 | ||
| {
 | ||
|     srs_freep(packet_data);
 | ||
| }
 | ||
| 
 | ||
| void TSMessage::append(u_int8_t*& p, int size)
 | ||
| {
 | ||
|     if (size <= 0) {
 | ||
|         return;
 | ||
|     }
 | ||
|     
 | ||
|     // for PES_packet_length is 0, the size is varient.
 | ||
|     if (packet_data_size - parsed_packet_size < size) {
 | ||
|         int realloc_size = size - (packet_data_size - parsed_packet_size);
 | ||
|         packet_data = (char*)realloc(packet_data, packet_data_size + realloc_size);
 | ||
|         
 | ||
|         packet_data_size += realloc_size;
 | ||
|     }
 | ||
|     
 | ||
|     memcpy(packet_data + parsed_packet_size, p, size);
 | ||
|     p += size;
 | ||
|     parsed_packet_size += size;
 | ||
| }
 | ||
| 
 | ||
| void TSMessage::detach(TSContext* ctx, TSMessage*& pmsg)
 | ||
| {
 | ||
|     if (parsed_packet_size >= packet_data_size) {
 | ||
|         ctx->detach(this);
 | ||
|         pmsg = this;
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| bool TSMessage::is_video()
 | ||
| {
 | ||
|     return type == TSPidTypeVideo;
 | ||
| }
 | ||
| 
 | ||
| TSAdaptionField::TSAdaptionField()
 | ||
| {
 | ||
|     adaption_field_length = 0;
 | ||
|     discontinuity_indicator = 0;
 | ||
|     random_access_indicator = 0;
 | ||
|     elementary_stream_priority_indicator = 0;
 | ||
|     PCR_flag = 0;
 | ||
|     OPCR_flag = 0;
 | ||
|     splicing_point_flag = 0;
 | ||
|     transport_private_data_flag = 0;
 | ||
|     adaptation_field_extension_flag = 0;
 | ||
|     program_clock_reference_base = 0;
 | ||
|     program_clock_reference_extension = 0;
 | ||
|     original_program_clock_reference_base = 0;
 | ||
|     original_program_clock_reference_extension = 0;
 | ||
|     splice_countdown = 0;
 | ||
|     transport_private_data_length = 0;
 | ||
|     transport_private_data = NULL;
 | ||
|     adaptation_field_extension_length = 0;
 | ||
|     ltw_flag = 0;
 | ||
|     piecewise_rate_flag = 0;
 | ||
|     seamless_splice_flag = 0;
 | ||
|     ltw_valid_flag = 0;
 | ||
|     ltw_offset = 0;
 | ||
|     piecewise_rate = 0;
 | ||
|     splice_type = 0;
 | ||
|     DTS_next_AU0 = 0;
 | ||
|     marker_bit0 = 0;
 | ||
|     DTS_next_AU1 = 0;
 | ||
|     marker_bit1 = 0;
 | ||
|     DTS_next_AU2 = 0;
 | ||
|     marker_bit2 = 0;
 | ||
|     af_ext_reserved = NULL;
 | ||
|     af_reserved = NULL;
 | ||
|     __field_size = 0;
 | ||
|     pcr = 0;
 | ||
|     original_pcr = 0;
 | ||
| }
 | ||
| 
 | ||
| TSAdaptionField::~TSAdaptionField()
 | ||
| {
 | ||
|     srs_freep(transport_private_data);
 | ||
|     srs_freep(af_ext_reserved);
 | ||
|     srs_freep(af_reserved);
 | ||
| }
 | ||
| 
 | ||
| int TSAdaptionField::get_size()
 | ||
| {
 | ||
|     return __field_size;
 | ||
| }
 | ||
| 
 | ||
| int TSAdaptionField::demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg)
 | ||
| {
 | ||
|     int ret = 0;
 | ||
| 
 | ||
|     adaption_field_length = *p++;
 | ||
|     u_int8_t* pos_af = p;
 | ||
|     __field_size = 1 + adaption_field_length;
 | ||
|     
 | ||
|     if (adaption_field_length <= 0) {
 | ||
|         trace("ts+af empty af decoded.");
 | ||
|         return ret;
 | ||
|     }
 | ||
|     
 | ||
|     int8_t value = *p++;
 | ||
|     
 | ||
|     discontinuity_indicator              =   (value >> 7) & 0x01;
 | ||
|     random_access_indicator              =   (value >> 6) & 0x01;
 | ||
|     elementary_stream_priority_indicator =   (value >> 5) & 0x01;
 | ||
|     PCR_flag                             =   (value >> 4) & 0x01;
 | ||
|     OPCR_flag                            =   (value >> 3) & 0x01;
 | ||
|     splicing_point_flag                  =   (value >> 2) & 0x01;
 | ||
|     transport_private_data_flag          =   (value >> 1) & 0x01;
 | ||
|     adaptation_field_extension_flag      =   (value >> 0) & 0x01;
 | ||
|     
 | ||
|     char* pp = NULL;
 | ||
|     if (PCR_flag) {
 | ||
|         pp = (char*)&program_clock_reference_base;
 | ||
|         pp[5] = *p++;
 | ||
|         pp[4] = *p++;
 | ||
|         pp[3] = *p++;
 | ||
|         pp[2] = *p++;
 | ||
|         pp[1] = *p++;
 | ||
|         pp[0] = *p++;
 | ||
|         
 | ||
|         program_clock_reference_extension = program_clock_reference_base & 0x1ff;
 | ||
|         program_clock_reference_base = (program_clock_reference_base >> 15) & 0x1ffffffffLL;
 | ||
|         
 | ||
|         // high 9bits
 | ||
|         pcr = program_clock_reference_extension;
 | ||
|         pcr = (pcr << 33) & 0x3fe00000000LL;
 | ||
|         // low 33bits
 | ||
|         pcr |= program_clock_reference_base;
 | ||
|     }
 | ||
|     if (OPCR_flag) {
 | ||
|         pp = (char*)&original_program_clock_reference_base;
 | ||
|         pp[5] = *p++;
 | ||
|         pp[4] = *p++;
 | ||
|         pp[3] = *p++;
 | ||
|         pp[2] = *p++;
 | ||
|         pp[1] = *p++;
 | ||
|         pp[0] = *p++;
 | ||
|         
 | ||
|         original_program_clock_reference_extension = original_program_clock_reference_base & 0x1ff;
 | ||
|         original_program_clock_reference_base = (original_program_clock_reference_base >> 15) & 0x1ffffffffLL;
 | ||
|         
 | ||
|         // high 9bits
 | ||
|         original_pcr = program_clock_reference_extension;
 | ||
|         original_pcr = (original_pcr << 33) & 0x3fe00000000LL;
 | ||
|         // low 33bits
 | ||
|         original_pcr |= program_clock_reference_base;
 | ||
|     }
 | ||
|     if (splicing_point_flag) {
 | ||
|         splice_countdown = *p++;
 | ||
|     }
 | ||
|     if (transport_private_data_flag) {
 | ||
|         transport_private_data_length = *p++;
 | ||
|         transport_private_data = new char[transport_private_data_length];
 | ||
|         for (int i = 0; i < transport_private_data_length; i++) {
 | ||
|             transport_private_data[i] = *p++;
 | ||
|         }
 | ||
|     }
 | ||
|     if (adaptation_field_extension_flag) {
 | ||
|         adaptation_field_extension_length = *p++;
 | ||
|         u_int8_t* pos_af_ext = p;
 | ||
|         
 | ||
|         ltw_flag = *p++;
 | ||
|         
 | ||
|         piecewise_rate_flag = (ltw_flag >> 6) & 0x01;
 | ||
|         seamless_splice_flag = (ltw_flag >> 5) & 0x01;
 | ||
|         ltw_flag = (ltw_flag >> 7) & 0x01;
 | ||
|         
 | ||
|         if (ltw_flag) {
 | ||
|             pp = (char*)<w_offset;
 | ||
|             pp[1] = *p++;
 | ||
|             pp[0] = *p++;
 | ||
|             
 | ||
|             ltw_valid_flag = (ltw_offset >> 15) &0x01;
 | ||
|             ltw_offset &= 0x7FFF;
 | ||
|         }
 | ||
|         if (piecewise_rate_flag) {
 | ||
|             pp = (char*)&piecewise_rate;
 | ||
|             pp[2] = *p++;
 | ||
|             pp[1] = *p++;
 | ||
|             pp[0] = *p++;
 | ||
|             
 | ||
|             piecewise_rate &= 0x3FFFFF;
 | ||
|         }
 | ||
|         if (seamless_splice_flag) {
 | ||
|             // 1B
 | ||
|             marker_bit0 = *p++;
 | ||
|             
 | ||
|             splice_type = (marker_bit0 >> 4) & 0x0F;
 | ||
|             DTS_next_AU0 = (marker_bit0 >> 1) & 0x07;
 | ||
|             marker_bit0 &= 0x01;
 | ||
|             
 | ||
|             // 2B
 | ||
|             pp = (char*)&DTS_next_AU1;
 | ||
|             pp[1] = *p++;
 | ||
|             pp[0] = *p++;
 | ||
|             
 | ||
|             marker_bit1 = DTS_next_AU1 & 0x01;
 | ||
|             DTS_next_AU1 = (DTS_next_AU1 >> 1) & 0x7FFF;
 | ||
|             
 | ||
|             // 2B
 | ||
|             pp = (char*)&DTS_next_AU2;
 | ||
|             pp[1] = *p++;
 | ||
|             pp[0] = *p++;
 | ||
|             
 | ||
|             marker_bit2 = DTS_next_AU2 & 0x01;
 | ||
|             DTS_next_AU2 = (DTS_next_AU2 >> 1) & 0x7FFF;
 | ||
|         }
 | ||
|         
 | ||
|         // af_ext_reserved
 | ||
|         int ext_size = adaptation_field_extension_length - (p - pos_af_ext);
 | ||
|         if (ext_size > 0) {
 | ||
|             af_ext_reserved = new char[ext_size];
 | ||
|             memcpy(af_ext_reserved, p, ext_size);
 | ||
|             p += ext_size;
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     // af_reserved
 | ||
|     int af_size = adaption_field_length - (p - pos_af);
 | ||
|     if (af_size > 0) {
 | ||
|         af_reserved = new char[af_size];
 | ||
|         memcpy(af_reserved, p, af_size);
 | ||
|         p += af_size;
 | ||
|     }
 | ||
|     
 | ||
|     trace("ts+af af flags parsed, discontinuity: %d random: %d priority: %d PCR: %d OPCR: %d slicing: %d private: %d extension: %d pcr: %"PRId64" opcr: %"PRId64"",
 | ||
|         discontinuity_indicator, random_access_indicator, elementary_stream_priority_indicator, PCR_flag, OPCR_flag, splicing_point_flag,
 | ||
|         transport_private_data_flag, adaptation_field_extension_flag, pcr, original_pcr);
 | ||
|     
 | ||
|     return ret;
 | ||
| }
 | ||
| 
 | ||
| TSPayloadReserved::TSPayloadReserved()
 | ||
| {
 | ||
|     size = 0;
 | ||
|     bytes = NULL;
 | ||
| }
 | ||
| 
 | ||
| TSPayloadReserved::~TSPayloadReserved()
 | ||
| {
 | ||
|     srs_freep(bytes);
 | ||
| }
 | ||
| 
 | ||
| int TSPayloadReserved::demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg)
 | ||
| {
 | ||
|     int ret = 0;
 | ||
|     
 | ||
|     size = pkt->payload->size - pkt->payload->pointer_field_size;
 | ||
| 
 | ||
|     // not parsed bytes.
 | ||
|     if (size > 0) {
 | ||
|         bytes = new char[size];
 | ||
|         memcpy(bytes, p, size);
 | ||
|         p += size;
 | ||
|     }
 | ||
| 
 | ||
|     return ret;
 | ||
| }
 | ||
| 
 | ||
| TSPayloadPAT::TSPayloadPAT()
 | ||
| {
 | ||
|     table_id = 0;
 | ||
|     section_syntax_indicator = 0;
 | ||
|     const0_value = 0;
 | ||
|     section_length = 0;
 | ||
|     transport_stream_id = 0;
 | ||
|     version_number = 0;
 | ||
|     current_next_indicator = 0;
 | ||
|     section_number = 0;
 | ||
|     last_section_number = 0;
 | ||
|     program_size = 0;
 | ||
|     programs = NULL;
 | ||
|     CRC_32 = 0;
 | ||
| }
 | ||
| 
 | ||
| TSPayloadPAT::~TSPayloadPAT()
 | ||
| {
 | ||
|     srs_freep(programs);
 | ||
| }
 | ||
| 
 | ||
| int TSPayloadPAT::demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg)
 | ||
| {
 | ||
|     int ret = 0;
 | ||
|     
 | ||
|     table_id = *p++;
 | ||
|     
 | ||
|     char* pp = (char*)§ion_length;
 | ||
|     pp[1] = *p++;
 | ||
|     pp[0] = *p++;
 | ||
|     u_int8_t* pos = p;
 | ||
|     
 | ||
|     section_syntax_indicator = (section_length >> 15) & 0x01;
 | ||
|     const0_value = (section_length >> 14) & 0x01;
 | ||
|     section_length &= 0x0FFF;
 | ||
|     
 | ||
|     pp = (char*)&transport_stream_id;
 | ||
|     pp[1] = *p++;
 | ||
|     pp[0] = *p++;
 | ||
|     
 | ||
|     current_next_indicator = *p++;
 | ||
|     version_number = (current_next_indicator >> 1) & 0x1F;
 | ||
|     current_next_indicator &= 0x01;
 | ||
|     
 | ||
|     section_number = *p++;
 | ||
|     last_section_number = *p++;
 | ||
|     
 | ||
|     // 4 is crc size.
 | ||
|     int program_bytes = section_length - 4 - (p - pos);
 | ||
|     program_size = program_bytes / 4;
 | ||
|     if (program_size > 0) {
 | ||
|         programs = new int32_t[program_size];
 | ||
|         for (int i = 0; i < program_size; i++) {
 | ||
|             pp = (char*)&programs[i];
 | ||
|             pp[3] = *p++;
 | ||
|             pp[2] = *p++;
 | ||
|             pp[1] = *p++;
 | ||
|             pp[0] = *p++;
 | ||
|             
 | ||
|             int16_t pid = programs[i] & 0x1FFF;
 | ||
|             ctx->push((TSPidTable)pid, TSStreamTypeReserved, TSPidTypePMT, pkt->header->continuity_counter);
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     pp = (char*)&CRC_32;
 | ||
|     pp[3] = *p++;
 | ||
|     pp[2] = *p++;
 | ||
|     pp[1] = *p++;
 | ||
|     pp[0] = *p++;
 | ||
|     
 | ||
|     return ret;
 | ||
| }
 | ||
| 
 | ||
| TSPMTESInfo::TSPMTESInfo()
 | ||
| {
 | ||
|     stream_type = 0;
 | ||
|     elementary_PID = 0;
 | ||
|     ES_info_length = 0;
 | ||
|     ES_info = NULL;
 | ||
| }
 | ||
| 
 | ||
| TSPMTESInfo::~TSPMTESInfo()
 | ||
| {
 | ||
|     srs_freep(ES_info);
 | ||
| }
 | ||
| 
 | ||
| TSPayloadPMT::TSPayloadPMT()
 | ||
| {
 | ||
|     table_id = 0;
 | ||
|     section_syntax_indicator = 0;
 | ||
|     const0_value = 0;
 | ||
|     section_length = 0;
 | ||
|     program_number = 0;
 | ||
|     version_number = 0;
 | ||
|     current_next_indicator = 0;
 | ||
|     section_number = 0;
 | ||
|     last_section_number = 0;
 | ||
|     PCR_PID = 0;
 | ||
|     program_info_length = 0;
 | ||
|     program_info_desc = NULL;
 | ||
|     CRC_32 = 0;
 | ||
| }
 | ||
| 
 | ||
| TSPayloadPMT::~TSPayloadPMT()
 | ||
| {
 | ||
|     srs_freep(program_info_desc);
 | ||
|     
 | ||
|     for (std::vector<TSPMTESInfo*>::iterator it = ES_info.begin(); it != ES_info.end(); ++it) {
 | ||
|         TSPMTESInfo* info = *it;
 | ||
|         srs_freep(info);
 | ||
|     }
 | ||
|     ES_info.clear();
 | ||
| }
 | ||
| 
 | ||
| TSPMTESInfo* TSPayloadPMT::at(int index)
 | ||
| {
 | ||
|     return ES_info.at(index);
 | ||
| }
 | ||
| 
 | ||
| int TSPayloadPMT::demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg)
 | ||
| {
 | ||
|     int ret = 0;
 | ||
|     
 | ||
|     table_id = *p++;
 | ||
|     
 | ||
|     char* pp = (char*)§ion_length;
 | ||
|     pp[1] = *p++;
 | ||
|     pp[0] = *p++;
 | ||
|     u_int8_t* pos = p;
 | ||
|     
 | ||
|     section_syntax_indicator = (section_length >> 15) & 0x01;
 | ||
|     const0_value = (section_length >> 14) & 0x01;
 | ||
|     section_length &= 0x0FFF;
 | ||
|     
 | ||
|     pp = (char*)&program_number;
 | ||
|     pp[1] = *p++;
 | ||
|     pp[0] = *p++;
 | ||
|     
 | ||
|     current_next_indicator = *p++;
 | ||
|     version_number = (current_next_indicator >> 1) & 0x1F;
 | ||
|     current_next_indicator &= 0x01;
 | ||
|     
 | ||
|     section_number = *p++;
 | ||
|     last_section_number = *p++;
 | ||
|     
 | ||
|     pp = (char*)&PCR_PID;
 | ||
|     pp[1] = *p++;
 | ||
|     pp[0] = *p++;
 | ||
|     
 | ||
|     PCR_PID &= 0x1FFF;
 | ||
|     
 | ||
|     pp = (char*)&program_info_length;
 | ||
|     pp[1] = *p++;
 | ||
|     pp[0] = *p++;
 | ||
|     
 | ||
|     program_info_length &= 0xFFF;
 | ||
|     
 | ||
|     if (program_info_length > 0) {
 | ||
|         program_info_desc = new char[program_info_length];
 | ||
|         memcpy(program_info_desc, p, program_info_length);
 | ||
|         p += program_info_length;
 | ||
|     }
 | ||
|     
 | ||
|     // [section_length] - 4(CRC) - 9B - [program_info_length]
 | ||
|     int ES_bytes = section_length - 4 - 9 - program_info_length;
 | ||
|     while (ES_bytes > 0) {
 | ||
|         TSPMTESInfo* info = new TSPMTESInfo();
 | ||
|         
 | ||
|         info->stream_type = *p++;
 | ||
|         ES_bytes--;
 | ||
|         
 | ||
|         pp = (char*)&info->elementary_PID;
 | ||
|         pp[1] = *p++;
 | ||
|         pp[0] = *p++;
 | ||
|         ES_bytes -= 2;
 | ||
|         
 | ||
|         info->elementary_PID &= 0x1FFF;
 | ||
|         
 | ||
|         pp = (char*)&info->ES_info_length;
 | ||
|         pp[1] = *p++;
 | ||
|         pp[0] = *p++;
 | ||
|         ES_bytes -= 2;
 | ||
|         
 | ||
|         info->ES_info_length &= 0x0FFF;
 | ||
|         
 | ||
|         if (info->ES_info_length > 0) {
 | ||
|             info->ES_info = new char[info->ES_info_length];
 | ||
|             memcpy(info->ES_info, p, info->ES_info_length);
 | ||
|             
 | ||
|             p += info->ES_info_length;
 | ||
|             ES_bytes -= info->ES_info_length;
 | ||
|         }
 | ||
|         
 | ||
|         ES_info.push_back(info);
 | ||
|         
 | ||
|         if (info->stream_type == TSStreamTypeVideoH264) {
 | ||
|             // TODO: support more video type.
 | ||
|             ctx->push((TSPidTable)info->elementary_PID, (TSStreamType)info->stream_type, TSPidTypeVideo, pkt->header->continuity_counter);
 | ||
|             trace("ts+pmt add pid: %d, type: H264 video", info->elementary_PID);
 | ||
|         } else if (info->stream_type == TSStreamTypeAudioAAC) {
 | ||
|             // TODO: support more audio type.
 | ||
|             // see aac: 6.2 Audio Data Transport Stream, ADTS
 | ||
|             ctx->push((TSPidTable)info->elementary_PID, (TSStreamType)info->stream_type, TSPidTypeAudio, pkt->header->continuity_counter);
 | ||
|             trace("ts+pmt add pid: %d, type: AAC audio", info->elementary_PID);
 | ||
|         } else {
 | ||
|             trace("ts+pmt ignore the stream type: %d", info->stream_type);
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     pp = (char*)&CRC_32;
 | ||
|     pp[3] = *p++;
 | ||
|     pp[2] = *p++;
 | ||
|     pp[1] = *p++;
 | ||
|     pp[0] = *p++;
 | ||
|     
 | ||
|     return ret;
 | ||
| }
 | ||
| 
 | ||
| TSPayloadPES::TSPayloadPES()
 | ||
| {
 | ||
|     packet_start_code_prefix = 0;
 | ||
|     stream_id = 0;
 | ||
|     PES_packet_length = 0;
 | ||
|     PES_scrambling_control = 0;
 | ||
|     PES_priority = 0;
 | ||
|     data_alignment_indicator = 0;
 | ||
|     copyright = 0;
 | ||
|     original_or_copy = 0;
 | ||
|     PTS_DTS_flags = 0;
 | ||
|     ESCR_flag = 0;
 | ||
|     ES_rate_flag = 0;
 | ||
|     DSM_trick_mode_flag = 0;
 | ||
|     additional_copy_info_flag = 0;
 | ||
|     PES_CRC_flag = 0;
 | ||
|     PES_extension_flag = 0;
 | ||
|     PES_header_data_length = 0;
 | ||
|     pts = 0;
 | ||
|     dts = 0;
 | ||
|     ESCR_extension = 0;
 | ||
|     ESCR_base = 0;
 | ||
|     ES_rate = 0;
 | ||
|     trick_mode_control = 0;
 | ||
|     trick_mode_value = 0;
 | ||
|     additional_copy_info = 0;
 | ||
|     previous_PES_packet_CRC = 0;
 | ||
|     PES_private_data_flag = 0;
 | ||
|     pack_header_field_flag = 0;
 | ||
|     program_packet_sequence_counter_flag = 0;
 | ||
|     P_STD_buffer_flag = 0;
 | ||
|     PES_extension_flag_2 = 0;
 | ||
|     PES_private_data = NULL;
 | ||
|     pack_field_length = 0;
 | ||
|     pack_field = NULL;
 | ||
|     program_packet_sequence_counter = 0;
 | ||
|     MPEG1_MPEG2_identifier = 0;
 | ||
|     original_stuff_length = 0;
 | ||
|     P_STD_buffer_scale = 0;
 | ||
|     P_STD_buffer_size = 0;
 | ||
|     PES_extension_field_length = 0;
 | ||
|     PES_extension_field = NULL;
 | ||
|     stuffing_size = 0;
 | ||
|     stuffing_byte = NULL;
 | ||
| }
 | ||
| 
 | ||
| TSPayloadPES::~TSPayloadPES()
 | ||
| {
 | ||
|     srs_freep(PES_private_data);
 | ||
|     srs_freep(pack_field);
 | ||
|     srs_freep(PES_extension_field);
 | ||
|     srs_freep(stuffing_byte);
 | ||
| }
 | ||
| 
 | ||
| int64_t TSPayloadPES::decode_33bits_int(u_int8_t*& p, int64_t& temp)
 | ||
| {
 | ||
|     char* pp = (char*)&temp;
 | ||
|     pp[4] = *p++;
 | ||
|     pp[3] = *p++;
 | ||
|     pp[2] = *p++;
 | ||
|     pp[1] = *p++;
 | ||
|     pp[0] = *p++;
 | ||
|     
 | ||
|     return decode_33bits_int(temp);
 | ||
| }
 | ||
| 
 | ||
| int64_t TSPayloadPES::decode_33bits_int(int64_t& temp)
 | ||
| {
 | ||
|     int64_t ret = 0;
 | ||
|     
 | ||
|     // marker_bit 1bit
 | ||
|     temp = temp >> 1;
 | ||
|     // PTS [14..0] 15bits
 | ||
|     ret |= temp & 0x7fff;
 | ||
|     // marker_bit 1bit
 | ||
|     temp = temp >> 1;
 | ||
|     // PTS [29..15] 15bits, 15zero, 29-15+1one
 | ||
|     ret |= temp & 0x3fff8000LL;
 | ||
|     // marker_bit 1bit
 | ||
|     temp = temp >> 1;
 | ||
|     // PTS [32..30] 3bits
 | ||
|     ret |= temp & 0x1c0000000LL;
 | ||
|     
 | ||
|     temp = temp >> 33;
 | ||
|     
 | ||
|     return ret;
 | ||
| }
 | ||
| 
 | ||
| int TSPayloadPES::demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg)
 | ||
| {
 | ||
|     int ret = 0;
 | ||
|     
 | ||
|     char* pp = (char*)&packet_start_code_prefix;
 | ||
|     pp[2] = *p++;
 | ||
|     pp[1] = *p++;
 | ||
|     pp[0] = *p++;
 | ||
|     
 | ||
|     packet_start_code_prefix &= 0xFFFFFF;
 | ||
|     if (packet_start_code_prefix != 0x01) {
 | ||
|         trace("ts+pes decode unit start packet error, msg is empty.");
 | ||
|         return -1;
 | ||
|     }
 | ||
| 
 | ||
|     stream_id = *p++;
 | ||
|     
 | ||
|     pp = (char*)&PES_packet_length;
 | ||
|     pp[1] = *p++;
 | ||
|     pp[0] = *p++;
 | ||
|     u_int8_t* pos_packet = p;
 | ||
|     
 | ||
|     if (stream_id != PES_program_stream_map
 | ||
|         && stream_id != PES_padding_stream
 | ||
|         && stream_id != PES_private_stream_2
 | ||
|         && stream_id != PES_ECM_stream
 | ||
|         && stream_id != PES_EMM_stream
 | ||
|         && stream_id != PES_program_stream_directory
 | ||
|         && stream_id != PES_DSMCC_stream
 | ||
|         && stream_id != PES_H_222_1_type_E
 | ||
|     ) {
 | ||
|         original_or_copy = *p++;
 | ||
|         
 | ||
|         //int8_t const2bits = (original_or_copy >> 6) & 0x03;
 | ||
|         PES_scrambling_control = (original_or_copy >> 4) & 0x03;
 | ||
|         PES_priority = (original_or_copy >> 3) & 0x01;
 | ||
|         data_alignment_indicator = (original_or_copy >> 2) & 0x01;
 | ||
|         copyright = (original_or_copy >> 1) & 0x01;
 | ||
|         original_or_copy &= 0x01;
 | ||
|         
 | ||
|         PES_extension_flag = *p++;
 | ||
|         
 | ||
|         PTS_DTS_flags = (PES_extension_flag >> 6) & 0x03;
 | ||
|         ESCR_flag = (PES_extension_flag >> 5) & 0x01;
 | ||
|         ES_rate_flag = (PES_extension_flag >> 4) & 0x01;
 | ||
|         DSM_trick_mode_flag = (PES_extension_flag >> 3) & 0x01;
 | ||
|         additional_copy_info_flag = (PES_extension_flag >> 2) & 0x01;
 | ||
|         PES_CRC_flag = (PES_extension_flag >> 1) & 0x01;
 | ||
|         PES_extension_flag &= 0x01;
 | ||
|         
 | ||
|         PES_header_data_length = *p++;
 | ||
|         u_int8_t* pos_header = p;
 | ||
| 
 | ||
|         int64_t temp = 0;
 | ||
|         if (PTS_DTS_flags == 0x2) {
 | ||
|             pts = decode_33bits_int(p, temp);
 | ||
|             // '0010' 4bits
 | ||
|             //int8_t const4bits = temp & 0x0F;
 | ||
|         }
 | ||
| 
 | ||
|         if (PTS_DTS_flags == 0x3) {
 | ||
|             pts = decode_33bits_int(p, temp);
 | ||
|             // '0011' 4bits
 | ||
|             //int8_t const4bits = temp & 0x0F;
 | ||
|             
 | ||
|             dts = decode_33bits_int(p, temp);
 | ||
|             // '0001' 4bits
 | ||
|             //int8_t const4bits = temp & 0x0F;
 | ||
|         }
 | ||
|         
 | ||
|         if (ESCR_flag) {
 | ||
|             pp = (char*)&temp;
 | ||
|             pp[5] = *p++;
 | ||
|             pp[4] = *p++;
 | ||
|             pp[3] = *p++;
 | ||
|             pp[2] = *p++;
 | ||
|             pp[1] = *p++;
 | ||
|             pp[0] = *p++;
 | ||
|             
 | ||
|             // marker_bit 1bit
 | ||
|             temp = temp >> 1;
 | ||
|             // ESCR_extension 9bits
 | ||
|             ESCR_extension = temp & 0x1f;
 | ||
|             temp = temp >> 9;
 | ||
|             
 | ||
|             ESCR_base = decode_33bits_int(temp);
 | ||
|             
 | ||
|             // reserved 2bits
 | ||
|             //int8_t reserved2bits = temp & 0x03;
 | ||
|         }
 | ||
|         
 | ||
|         if (ES_rate_flag) {
 | ||
|             pp = (char*)&ES_rate;
 | ||
|             pp[2] = *p++;
 | ||
|             pp[1] = *p++;
 | ||
|             pp[0] = *p++;
 | ||
|             
 | ||
|             ES_rate = ES_rate >> 1;
 | ||
|             ES_rate &= 0x3FFFFF;
 | ||
|         }
 | ||
| 
 | ||
|         if (DSM_trick_mode_flag) {
 | ||
|             trick_mode_control = *p++;
 | ||
| 
 | ||
|             trick_mode_value = trick_mode_control & 0x1f;
 | ||
|             trick_mode_control = (trick_mode_control >> 5) & 0x03;
 | ||
|         }
 | ||
|         
 | ||
|         if (additional_copy_info_flag) {
 | ||
|             additional_copy_info = *p++;
 | ||
|             additional_copy_info &= 0x7f;
 | ||
|         }
 | ||
|         
 | ||
|         if (PES_CRC_flag) {
 | ||
|             pp = (char*)&previous_PES_packet_CRC;
 | ||
|             pp[1] = *p++;
 | ||
|             pp[0] = *p++;
 | ||
|         }
 | ||
| 
 | ||
|         if (PES_extension_flag) {
 | ||
|             PES_extension_flag_2 = *p++;
 | ||
|             
 | ||
|             PES_private_data_flag = (PES_extension_flag_2 >> 7) & 0x01;
 | ||
|             pack_header_field_flag = (PES_extension_flag_2 >> 6) & 0x01;
 | ||
|             program_packet_sequence_counter_flag = (PES_extension_flag_2 >> 5) & 0x01;
 | ||
|             P_STD_buffer_flag = (PES_extension_flag_2 >> 4) & 0x01;
 | ||
|             PES_extension_flag_2 &= PES_extension_flag_2 & 0x01;
 | ||
|             
 | ||
|             if (PES_private_data_flag) {
 | ||
|                 PES_private_data = new char[16];
 | ||
|                 memcpy(PES_private_data, p, 16);
 | ||
|                 p += 16;
 | ||
|             }
 | ||
|             
 | ||
|             if (pack_header_field_flag) {
 | ||
|                 pack_field_length = *p++;
 | ||
|                 if (pack_field_length > 0) {
 | ||
|                     pack_field = new char[pack_field_length];
 | ||
|                     memcpy(pack_field, p, pack_field_length);
 | ||
|                     p += pack_field_length;
 | ||
|                 }
 | ||
|             }
 | ||
|             
 | ||
|             if (program_packet_sequence_counter_flag) {
 | ||
|                 program_packet_sequence_counter = *p++;
 | ||
|                 program_packet_sequence_counter &= 0x7f;
 | ||
|                 
 | ||
|                 original_stuff_length = *p++;
 | ||
|                 MPEG1_MPEG2_identifier = (original_stuff_length >> 6) & 0x01;
 | ||
|                 original_stuff_length &= 0x3f;
 | ||
|             }
 | ||
|             
 | ||
|             if (P_STD_buffer_flag) {
 | ||
|                 pp = (char*)&P_STD_buffer_size;
 | ||
|                 pp[1] = *p++;
 | ||
|                 pp[0] = *p++;
 | ||
|                 
 | ||
|                 // '01'
 | ||
|                 //int8_t const2bits = (P_STD_buffer_scale >>14) & 0x03;
 | ||
| 
 | ||
|                 P_STD_buffer_scale = (P_STD_buffer_scale >>13) & 0x01;
 | ||
|                 P_STD_buffer_size &= 0x1FFF;
 | ||
|             }
 | ||
|             
 | ||
|             if (PES_extension_flag_2) {
 | ||
|                 PES_extension_field_length = *p++;
 | ||
|                 PES_extension_field_length &= 0x07;
 | ||
|                 
 | ||
|                 if (PES_extension_field_length > 0) {
 | ||
|                     PES_extension_field = new char[PES_extension_field_length];
 | ||
|                     memcpy(PES_extension_field, p, PES_extension_field_length);
 | ||
|                     p += PES_extension_field_length;
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
|         
 | ||
|         // stuffing_byte
 | ||
|         int stuffing_size = PES_header_data_length - (p - pos_header);
 | ||
|         if (stuffing_size > 0) {
 | ||
|             stuffing_byte = new char[stuffing_size];
 | ||
|             memcpy(stuffing_byte, p, stuffing_size);
 | ||
|             p += stuffing_size;
 | ||
|         }
 | ||
|         
 | ||
|         // get the pid.
 | ||
|         TSPid* pid = ctx->get(pkt->header->pid);
 | ||
|         if (!pid) {
 | ||
|             trace("ts+pes pid: %d type is invalid.", pkt->header->pid);
 | ||
|         }
 | ||
|         
 | ||
|         // get the message to build from the chunks(PES packets).
 | ||
|         TSMessage* msg = ctx->get_msg(pid->pid);
 | ||
| 
 | ||
|         msg->type = pid->type;
 | ||
|         msg->stream_type = pid->stream_type;
 | ||
|         msg->continuity_counter = pid->continuity_counter;
 | ||
|         msg->stream_id = stream_id;
 | ||
|         msg->packet_start_code_prefix = packet_start_code_prefix;
 | ||
|         msg->dts = dts;
 | ||
|         msg->pts = pts;
 | ||
|         
 | ||
|         // PES_packet_data_byte, page58.
 | ||
|         // the packet size contains the header size.
 | ||
|         // The number of PES_packet_data_bytes, N, is specified by the 
 | ||
|         // PES_packet_length field. N shall be equal to the value 
 | ||
|         // indicated in the PES_packet_length minus the number of bytes 
 | ||
|         // between the last byte of the PES_packet_length field and the 
 | ||
|         // first PES_packet_data_byte.
 | ||
|         msg->PES_packet_length = PES_packet_length;
 | ||
|         msg->packet_header_size = p - pos_packet;
 | ||
|         msg->packet_data_size = PES_packet_length - msg->packet_header_size;
 | ||
|         
 | ||
|         /**
 | ||
|         * when actual packet length > 0xffff(65535),
 | ||
|         * which exceed the max u_int16_t packet length,
 | ||
|         * use 0 packet length, the next unit start indicates the end of packet.
 | ||
|         */
 | ||
|         if (PES_packet_length == 0) {
 | ||
|             msg->packet_data_size = last - p - msg->packet_header_size;
 | ||
|         }
 | ||
|         
 | ||
|         if (msg->packet_data_size > 0) {
 | ||
|             msg->packet_data = new char[msg->packet_data_size];
 | ||
|         }
 | ||
|         
 | ||
|         // PES_packet_data_byte
 | ||
|         int size = srs_min(msg->packet_data_size, last - p);
 | ||
|         if (size > 0) {
 | ||
|             msg->append(p, size);
 | ||
|         }
 | ||
|         
 | ||
|         if (PES_packet_length > 0) {
 | ||
|             msg->detach(ctx, pmsg);
 | ||
|         }
 | ||
|     
 | ||
|         trace("ts+pes stream_id: %d size: %d pts: %"PRId64" dts: %"PRId64" total: %d header: %d packet_size: %d parsed_size: %d",
 | ||
|             stream_id, PES_packet_length, pts, dts, msg->PES_packet_length, msg->packet_header_size, msg->packet_data_size, msg->parsed_packet_size);
 | ||
|     } else if (stream_id == PES_program_stream_map
 | ||
|         || stream_id == PES_private_stream_2
 | ||
|         || stream_id == PES_ECM_stream
 | ||
|         || stream_id == PES_EMM_stream
 | ||
|         || stream_id == PES_program_stream_directory
 | ||
|         || stream_id == PES_DSMCC_stream
 | ||
|         || stream_id == PES_H_222_1_type_E
 | ||
|     ) {
 | ||
|         // for (i = 0; i < PES_packet_length; i++) {
 | ||
|         //         PES_packet_data_byte
 | ||
|         // }
 | ||
|         // TODO: FIXME: implements it.
 | ||
|     } else if (stream_id == PES_padding_stream) {
 | ||
|         // for (i = 0; i < PES_packet_length; i++) {
 | ||
|         //         padding_byte
 | ||
|         // }
 | ||
|     }
 | ||
|     
 | ||
|     return ret;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
| * 2.4.3.6 PES packet. page 49.
 | ||
| */
 | ||
| 
 | ||
| TSPayload::TSPayload()
 | ||
| {
 | ||
|     size = 0;
 | ||
|     pointer_field_size = 0;
 | ||
|     type = TSPidTypeReserved;
 | ||
|     pointer_field = 0;
 | ||
|     reserved = NULL;
 | ||
|     pat = NULL;
 | ||
|     pmt = NULL;
 | ||
|     pes = NULL;
 | ||
|     
 | ||
| }
 | ||
| 
 | ||
| TSPayload::~TSPayload()
 | ||
| {
 | ||
|     srs_freep(reserved);
 | ||
|     srs_freep(pat);
 | ||
|     srs_freep(pmt);
 | ||
|     srs_freep(pes);
 | ||
| }
 | ||
| 
 | ||
| void TSPayload::read_pointer_field(TSPacket* pkt, u_int8_t*& p)
 | ||
| {
 | ||
|     if (pkt->header->payload_unit_start_indicator) {
 | ||
|         pointer_field = *p++;
 | ||
|         pointer_field_size = 1;
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| int TSPayload::demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg)
 | ||
| {
 | ||
|     int ret = 0;
 | ||
|     
 | ||
|     if (pkt->header->pid == TSPidTablePAT) {
 | ||
|         read_pointer_field(pkt, p);
 | ||
|         
 | ||
|         type = TSPidTypePAT;
 | ||
|         pat = new TSPayloadPAT();
 | ||
|         return pat->demux(ctx, pkt, start, last, p, pmsg);
 | ||
|     }
 | ||
|     
 | ||
|     TSPid* pid = ctx->get(pkt->header->pid);
 | ||
|     if (pid && pid->type == TSPidTypePMT) {
 | ||
|         read_pointer_field(pkt, p);
 | ||
|         
 | ||
|         type = pid->type;
 | ||
|         pmt = new TSPayloadPMT();
 | ||
|         return pmt->demux(ctx, pkt, start, last, p, pmsg);
 | ||
|     }
 | ||
|     if (pid && (pid->type == TSPidTypeVideo || pid->type == TSPidTypeAudio)) {
 | ||
|         TSMessage* msg = ctx->get_msg(pkt->header->pid);
 | ||
| 
 | ||
|         if (pkt->adaption_field->pcr > 0) {
 | ||
|             msg->pcr = pkt->adaption_field->pcr;
 | ||
|         }
 | ||
|         
 | ||
|         // flush previous PES_packet_length(0) packets.
 | ||
|         if (msg->packet_start_code_prefix == 0x01 
 | ||
|             && pkt->header->payload_unit_start_indicator == 1
 | ||
|             && msg->PES_packet_length == 0
 | ||
|         ) {
 | ||
|             msg->detach(ctx, pmsg);
 | ||
|             // reparse current message
 | ||
|             p = start;
 | ||
|             return ret;
 | ||
|         }
 | ||
|         
 | ||
|         // parse continous packet.
 | ||
|         if (!pkt->header->payload_unit_start_indicator) {
 | ||
|             if (msg->packet_start_code_prefix != 0x01) {
 | ||
|                 trace("ts+pes decode continous packet error, msg is empty.");
 | ||
|                 return -1;
 | ||
|             }
 | ||
|             msg->append(p, last - p);
 | ||
|             
 | ||
|             // for PES_packet_length is 0, donot attach it.
 | ||
|             if (msg->PES_packet_length > 0) {
 | ||
|                 msg->detach(ctx, pmsg);
 | ||
|             }
 | ||
|             return ret;
 | ||
|         }
 | ||
|         
 | ||
|         type = pid->type;
 | ||
|         pes = new TSPayloadPES();
 | ||
|         return pes->demux(ctx, pkt, start, last, p, pmsg);
 | ||
|     }
 | ||
|     
 | ||
|     // not parsed bytes.
 | ||
|     type = TSPidTypeReserved;
 | ||
|     reserved = new TSPayloadReserved();
 | ||
|     if ((ret = reserved->demux(ctx, pkt, start, last, p, pmsg)) != 0) {
 | ||
|         return ret;
 | ||
|     }
 | ||
|     
 | ||
|     return ret;
 | ||
| }
 | ||
| 
 | ||
| TSPacket::TSPacket()
 | ||
| {
 | ||
|     header = new TSHeader();
 | ||
|     adaption_field = new TSAdaptionField();
 | ||
|     payload = new TSPayload();
 | ||
| }
 | ||
| 
 | ||
| TSPacket::~TSPacket()
 | ||
| {
 | ||
|     srs_freep(header);
 | ||
|     srs_freep(adaption_field);
 | ||
|     srs_freep(payload);
 | ||
| }
 | ||
| 
 | ||
| int TSPacket::demux(TSContext* ctx, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg)
 | ||
| {
 | ||
|     int ret = 0;
 | ||
|     
 | ||
|     if ((ret = header->demux(ctx, this, start, last, p, pmsg))  != 0) {
 | ||
|         return ret;
 | ||
|     }
 | ||
| 
 | ||
|     if (header->adaption_field_control == TSAdaptionTypeAdaptionOnly || header->adaption_field_control == TSAdaptionTypeBoth) {
 | ||
|         if ((ret = adaption_field->demux(ctx, this, start, last, p, pmsg)) != 0) {
 | ||
|             trace("ts+header af(adaption field) decode error. ret=%d", ret);
 | ||
|             return ret;
 | ||
|         }
 | ||
|         trace("ts+header af(adaption field) decoded.");
 | ||
|     }
 | ||
|     
 | ||
|     // calc the user defined data size for payload.
 | ||
|     payload->size = TS_PACKET_SIZE - header->get_size() - adaption_field->get_size();
 | ||
|     
 | ||
|     if (header->adaption_field_control == TSAdaptionTypePayloadOnly || header->adaption_field_control == TSAdaptionTypeBoth) {
 | ||
|         // parse new packet.
 | ||
|         if ((ret = payload->demux(ctx, this, start, last, p, pmsg)) != 0) {
 | ||
|             trace("ts+header payload decode error. ret=%d", ret);
 | ||
|             return ret;
 | ||
|         }
 | ||
|         trace("ts+header payload decoded.");
 | ||
|     }
 | ||
|     
 | ||
|     ctx->ts_packet_count++;
 | ||
|     trace("ts+header parsed %"PRId64" packets finished. parsed: %d left: %d header: %d payload: %d(%d+%d)", 
 | ||
|         ctx->ts_packet_count, (int)(p - start), (int)(last - p), header->get_size(), payload->size, payload->pointer_field_size, 
 | ||
|         payload->size - payload->pointer_field_size);
 | ||
|     
 | ||
|     return finish();
 | ||
| }
 | ||
| 
 | ||
| int TSPacket::finish()
 | ||
| {
 | ||
|     return 0;
 | ||
| }
 | ||
|     
 | ||
| TSHeader::TSHeader()
 | ||
| {
 | ||
|     sync_byte = 0;
 | ||
|     transport_error_indicator = 0;
 | ||
|     payload_unit_start_indicator = 0;
 | ||
|     transport_priority = 0;
 | ||
|     pid = TSPidTablePAT;
 | ||
|     transport_scrambling_control = 0;
 | ||
|     adaption_field_control = TSAdaptionTypeReserved;
 | ||
|     continuity_counter = 0;
 | ||
| }
 | ||
| 
 | ||
| TSHeader::~TSHeader()
 | ||
| {
 | ||
| }
 | ||
| 
 | ||
| int TSHeader::get_size()
 | ||
| {
 | ||
|     return 4;
 | ||
| }
 | ||
| 
 | ||
| int TSHeader::demux(TSContext* ctx, TSPacket* pkt, u_int8_t* start, u_int8_t* last, u_int8_t*& p, TSMessage*& pmsg)
 | ||
| {
 | ||
|     int ret = 0;
 | ||
| 
 | ||
|     // ts packet header.
 | ||
|     sync_byte = *p++;
 | ||
|     if (sync_byte != 0x47) {
 | ||
|         trace("ts+sync_bytes invalid sync_bytes: %#x, expect is 0x47", sync_byte);
 | ||
|         return -1;
 | ||
|     }
 | ||
|     
 | ||
|     int16_t _pid = 0;
 | ||
|     char* pp = (char*)&_pid;
 | ||
|     pp[1] = *p++;
 | ||
|     pp[0] = *p++;
 | ||
|     
 | ||
|     transport_error_indicator = (_pid >> 15) & 0x01;
 | ||
|     payload_unit_start_indicator = (_pid >> 14) & 0x01;
 | ||
|     transport_priority = (_pid >> 13) & 0x01;
 | ||
|     _pid &= 0x1FFF;
 | ||
|     
 | ||
|     pid = (TSPidTable)_pid;
 | ||
|     
 | ||
|     continuity_counter = *p++;
 | ||
|     
 | ||
|     transport_scrambling_control = (continuity_counter >> 6) & 0x03;
 | ||
|     int8_t _adaption_field_control = (continuity_counter >> 4) & 0x03;
 | ||
|     adaption_field_control = (TSAdaptionType)_adaption_field_control;
 | ||
|     continuity_counter &= 0x0F;
 | ||
|     
 | ||
|     ctx->push(pid, TSStreamTypeReserved, TSPidTypePAT, continuity_counter);
 | ||
|     
 | ||
|     trace("ts+header sync: %#x error: %d unit_start: %d priotiry: %d pid: %d scrambling: %d adaption: %d counter: %d",
 | ||
|         sync_byte, transport_error_indicator, payload_unit_start_indicator, transport_priority, pid,
 | ||
|         transport_scrambling_control, adaption_field_control, continuity_counter);
 | ||
|         
 | ||
|     return ret;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
| * Annex B Byte stream format, in page 211.
 | ||
| */
 | ||
| class TSH264Codec
 | ||
| {
 | ||
| public:
 | ||
|     u_int8_t* raw_data;
 | ||
|     int size;
 | ||
|     
 | ||
|     TSH264Codec()
 | ||
|     {
 | ||
|         size = 0;
 | ||
|         raw_data = NULL;
 | ||
|     }
 | ||
|     
 | ||
|     u_int8_t at(int index)
 | ||
|     {
 | ||
|         if (index >= size) {
 | ||
|             return 0;
 | ||
|         }
 | ||
|         return raw_data[index];
 | ||
|     }
 | ||
|     
 | ||
|     int parse(TSMessage* msg, char* last, char*& p)
 | ||
|     {
 | ||
|         int ret = 0;
 | ||
|         
 | ||
|         srs_assert(p);
 | ||
|         
 | ||
|         while (next_start_code_prefix(p) != 0x000001) {
 | ||
|             char ch = *p++;
 | ||
|             if (ch != 0x00) {
 | ||
|                 trace("ts+h264 parse msg failed, "
 | ||
|                     "expect 0x00 before start-code. actual is: %#x", (u_int8_t)ch);
 | ||
|                 return -1;
 | ||
|             }
 | ||
|         }
 | ||
|         
 | ||
|         if (p >= last) {
 | ||
|             trace("ts+h264 parse msg finished, no start-code.");
 | ||
|             return ret;
 | ||
|         }
 | ||
|         
 | ||
|         // start_code_prefix_one_3bytes /* equal to 0x000001 */
 | ||
|         p += 3;
 | ||
|         
 | ||
|         if (p < last) {
 | ||
|             raw_data = (u_int8_t*)p;
 | ||
|         }
 | ||
|         while (p < last - 3) {
 | ||
|             if (match_start_code_prefix(p)) {
 | ||
|                 break;
 | ||
|             }
 | ||
|             p++;
 | ||
|         }
 | ||
|         
 | ||
|         if (raw_data) {
 | ||
|             size = (u_int8_t*)p - raw_data;
 | ||
|             if (p == last - 3) {
 | ||
|                 size = (u_int8_t*)last - raw_data;
 | ||
|                 p = last;
 | ||
|             }
 | ||
|         }
 | ||
|         
 | ||
|         trace("ts+h264 parse msg finished");
 | ||
|         return ret;
 | ||
|     }
 | ||
|     
 | ||
|     bool match_start_code_prefix(char*p)
 | ||
|     {
 | ||
|         return p[0] == 0x00 && p[1] == 0x00 && (p[2] == 0x00 || p[2] == 0x01);
 | ||
|     }
 | ||
|     
 | ||
|     int32_t next_start_code_prefix(char* p)
 | ||
|     {
 | ||
|         int32_t value = 0;
 | ||
|         char* pp = (char*)&value;
 | ||
|         
 | ||
|         pp[2] = p[0];
 | ||
|         pp[1] = p[1];
 | ||
|         pp[0] = p[2];
 | ||
| 
 | ||
|         return value;
 | ||
|     }
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
| * Table 35 – Sampling frequency dependent on
 | ||
| * sampling_frequency_index. in page 46.
 | ||
| */
 | ||
| enum TSAacSampleFrequency
 | ||
| {
 | ||
|     TSAacSampleFrequency96000         = 0x00,
 | ||
|     TSAacSampleFrequency88200         = 0x01,
 | ||
|     TSAacSampleFrequency64000         = 0x02,
 | ||
|     TSAacSampleFrequency48000         = 0x03,
 | ||
|     TSAacSampleFrequency44100         = 0x04,
 | ||
|     TSAacSampleFrequency32000         = 0x05,
 | ||
|     TSAacSampleFrequency24000         = 0x06,
 | ||
|     TSAacSampleFrequency22050         = 0x07,
 | ||
|     TSAacSampleFrequency16000         = 0x08,
 | ||
|     TSAacSampleFrequency12000         = 0x09,
 | ||
|     TSAacSampleFrequency11025         = 0x0a,
 | ||
|     TSAacSampleFrequency8000          = 0x0b,
 | ||
|     TSAacSampleFrequencyReserved0     = 0x0c,
 | ||
|     TSAacSampleFrequencyReserved1     = 0x0d,
 | ||
|     TSAacSampleFrequencyReserved2     = 0x0e,
 | ||
|     TSAacSampleFrequencyReserved3     = 0x0f,
 | ||
| };
 | ||
| 
 | ||
| /**
 | ||
| * 6.2 Audio Data Transport Stream, ADTS, in page 26.
 | ||
| */
 | ||
| class TSAacAdts
 | ||
| {
 | ||
| public:
 | ||
|     // adts_fixed_header
 | ||
|     // 2B, 16bits
 | ||
|     int16_t syncword; //12bits
 | ||
|     int8_t ID; //1bit
 | ||
|     int8_t layer; //2bits
 | ||
|     int8_t protection_absent; //1bit
 | ||
|     // 12bits
 | ||
|     int8_t profile; //2bit
 | ||
|     TSAacSampleFrequency sampling_frequency_index; //4bits
 | ||
|     int8_t private_bit; //1bit
 | ||
|     int8_t channel_configuration; //3bits
 | ||
|     int8_t original_or_copy; //1bit
 | ||
|     int8_t home; //1bit
 | ||
|     
 | ||
|     // adts_variable_header
 | ||
|     // 28bits
 | ||
|     int8_t copyright_identification_bit; //1bit
 | ||
|     int8_t copyright_identification_start; //1bit
 | ||
|     int16_t frame_length; //13bits
 | ||
|     int16_t adts_buffer_fullness; //11bits
 | ||
|     int8_t number_of_raw_data_blocks_in_frame; //2bits
 | ||
|     
 | ||
|     u_int8_t* raw_data;
 | ||
|     int size;
 | ||
|     
 | ||
|     TSAacAdts()
 | ||
|     {
 | ||
|         syncword = 0;
 | ||
|         ID = 0;
 | ||
|         layer = 0;
 | ||
|         protection_absent = 0;
 | ||
|         profile = 0;
 | ||
|         sampling_frequency_index = TSAacSampleFrequencyReserved0;
 | ||
|         private_bit = 0;
 | ||
|         channel_configuration = 0;
 | ||
|         original_or_copy = 0;
 | ||
|         home = 0;
 | ||
|         copyright_identification_bit = 0;
 | ||
|         copyright_identification_start = 0;
 | ||
|         frame_length = 0;
 | ||
|         adts_buffer_fullness = 0;
 | ||
|         number_of_raw_data_blocks_in_frame = 0;
 | ||
|         
 | ||
|         size = 0;
 | ||
|         raw_data = NULL;
 | ||
|     }
 | ||
|     
 | ||
|     u_int8_t at(int index)
 | ||
|     {
 | ||
|         if (index >= size) {
 | ||
|             return 0;
 | ||
|         }
 | ||
|         return raw_data[index];
 | ||
|     }
 | ||
|     
 | ||
|     int parse(TSMessage* msg, char*& p)
 | ||
|     {
 | ||
|         int ret = 0;
 | ||
|         
 | ||
|         srs_assert(p);
 | ||
|         
 | ||
|         char* start = p;
 | ||
|         
 | ||
|         // adts_fixed_header
 | ||
|         char* pp = (char*)&syncword;
 | ||
|         pp[1] = *p++;
 | ||
|         pp[0] = *p++;
 | ||
|         
 | ||
|         protection_absent = syncword & 0x01;
 | ||
|         layer = (syncword >> 1) & 0x03;
 | ||
|         ID = (syncword >> 3) & 0x01;
 | ||
|         syncword = (syncword >> 4) & 0x0FFF;
 | ||
|         if (syncword != 0xfff) {
 | ||
|             trace("ts+aac invalid sync word. expect 0xfff, actual %#x", syncword);
 | ||
|             return -1;
 | ||
|         }
 | ||
| 
 | ||
|         // adts_variable_header
 | ||
|         int64_t temp = 0;
 | ||
|         pp = (char*)&temp;
 | ||
|         pp[4] = *p++;
 | ||
|         pp[3] = *p++;
 | ||
|         pp[2] = *p++;
 | ||
|         pp[1] = *p++;
 | ||
|         pp[0] = *p++;
 | ||
|         
 | ||
|         number_of_raw_data_blocks_in_frame = temp & 0x03;
 | ||
|         temp = temp >> 2;
 | ||
| 
 | ||
|         adts_buffer_fullness = temp & 0x7FF;
 | ||
|         temp = temp >> 11;
 | ||
| 
 | ||
|         frame_length = temp & 0x1FFF;
 | ||
|         temp = temp >> 13;
 | ||
|         
 | ||
|         copyright_identification_start = temp & 0x01;
 | ||
|         temp = temp >> 1;
 | ||
|         
 | ||
|         copyright_identification_bit = temp & 0x01;
 | ||
|         temp = temp >> 1;
 | ||
|         
 | ||
|         // adts_fixed_header
 | ||
|         home = temp & 0x01;
 | ||
|         temp = temp >> 1;
 | ||
|         
 | ||
|         original_or_copy = temp & 0x01;
 | ||
|         temp = temp >> 1;
 | ||
|         
 | ||
|         channel_configuration = temp & 0x07;
 | ||
|         temp = temp >> 3;
 | ||
|         
 | ||
|         private_bit = temp & 0x01;
 | ||
|         temp = temp >> 1;
 | ||
|         
 | ||
|         sampling_frequency_index = (TSAacSampleFrequency)(temp & 0x0F);
 | ||
|         temp = temp >> 4;
 | ||
|         
 | ||
|         profile = temp & 0x03;
 | ||
|         temp = temp >> 2;
 | ||
| 
 | ||
|         if (!number_of_raw_data_blocks_in_frame) {
 | ||
|             // adts_error_check
 | ||
|             if (!protection_absent) {
 | ||
|                 // crc_check
 | ||
|                 trace("ts+aac TODO: crc_check.");
 | ||
|             }
 | ||
|             // raw_data_block
 | ||
|             raw_data = (u_int8_t*)p;
 | ||
|             size = frame_length - (p - start);
 | ||
|             p += size;
 | ||
|         } else {
 | ||
|             trace("ts+aac TODO: parse multiple blocks.");
 | ||
|         }
 | ||
|         
 | ||
|         return ret;
 | ||
|     }
 | ||
| };
 | ||
| 
 | ||
| class AacMuxer
 | ||
| {
 | ||
| public:
 | ||
|     int fd;
 | ||
|     const char* file;
 | ||
| 
 | ||
|     AacMuxer()
 | ||
|     {
 | ||
|         file = NULL;
 | ||
|         fd = 0;
 | ||
|     }
 | ||
|     
 | ||
|     virtual ~AacMuxer()
 | ||
|     {
 | ||
|         if (fd > 0) {
 | ||
|             close(fd);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     int open(const char* _file)
 | ||
|     {
 | ||
|         file = _file;
 | ||
|         if ((fd = ::open(file, O_CREAT|O_WRONLY|O_TRUNC, 
 | ||
|             S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH)) < 0 
 | ||
|         ) {
 | ||
|             return -1;
 | ||
|         }
 | ||
|         
 | ||
|         return 0;
 | ||
|     }
 | ||
| 
 | ||
|     int write_audio(char* data, int size)
 | ||
|     {
 | ||
|         if (size > 0 && write(fd, data, size) != size) {
 | ||
|             return -1;
 | ||
|         }
 | ||
|         
 | ||
|         return 0;
 | ||
|     }
 | ||
| 
 | ||
|     int write_video(char* data, int size)
 | ||
|     {
 | ||
|         return 0;
 | ||
|     }
 | ||
| };
 | ||
| 
 | ||
| int consume(TSMessage* msg, AacMuxer* aac_muxer)
 | ||
| {
 | ||
|     int ret = 0;
 | ||
|     
 | ||
|     char* p = msg->packet_data;
 | ||
|     if (!p) {
 | ||
|         trace("ts+aac+h264 ignore empty message.");
 | ||
|         return ret;
 | ||
|     }
 | ||
|     
 | ||
|     char* last = msg->packet_data + msg->packet_data_size;
 | ||
|         
 | ||
|     if (!msg->is_video()) {
 | ||
|         // write AAC raw audio.
 | ||
|         if (aac_muxer && (ret = aac_muxer->write_audio((char*)msg->packet_data, msg->packet_data_size)) != 0) {
 | ||
|             return ret;
 | ||
|         }
 | ||
|         
 | ||
|         // parse AAC audio.
 | ||
|         int64_t dts = -1;
 | ||
|         while (p < last) {
 | ||
|             TSAacAdts aac;
 | ||
|             if ((ret = aac.parse(msg, p)) != 0) {
 | ||
|                 return ret;
 | ||
|             }
 | ||
|             trace("ts+aac audio raw data parsed, size: %d, 0x%02x 0x%02x 0x%02x 0x%02x",
 | ||
|                 aac.size, aac.at(0), aac.at(1), aac.at(2), aac.at(3));
 | ||
|             
 | ||
|             if (dts == -1) {
 | ||
|                 dts = (msg->dts == 0)? msg->pts : msg->dts;
 | ||
|             } else {
 | ||
|                 // see ffmpeg: avpriv_aac_parse_header
 | ||
|                 // rdb = get_bits(gbc, 2);  /* number_of_raw_data_blocks_in_frame */
 | ||
|                 // hdr->samples  = (rdb + 1) * 1024;
 | ||
|                 int samples = (aac.number_of_raw_data_blocks_in_frame + 1) * 1024;
 | ||
|                 static int sample_rates[] = {
 | ||
|                     96000, 88200, 64000, 48000, 44100, 32000,
 | ||
|                     24000, 22050, 16000, 12000, 11025, 8000, 
 | ||
|                     1, 1, 1, 1
 | ||
|                 };
 | ||
|                 int sample_rate = sample_rates[aac.sampling_frequency_index];
 | ||
|                 
 | ||
|                 dts += samples * 90000 / sample_rate;
 | ||
|             }
 | ||
|             
 | ||
|             trace("ts+aac+h264+data %s pts:%"PRId64" dts:%"PRId64" size: %d",
 | ||
|                 (msg->type == TSPidTypeVideo)? "video":"audio", dts, dts, aac.frame_length);
 | ||
|                 
 | ||
|             // TODO: process audio.
 | ||
|         }
 | ||
|     } else {     
 | ||
|         trace("ts+aac+h264+data %s pts:%"PRId64" dts:%"PRId64" size: %d",
 | ||
|             (msg->type == TSPidTypeVideo)? "video":"audio", msg->pts, 
 | ||
|             (msg->dts == 0)? msg->pts : msg->dts, msg->packet_data_size);
 | ||
|            
 | ||
|         // parse H264 video.
 | ||
|         bool first = true;
 | ||
|         while (p < last) {
 | ||
|             TSH264Codec h264;
 | ||
|             if ((ret = h264.parse(msg, last, p)) != 0) {
 | ||
|                 return ret;
 | ||
|             }
 | ||
|             trace("ts+h264 video raw data parsed, size: %d, 0x%02x 0x%02x 0x%02x 0x%02x",
 | ||
|                 h264.size, h264.at(0), h264.at(1), h264.at(2), h264.at(3));
 | ||
|                 
 | ||
|             // first?
 | ||
|             if (!first) {
 | ||
|                 continue;
 | ||
|             }
 | ||
|             first = false;
 | ||
|             
 | ||
|             // TODO: process video.
 | ||
|             
 | ||
|             // directly check the sequence header for test_22m.flv
 | ||
|             if (h264.at(0) == 0x67 && h264.at(1) == 0x00 && h264.at(2) == 0x1f && h264.at(3) == 0xac) {
 | ||
|                 trace("ts+h264 directly find the sequence header for test_22m.flv");
 | ||
|             }
 | ||
|             // 7.3.1 NAL unit syntax, hls-mpeg-ts-iso13818-1.pdf, page 44
 | ||
|             char* pp = (char*)h264.raw_data;
 | ||
|             int8_t nal_unit_type = *pp++;
 | ||
|             int8_t nal_ref_idc = (nal_unit_type >> 5) & 0x03;
 | ||
|             nal_unit_type &= 0x1f;
 | ||
|             
 | ||
|             msg->nal_ref_idc = nal_ref_idc;
 | ||
|             msg->nal_unit_type = nal_unit_type;
 | ||
|             
 | ||
|             if (nal_ref_idc != 0) {
 | ||
|                 trace("ts+h264 got an SPS or PPS.");
 | ||
|             }
 | ||
|             if (nal_unit_type == 7) {
 | ||
|                 trace("ts+h264 got an SPS.");
 | ||
|             } else if (nal_unit_type == 5) {
 | ||
|                 trace("ts+h264 got an Coded slice of an IDR picture.");
 | ||
|             } else if (nal_unit_type == 8) {
 | ||
|                 trace("ts+h264 got an PPS.");
 | ||
|             } else if (nal_unit_type == 9) {
 | ||
|                 trace("ts+h264 got an Picture delimiter.");
 | ||
|                 int8_t pic_type = *pp++;
 | ||
|                 pic_type = (pic_type >> 6) & 0x07;
 | ||
|                 if (pic_type == 0) {
 | ||
|                     trace("ts+h264 got an I picture.");
 | ||
|                 } else if (pic_type == 1) {
 | ||
|                     trace("ts+h264 got an I,P picture.");
 | ||
|                 } else if (pic_type == 2) {
 | ||
|                     trace("ts+h264 got an I,P,B picture.");
 | ||
|                 } else if (pic_type == 3) {
 | ||
|                     trace("ts+h264 got an SI picture.");
 | ||
|                 } else if (pic_type == 4) {
 | ||
|                     trace("ts+h264 got an SI,SP picture.");
 | ||
|                 } else if (pic_type == 5) {
 | ||
|                     trace("ts+h264 got an I,SI picture.");
 | ||
|                 } else if (pic_type == 6) {
 | ||
|                     trace("ts+h264 got an I,SI,P,SP picture.");
 | ||
|                 } else if (pic_type == 7) {
 | ||
|                     trace("ts+h264 got an I,SI,P,SP,B picture.");
 | ||
|                 }
 | ||
|             } else {
 | ||
|                 trace("ts+h264 got an unknown unit type: %d.", nal_unit_type);
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     return ret;
 | ||
| }
 | ||
| 
 | ||
| int main(int argc, char** argv)
 | ||
| {
 | ||
|     const char* file = "livestream-1347.ts";
 | ||
|     const char* output_aac_file = "output.aac";
 | ||
|     if (argc > 2) {
 | ||
|         file = argv[1];
 | ||
|         output_aac_file = argv[2];
 | ||
|     }
 | ||
|     
 | ||
|     int fd = open(file, O_RDONLY);
 | ||
|     AacMuxer aac_muxer;
 | ||
|     
 | ||
|     int ret = 0;
 | ||
|     if ((ret = aac_muxer.open(output_aac_file)) != 0) {
 | ||
|         trace("aac_muxer+open open flv file failed.");
 | ||
|         return ret;
 | ||
|     }
 | ||
|     
 | ||
|     trace("demuxer+read packet count offset T+0  T+1  T+2  T+3  T+x T+L2 T+L1 T+L0");
 | ||
|     
 | ||
|     TSContext ctx;
 | ||
|     for (int i = 0, offset = 0; ; i++) {
 | ||
|         u_int8_t ts_packet[TS_PACKET_SIZE];
 | ||
|         memset(ts_packet, 0, sizeof(ts_packet));
 | ||
|         
 | ||
|         int nread = read(fd, ts_packet, sizeof(ts_packet));
 | ||
|         if (nread == 0) {
 | ||
|             trace("demuxer+read got EOF, read completed, offset: %07d.", offset);
 | ||
|             break;
 | ||
|         }
 | ||
|         if (nread != TS_PACKET_SIZE) {
 | ||
|             trace("demuxer+read error to read ts packet. nread=%d", nread);
 | ||
|             break;
 | ||
|         }
 | ||
|         trace("demuxer+read packet %04d %07d 0x%02x 0x%02x 0x%02x 0x%02x ... 0x%02x 0x%02x 0x%02x", 
 | ||
|             i, offset, ts_packet[0], ts_packet[1], ts_packet[2], ts_packet[3], 
 | ||
|             ts_packet[TS_PACKET_SIZE - 3], ts_packet[TS_PACKET_SIZE - 2], ts_packet[TS_PACKET_SIZE - 1]);
 | ||
|         
 | ||
|         u_int8_t* p = ts_packet;
 | ||
|         u_int8_t* start = ts_packet;
 | ||
|         u_int8_t* last = ts_packet + TS_PACKET_SIZE;
 | ||
|         
 | ||
|         // maybe need to parse multiple times for the PES_packet_length(0) packets.
 | ||
|         while (p == start) {
 | ||
|             TSPacket pkt;
 | ||
|             TSMessage* msg = NULL;
 | ||
|             if ((ret = pkt.demux(&ctx, start, last, p, msg)) != 0) {
 | ||
|                 trace("demuxer+read decode ts packet error. ret=%d", ret);
 | ||
|                 return ret;
 | ||
|             }
 | ||
|             
 | ||
|             offset += nread;
 | ||
|             if (!msg) {
 | ||
|                 continue;
 | ||
|             }
 | ||
|             
 | ||
|             if ((ret = consume(msg, &aac_muxer)) != 0) {
 | ||
|                 trace("demuxer+consume parse and consume message failed. ret=%d", ret);
 | ||
|                 return -1;
 | ||
|             }
 | ||
|             
 | ||
|             int64_t pts = msg->pts;
 | ||
|             int64_t dts = (msg->dts == 0)? msg->pts : msg->dts;
 | ||
|             int64_t pcr = msg->pcr;
 | ||
|             static int64_t last_pcr_dts = 0;
 | ||
|             trace("demuxer+report id=%d, type=%s, size=%d, dts=%d, pts=%d, cts=%d, pcr=%d, dts-pcr=%d, ref=%d, unit=%d, dts(diff-pcr)=%d",
 | ||
|                 (int)ctx.ts_packet_count, (msg->type == TSPidTypeVideo)? "video":"audio", 
 | ||
|                 (int)msg->parsed_packet_size, (int)dts, (int)pts, (int)(pts - dts), (int)pcr, (int)(pcr? dts - pcr : 0),
 | ||
|                 (int)msg->nal_ref_idc, (int)msg->nal_unit_type, (int)(pcr? dts - last_pcr_dts: 0));
 | ||
|             if (pcr > 0) {
 | ||
|                 last_pcr_dts = dts;
 | ||
|             }
 | ||
|             
 | ||
|             srs_freep(msg);
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     close(fd);
 | ||
|     return ret;
 | ||
| }
 | ||
| 
 |