跳过导航,直接进入主要内容跳过导航,直接进入主要内容跳过导航,直接进入主要内容

PS 封装与 RTP:独立使用

媒体路径可以脱离 SIP 服务器单独使用:把 H.264/H.265 NAL 封成 MPEG-2 节目流、打包 RTP,或把 PS 流解析回 NAL。

安装

cargo add gb28181-rs@0.12.0

封装

use gb28181_rs::ps::{mux_h264_to_ps, mux_h265_to_ps};

// NAL 负载不含 Annex-B 起始码;PTS/DTS 为 90 kHz 时钟值。
let ps: Vec<u8> = mux_h264_to_ps(&[&sps, &pps, &idr], true, 90_000, 90_000);
let ps265: Vec<u8> = mux_h265_to_ps(&[&vps, &sps, &pps, &idr], true, pts, dts);

关键帧输出 GB28181 解复用器要求的完整 pack header + 节目流映射 (PSM)前导,再接 PES 负载;非关键帧只有 pack header + PES。超出 PES 长度字段合理范围的访问单元会切成多个有界 PES 包。

解析

use gb28181_rs::{parse_ps_to_h264, parse_ps_to_nal_units, parse_ps_pack_header, parse_pes_packet};

let frames: Vec<Vec<u8>> = parse_ps_to_h264(&ps)?;     // Annex-B 帧(起始码已还原)
let nalus: Vec<Vec<u8>> = parse_ps_to_nal_units(&ps)?; // 裸 NAL 负载

RTP 打包

use std::net::SocketAddr;
use gb28181_rs::RtpPusher;

let mut pusher = RtpPusher::new(
    SocketAddr::from(([192, 0, 2, 10], 30000)), // 平台媒体端口
    0x1234_5678,                                // INVITE SDP 里的 SSRC
    96,                                         // 动态负载类型(PS)
);
let packet = pusher.build_rtp_packet(&ps);      // 超长 PS 自动分片
pusher.increment_timestamp(3600);               // 帧间自行推进 RTP 时钟

字节级保证

PS 输出是线上契约:本库与 Go 孪生库(gb28181-go)必须产出逐字 节相同的流。金串测试把小关键帧、P 帧、>64 KB 访问单元四段 PES 切分 都钉成十六进制常量。无论改哪个库的封装路径,这些测试都会先红。

cargo run --example ps_mux
# 离线:封装 → 解回往返与超大帧切分。
可访问性增强功能已加载,支持焦点管理、键盘导航和屏幕阅读器