// // 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 #include #include #include #include #include #include #include #include #include #include #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 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 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::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::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; }