基于RFC3984和GB28181标准的H264 over RTP/PS封装解析实现
一、RTP包解析流程
sequenceDiagramparticipant Receiver as 接收端participant Parser as RTP解析器participant PSParser as PS解析器participant Decoder as H264解码器Receiver->>Parser: 接收RTP包Parser->>Parser: 解析RTP头部Parser->>Parser: 检查负载类型(PT=96/98)Parser->>PSParser: 提取RTP负载(PS数据)PSParser->>PSParser: 解析PS包头PSParser->>PSParser: 解析系统头PSParser->>PSParser: 解析节目映射表PSParser->>PSParser: 提取PES包PSParser->>Decoder: 重组H264 NALU
二、关键数据结构定义
1. RTP头部结构
typedef struct {uint8_t version_p_x_cc; // V=2,P=0,X=0,CC=0uint8_t m_payload_type; // M=0, PT=96(PS)/98(H264)uint16_t sequence_number; // 16位序列号uint32_t timestamp; // 32位时间戳(90kHz)uint32_t ssrc; // 同步源标识
} RtpHeader;
2. PS包头结构
typedef struct {uint8_t start_code[4]; // 0x000001BAuint16_t system_clock_ref_base; // 系统时钟基准uint8_t stuffing_length; // 填充长度(3位)// 其他字段省略
} PsHeader;
三、核心处理流程
1. RTP包接收与解析
void process_rtp_packet(uint8_t *buffer, int len) {RtpHeader *rtp_hdr = (RtpHeader*)buffer;// 验证RTP头部if(rtp_hdr->version_p_x_cc != 0x80) return; // 非标准RTP// 提取PS负载uint8_t *ps_payload = buffer + 12; // 跳过12字节RTP头int ps_len = len - 12;// 处理PS数据parse_ps_stream(ps_payload, ps_len);
}
2. PS流解析与重组
void parse_ps_stream(uint8_t *data, int len) {while(len >= 4) {if(memcmp(data, "\x00\x00\x01\xBA", 4) == 0) {// 解析PS头PsHeader *ps_hdr = (PsHeader*)data;data += sizeof(PsHeader);len -= sizeof(PesHeader);// 处理系统头parse_system_header(data);// 处理节目映射表parse_program_map_table(data);} else if(memcmp(data, "\x00\x00\x01\xBB", 4) == 0) {// 跳过系统标题data += 12;len -= 12;} else {// 处理PES包process_pes_packet(data, len);break;}}
}
3. H264 NALU重组
void process_pes_packet(uint8_t *data, int len) {PESHeader *pes_hdr = (PESHeader*)data;if(pes_hdr->stream_id == 0xE0) { // 视频流uint8_t *nal_data = data + sizeof(PESHeader);int nal_len = len - sizeof(PESHeader);// 处理H264 NALUwhile(nal_len > 4) {if(memcmp(nal_data, "\x00\x00\x00\x01", 4) == 0) {// 提取NALU头uint8_t nalu_hdr = nal_data[4];uint8_t nalu_type = nalu_hdr & 0x1F;// 处理分片if(nalu_type == 28) { // FU-Ahandle_fu_a_fragment(nal_data);} else {// 完整NALU处理process_nalu(nal_data + 5, nal_len - 5);}}nal_data++;nal_len--;}}
}
四、关键算法实现
1. FU-A分片重组
typedef struct {uint8_t nalu_type;uint8_t nri;bool start_bit;bool end_bit;std::vector<uint8_t> payload;
} FuFragment;void handle_fu_a_fragment(uint8_t *data) {FuFragment frag;frag.nalu_type = (data[0] >> 3) & 0x1F;frag.nri = (data[0] >> 1) & 0x03;frag.start_bit = (data[1] & 0x80) ? true : false;frag.end_bit = (data[1] & 0x40) ? true : false;if(frag.start_bit) {current_nalu = frag;} else {current_nalu.payload.insert(current_nalu.payload.end(), data+2, data+1400);}if(frag.end_bit) {// 完成重组process_nalu(current_nalu.payload.data(), current_nalu.payload.size());current_nalu = {};}
}
2. NALU头解析
void parse_nalu_header(uint8_t hdr) {uint8_t forbidden_zero_bit = (hdr >> 7) & 0x01;uint8_t nri = (hdr >> 5) & 0x03;uint8_t nalu_type = hdr & 0x1F;// 类型判断switch(nalu_type) {case 7: // SPScase 8: // PPScase 5: // IDRcase 1: // 非IDRprocess_nalu_data(hdr, ...);break;default:// 处理其他类型break;}
}
五、时间戳同步机制
uint32_t last_video_ts = 0;
uint32_t audio_ts = 0;void sync_timestamps(uint32_t rtp_ts) {static uint32_t base_ts = 0;if(base_ts == 0) {base_ts = rtp_ts;}current_ts = base_ts + (rtp_ts - base_ts) * 1000 / 90; // 转换为毫秒// 音视频同步if(abs(current_ts - audio_ts) > 100) {adjust_audio_clock(current_ts);}
}
参考代码 接收解析封装H264为PS数据的RTP包 youwenfan.com/contentcnb/72715.html
六、错误恢复策略
-
丢包检测:
if(rtp_hdr->sequence_number != expected_sn) {handle_packet_loss(expected_sn, rtp_hdr->sequence_number);expected_sn = rtp_hdr->sequence_number + 1; }
-
关键帧请求:
void on_packet_loss(int lost_count) {if(lost_count > 5) {send_rtcp_pli(); // 请求PLI} }
七、性能优化建议
-
零拷贝技术:
// 使用内存池管理NALU缓冲区 std::vector<uint8_t> nal_buffer(MTU_SIZE);
-
SIMD加速:
#pragma unroll(4) for(int i=0; i<16; i++) {__m128i data = _mm_loadu_si128((__m128i*)(buffer+i));// SIMD处理 }
-
多线程处理:
// 分离接收线程与解析线程 std::thread receiver_thread(receive_rtp); std::thread parser_thread(parse_ps_stream);
八、完整代码结构
h264_rtp_demuxer/
├── src/
│ ├── rtp_parser.cpp # RTP包解析
│ ├── ps_demuxer.cpp # PS流解析
│ ├── h264_processor.cpp # H264数据处理
│ └── sync_engine.cpp # 时间戳同步
├── include/
│ ├── rtp_header.h
│ ├── ps_header.h
│ └── h264_types.h
└── tests/└── test_demuxer.cpp # 单元测试
九、调试工具推荐
-
Wireshark:抓包分析RTP负载类型和PS结构
-
FFmpeg:验证解码结果
ffplay -protocol_whitelist "file,udp,rtp" -i input.ps
-
H264 Analyzer:查看NALU类型分布
该方案通过严格遵循RFC3984和GB28181标准,实现了H264在RTP/PS封装下的可靠传输。实际部署时需根据网络环境调整MTU大小(通常1400-1500字节)和缓冲区策略。