mirror of
https://github.com/ossrs/srs.git
synced 2025-02-12 19:31:53 +00:00
2273 lines
63 KiB
C++
2273 lines
63 KiB
C++
/*
|
||
The MIT License (MIT)
|
||
|
||
Copyright (c) 2013-2016 SRS(ossrs)
|
||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||
this software and associated documentation files (the "Software"), to deal in
|
||
the Software without restriction, including without limitation the rights to
|
||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||
subject to the following conditions:
|
||
|
||
The above copyright notice and this permission notice shall be included in all
|
||
copies or substantial portions of the Software.
|
||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
*/
|
||
/**
|
||
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;
|
||
}
|
||
|