接收解析封装H264为PS数据的RTP包
基于RFC3984和GB28181标准的H264 over RTP/PS封装解析实现
一、RTP包解析流程
sequenceDiagram
    participant 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=0
    uint8_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];     // 0x000001BA
    uint16_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 NALU
        while(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-A
                    handle_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:  // SPS
        case 8:  // PPS
        case 5:  // IDR
        case 1:  // 非IDR
            process_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字节)和缓冲区策略。
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号