当用户自建的国标设备出现接入异常情况时,在确认设备接入和功能实现符合GB/T 28181-2016协议规范定义的前提下,可以参考本文从信令和流媒体的角度排查问题。
信令接入常见问题
IPC设备、NVR主设备上线失败
检查自建国标设备配置的服务器IP和端口号是否正确。
抓包检查设备是否与服务器完成两阶段注册交互:
设备向服务器发送首次
REGISTER
报文。服务器向设备回复
401 Unauthorized
报文,并携带用于鉴权的WWW-Authenticate
字段。设备向服务器发送二次
REGISTER
报文,相比首次REGISTER
报文,携带用于鉴权的Authorization
字段。服务器向设备回复
200 OK
报文:如果发现接收不到来自服务器的报文,检查网络联通性。
如果发现服务器始终回复
401 Unauthorized
报文,检查密码填写是否正确。二次REGISTER
报文是否包含用于鉴权的Authorization
字段。如果发现服务器始终回复
400 Bad Request
报文,检查服务器或设备国标ID填写是否正确。
NVR视频通道(子设备)上线失败
检查自建国标设备的视频通道(子设备)配置是否正确。
抓包检查设备是否与服务器完成查询子设备目录的交互:
服务器向设备发送
MESSAGE
报文,报文体包含<Query>,Catalog
,查询子设备目录。设备向服务器回复
200 OK
报文。设备向服务器发送
MESSAGE
报文,报文体包含<Response>,Catalog
,回复子设备目录(可能多条)。服务器向设备回复
200 OK
报文(如果多条,则一一对应):如果发现接收不到来自服务器的报文,请检查网络联通性。
如果交互均正常,检查设备发送
MESSAGE
报文速度是否过慢。设备发送首条
MESSAGE
报文,距离服务发送MESSAGE
报文时间间隔应当低于1000毫秒。设备发送一条
MESSAGE
报文,距离设备发送上一条MESSAGE
报文时间间隔应当低于1000毫秒。设备发送所有
MESSAGE
报文总时间应当低于3000毫秒。
获取设备录像文件失败
检查自建国标设备在查询时段内是否存在录像文件。
抓包检查设备是否与服务器完成查询录像文件的交互:
服务器向设备发送
MESSAGE
报文,报文体包含<Query>,RecordInfo
,查询录像文件。设备向服务器回复
200 OK
报文。设备向服务器发送 MESSAGE 报文,报文体包含
<Response>,RecordInfo
,回复录像文件(可能多条)。服务器向设备回复
200 OK
报文(如果多条,则一一对应):如果发现接收不到来自服务器的报文,检查网络联通性。
如果交互均正常,检查设备发送
MESSAGE
报文速度是否过慢。设备发送首条
MESSAGE
报文,距离服务端发送MESSAGE
报文的时间间隔应当低于500毫秒。设备发送一条
MESSAGE
报文,距离设备发送上一条MESSAGE
报文的时间间隔应当低于500毫秒。设备发送所有
MESSAGE
报文的总时间应当低于2000毫秒。
设备直播、点播失败
抓包检查设备是否与服务器完成直播、点播的信令交互:
服务器向设备发送
INVITE
报文,报文体包含SDP
消息内容。设备向服务器回复
100 Trying
报文(中间报文,可能无)。设备向服务器回复
101 Dialog Establishment
报文(中间报文,可能无)。设备向服务器回复
200 OK
报文,报文体包含SDP
消息内容。服务器向设备回复
ACK
报文:如果发现接收不到来自服务器的报文,检查网络联通性。
如果发现设备回复
503 Service Unavailable
,检查设备是否处于可用状态,或重启设备。如果发现设备回复
486 Busy Here
,检查设备是否被邀请同时推主码流和子码流。部分国标设备不支持同时推多路直播流,且国标设备不区分主、子码流,邀请同时推主码流和子码流会导致异常。
如果发现服务不回复
ACK
,检查设备回复200 OK
是否速度过慢,设备回复200 OK
,距离服务发送INVITE
报文时间间隔应当低于2000毫秒。
流媒体接入常见问题
设备直播、点播失败
根据信令接入常见问题的设备直播、点播失败,检查信令交互阶段是否有异常。
物联网智能视频服务仅支持
RTP/PS/H.264(H.265)+AAC
的流媒体数据封包格式和TCP
流媒体传输模式,即视频编码格式采用H.264
或H.265
,音频编码格式采用AAC
,音视频流封装格式采用PS
,实时传输协议采用RTP
,收流传输层协议采用TCP
。需逐一检查设备实现是否符合协议标准。GB/T 28181-2016定义TCP收流的RTP封装需要符合RFC 4571标准,即
RTP over TCP
相比RTP over UDP
的封包格式,在RTP
头部前有2字节的标识信息:length
用于标识后续RTP
报文的长度。以上信息的缺失或错误会导致RTP解码失败。RTP
包外格式信息:0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | length | RTP Packet... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
检查
RTP
头部中填充的SSRC
字段,是否与信令交互中INVITE
报文中SDP
报文体的y
字段标识的SSRC
相等。物联网智能视频服务使用
SSRC
字段进行流鉴权,SSRC
字段来源有误,会导致流鉴权失败。说明如果
SSRC
填写正确,仍然鉴权失败,请切换大小端写入方式重试。RTP
头部格式信息:0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P|X| CC |M| PT | sequence number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | synchronization source (SSRC) identifier | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | contributing source (CSRC) identifiers... | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
检查推流的首个关键帧是否为包含
sps
、pps
等信息的sequence header
帧。获取不到sequence header
关键帧,会导致后续收流失败。检查
RTP
载荷的视频数据格式是否正确:使用抓包工具抓取设备端向服务器发送的流媒体数据包。
获取
RTP
裸流数据:可以使用Wireshark打开抓包获得的pcap/pcapng文件,跟踪该RTP
流数据的TCP流,用Raw格式展示抓取的RTP
裸流数据。保存为
rtp.raw
文件。获取
PS
流数据。使用下面的脚本将rtp.raw
解析为demux.ps
文件。import struct def parse_rtp_packet(rtp_packet): """ 解析RTP报文,并返回序列号、SSRC和有效载荷。 """ # 解析固定的RTP头部,长度为12字节 rtp_header_format = '!BBHII' rtp_header_length = struct.calcsize(rtp_header_format) rtp_header = rtp_packet[:rtp_header_length] _, _, sequence_number, _, ssrc = struct.unpack(rtp_header_format, rtp_header) payload = rtp_packet[rtp_header_length:] # 跳过固定头部,获取载荷 return sequence_number, ssrc, payload def parse_rtp_stream(file_path, output_path): """ 解析RTP流并将载荷数据保存到文件中。 """ with open(file_path, 'rb') as f, open(output_path, 'wb') as out: while True: # 读取2字节的长度字段 length_data = f.read(2) if not length_data: break # 文件结束 # 解析长度字段,获取RTP报文长度 (rtp_packet_length,) = struct.unpack('!H', length_data) # 读取RTP报文 rtp_packet = f.read(rtp_packet_length) if len(rtp_packet) != rtp_packet_length: print("Warning: RTP packet length mismatch. Expected: {}, got: {}".format(rtp_packet_length, len(rtp_packet))) break # 数据长度不符,可能是文件损坏或读取错误 # 解析RTP报文 sequence_number, ssrc, payload = parse_rtp_packet(rtp_packet) print("Sequence: {}, SSRC: {}".format(sequence_number, ssrc)) # 将载荷写入输出文件 out.write(payload) # 文件路径和输出路径 rtp_file_path = 'rtp.raw' output_payload_path = 'demux.ps' # 解析RTP流并保存载荷 parse_rtp_stream(rtp_file_path, output_payload_path)
RTP
裸流数据包含多个RTP
数据包,数据包的SSRC相同。如果发现数据包的SSRC不相同,检查RTP
包外格式信息和RTP
头部格式信息。使用FFplay工具播放
demux.ps
文件,观察是否符合预期。