当前位置: 首页 > news >正文

接收解析封装H264为PS数据的RTP包

基于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

六、错误恢复策略

  1. 丢包检测

    if(rtp_hdr->sequence_number != expected_sn) {handle_packet_loss(expected_sn, rtp_hdr->sequence_number);expected_sn = rtp_hdr->sequence_number + 1;
    }
    
  2. 关键帧请求

    void on_packet_loss(int lost_count) {if(lost_count > 5) {send_rtcp_pli(); // 请求PLI}
    }
    

七、性能优化建议

  1. 零拷贝技术

    // 使用内存池管理NALU缓冲区
    std::vector<uint8_t> nal_buffer(MTU_SIZE);
    
  2. SIMD加速

    #pragma unroll(4)
    for(int i=0; i<16; i++) {__m128i data = _mm_loadu_si128((__m128i*)(buffer+i));// SIMD处理
    }
    
  3. 多线程处理

    // 分离接收线程与解析线程
    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     # 单元测试

九、调试工具推荐

  1. Wireshark:抓包分析RTP负载类型和PS结构

  2. FFmpeg:验证解码结果

    ffplay -protocol_whitelist "file,udp,rtp" -i input.ps
    
  3. H264 Analyzer:查看NALU类型分布

该方案通过严格遵循RFC3984和GB28181标准,实现了H264在RTP/PS封装下的可靠传输。实际部署时需根据网络环境调整MTU大小(通常1400-1500字节)和缓冲区策略。

http://www.vanclimg.com/news/307.html

相关文章:

  • Zabbix优化参考1
  • hi
  • 框幅式高光谱文献数据库,换“新”看!科研效率Up Up!
  • vxe-table 实现服务端筛选、分页筛选
  • 函数参数为字符串类型时,默认值设为NULL会报错
  • 中电金信:源启研发协同一体化平台、源启混沌工程平台通过信通院可信云最高级评估
  • LGP9310 [EGTS 2021] Luna likes Love 学习笔记
  • 使用Amazon Q和MCP优化深度学习环境
  • Linux 系统硬盘命名规则详细解析
  • 【LeetCode 160】算法:相交链表 —— 双指针法和数学法
  • cgroup机制
  • ls | tee 1.txt 如何拿到ls的返回值$?
  • 深入浅出:Clang中的控制流完整性(CFI)技术解析
  • 工业互联网甄选联盟会员组织正式成立,合作共赢
  • VK16K33AQ QNF28小体积封装大电流LED驱动电子烟LED屏显方案
  • HelloWorld
  • 颠覆性应用指南:EtherCAT转PROFINET网关的工业场景核爆方案大全
  • 如何将 Markdown格式文章快速发布到微信公众号.240516
  • Maven 镜像配置文件 maven-settings.xml
  • 图论
  • 开源能源管理系统:数字化时代能源安全与效能提升的核心引擎
  • 四.分支语句的简单应用
  • 使用AnythingLLM本地化投喂文件,简单三步快速本地化部署DeepSeek满血版看这篇!.250304
  • 循环for、while
  • 最小斯坦纳树
  • 浏览器跨标签页通信
  • 以太坊开发指南:SendTransaction vs CallContract 的区别与错误处理实践 - 若
  • Ntpdate系统时间同步
  • oracle 自增id
  • 接地气的软件开发流程.240618