1. 国标GB28181摄像头模拟

实现了将h264封装ps流并打包rtp推流到服务器
先抛开标题, 整体的GBServer需要交互Sip和流媒体(ZLMediaKit实现)两部分
目前先了解Sip信令部分

ZLMediaKit获取实时流API
127.0.0.1/index/api/getMediaList

1.1 Sip信令简介

详细协议介绍见此

  • Sip(session initiation protocol) : 由互联网工程任务组制定的, 用于多方多媒体通信的框架协议
    是一个基于文本的应用层控制协议, 独立于底层传输协议, 用于建立、修改和终止IP网络上的双方或多方多媒体会话

  • 信令 : 控制电路的信号,是终端和终端、终端和网络之间传递的一种消息,专门用来控制电路,建立、管理、删除连接, 以使用户能够正常通过这些连接进行通信

  • 事务 : 客户和服务器之间的操作从第 1 个请求至最终响应为止的所有消息构成一个SIP事务
    一个正常的呼叫一般包含三个事务
    其中,呼叫启动包含两个操作请求:邀请(Invite)和证实(ACK),前者需要回送响应, 后者只是证实已收到最终响应, 不需要回送响应
    呼叫终结包含一个操作请求:Bye

  • SIP URL (Uniform Resource Locators) : 一般结构为
    SIP: 用户名: 口令 @ 主机: 端口;传送参数: 用户参数; 方法参数; 生存期参数; 服务器地址参数? 头部名=头部值

Sip信令交互过程

  1. 摄像头(客户端)像服务段发送注册请求

  2. 服务器回复 401 (未授权, 未携带密码)

  3. 摄像头收到 401 第二次发起注册, 此次注册携带密码(密钥)

  4. 服务器返回注册成功(200 OK)

  5. 服务器像摄像头发起 invite 请求,告诉摄像机我要接受流媒体,并包含SDP(解释自己能处理的流)

  6. 摄像机收到 invite 后回复 100(Trying)

  7. 摄像机传输message(必要信息,ip port…)

  8. 摄像机回复状态码 101(Establishment)

  9. 摄像机回复状态码 200, 并且包含了 SDP(摄像机告诉服务器, 我的摄像头有哪些流)

  10. 服务器收到 200

  11. 服务器收到SDP后, 向摄像机发送 ACK 确认

  12. 摄像机开始推流

  13. 摄像机发送 BYE

  14. 服务器回复 200

1.2 PS流

PS流封装标准如下,其详细信息暂不去了解

GB28181要求的RTP流格式

I帧的PS流格式,这里需要注意的是SPS、PPS之前要加上PES头部
如下图所示, 其中绿色部分就是我们拿到的H.264裸流数据, 须将它拆分成三段并在前面加上PES头部
这一点在GB28181标准中没有细说,需要通过分析海康IPC流才能看出

一般情况下IDR帧很大, 超过了RTP的负载长度限制(1400字节), 所以上面这一个I帧要拆分成若干包RTP分多次发送
第一包的结构如上图所示, 第二包以后RTP的结构就简单多了, 如下

上面提到的是I帧的情况,P/B帧的帧格式较为简单了, 因为它既没有SYS、PSM, 也没有SPS、PPS

P/B帧大小一般不超过1400字节, 如果超过1400字节, 也需分成多包RTP数据进行传输, 超出1400部分的第二包RTP结构

头部信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//RTPHeader
#define RTP_HDR_LEN 12
static int gb28181_make_rtp_header(char *pData, int marker_flag, unsigned short cseq, long long curpts, unsigned int ssrc)
{
bits_buffer_s bitsBuffer;
if (pData == NULL)
return -1;
bitsBuffer.i_size = RTP_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);

memset(bitsBuffer.p_data, 0, RTP_HDR_SIZE);
bits_write(&bitsBuffer, 2, RTP_VERSION); // rtp version 版本号,固定为2
bits_write(&bitsBuffer, 1, 0); // rtp padding
bits_write(&bitsBuffer, 1, 0); // rtp extension
bits_write(&bitsBuffer, 4, 0); // rtp CSRC count
bits_write(&bitsBuffer, 1, (marker_flag)); // rtp marker 结束标志位,一帧图像的最后一包RTP置1
bits_write(&bitsBuffer, 7, 96); // rtp payload type,96代表PS
bits_write(&bitsBuffer, 16, (cseq)); // rtp sequence
bits_write(&bitsBuffer, 32, (curpts)); // rtp timestamp
bits_write(&bitsBuffer, 32, (ssrc)); // rtp SSRC
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//PSH(PS Header)
#define PS_HDR_LEN 14
static int gb28181_make_ps_header(char *pData, unsigned long long s64Scr)
{
unsigned long long lScrExt = (s64Scr) % 100;
s64Scr = s64Scr / 100;
bits_buffer_s bitsBuffer;
bitsBuffer.i_size = PS_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);
memset(bitsBuffer.p_data, 0, PS_HDR_LEN);
bits_write(&bitsBuffer, 32, 0x000001BA); //start codes 起始码
bits_write(&bitsBuffer, 2, 1); //marker bits '01b'
bits_write(&bitsBuffer, 3, (s64Scr>>30)&0x07); //System clock [32..30]
bits_write(&bitsBuffer, 1, 1); //marker bit
bits_write(&bitsBuffer, 15, (s64Scr>>15)&0x7FFF); //System clock [29..15]
bits_write(&bitsBuffer, 1, 1); //marker bit
bits_write(&bitsBuffer, 15, s64Scr & 0x7fff); //System clock [14..0]
bits_write(&bitsBuffer, 1, 1); //marker bit
bits_write(&bitsBuffer, 9, 0); //SCR extension
bits_write(&bitsBuffer, 1, 1); //marker bit
bits_write(&bitsBuffer, 22, (255)&0x3fffff); //bit rate(n units of 50 bytes per second.)
bits_write(&bitsBuffer, 2, 3); //marker bits '11'
bits_write(&bitsBuffer, 5, 0x1f); //reserved(reserved for future use)
bits_write(&bitsBuffer, 3, 0); //stuffing length
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//SYS,它包含了流类型信息,比如音频还是视频、视频编码格式
#define SYS_HDR_LEN 18
static int gb28181_make_sys_header(char *pData)
{
bits_buffer_s bitsBuffer;
bitsBuffer.i_size = SYS_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);
memset(bitsBuffer.p_data, 0, SYS_HDR_LEN);
//system header
bits_write( &bitsBuffer, 32, 0x000001BB); //start code
bits_write( &bitsBuffer, 16, SYS_HDR_LEN-6); // 减6,是因为start code加上length这两位,占了6个字节
bits_write( &bitsBuffer, 1, 1); //marker_bit
bits_write( &bitsBuffer, 22, 50000); //rate_bound
bits_write( &bitsBuffer, 1, 1); //marker_bit
bits_write( &bitsBuffer, 6, 1); //audio_bound
bits_write( &bitsBuffer, 1, 0); //fixed_flag
bits_write( &bitsBuffer, 1, 1); //CSPS_flag
bits_write( &bitsBuffer, 1, 1); //system_audio_lock_flag
bits_write( &bitsBuffer, 1, 1); //system_video_lock_flag
bits_write( &bitsBuffer, 1, 1); //marker_bit
bits_write( &bitsBuffer, 5, 1); //video_bound
bits_write( &bitsBuffer, 1, 0); //dif from mpeg1
bits_write( &bitsBuffer, 7, 0x7F); //reserver
//audio stream bound
bits_write( &bitsBuffer, 8, 0xC0); //stream_id 音频的流id
bits_write( &bitsBuffer, 2, 3); //marker_bit
bits_write( &bitsBuffer, 1, 0); //PSTD_buffer_bound_scale
bits_write( &bitsBuffer, 13, 512); //PSTD_buffer_size_bound
//video stream bound
bits_write( &bitsBuffer, 8, 0xE0); //stream_id 视频的流id
bits_write( &bitsBuffer, 2, 3); //marker_bit
bits_write( &bitsBuffer, 1, 1); //PSTD_buffer_bound_scale
bits_write( &bitsBuffer, 13, 2048); //PSTD_buffer_size_bound
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//PSM,这里面记录了媒体信息,比如音视频的编码格式:
static int gb28181_make_psm_header(char *pData)
{

bits_buffer_s bitsBuffer;
bitsBuffer.i_size = PSM_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);
memset(bitsBuffer.p_data, 0, PSM_HDR_LEN); //24Bytes
bits_write(&bitsBuffer, 24,0x000001); //start code
bits_write(&bitsBuffer, 8, 0xBC); //map stream id
bits_write(&bitsBuffer, 16,18); //program stream map length
bits_write(&bitsBuffer, 1, 1); //current next indicator
bits_write(&bitsBuffer, 2, 3); //reserved
bits_write(&bitsBuffer, 5, 0); //program stream map version
bits_write(&bitsBuffer, 7, 0x7F); //reserved
bits_write(&bitsBuffer, 1, 1); //marker bit
bits_write(&bitsBuffer, 16,0); //programe stream info length
bits_write(&bitsBuffer, 16, 8); //elementary stream map length is
//video
bits_write(&bitsBuffer, 8, 0x1B); //stream_type 视频编码格式H.264
bits_write(&bitsBuffer, 8, 0xE0); //elementary_stream_id
bits_write(&bitsBuffer, 16, 0); //elementary_stream_info_length
//audio
bits_write(&bitsBuffer, 8, 0x90); //stream_type 音频编码格式G711
bits_write(&bitsBuffer, 8, 0xC0); //elementary_stream_id
bits_write(&bitsBuffer, 16, 0); //elementary_stream_info_length is
//crc (2e b9 0f 3d)
bits_write(&bitsBuffer, 8, 0x45); //crc (24~31) bits
bits_write(&bitsBuffer, 8, 0xBD); //crc (16~23) bits
bits_write(&bitsBuffer, 8, 0xDC); //crc (8~15) bits
bits_write(&bitsBuffer, 8, 0xF4); //crc (0~7) bits
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//PES头部,它记录了帧的时间戳,DTS可以不填,如果填写要和PTS保持一致,且同一帧数据的PTS要也要一样(即SPS、PPS、IDR的PES要一致)
#define PES_HDR_LEN 19
static int gb28181_make_pes_header(char *pData, int stream_id, int payload_len, unsigned long long pts, unsigned long long dts)
{

bits_buffer_s bitsBuffer;
bitsBuffer.i_size = PES_HDR_LEN;
bitsBuffer.i_data = 0;
bitsBuffer.i_mask = 0x80;
bitsBuffer.p_data = (unsigned char *)(pData);
memset(bitsBuffer.p_data, 0, PES_HDR_LEN);
//system header
bits_write( &bitsBuffer, 24,0x000001); //start code
bits_write( &bitsBuffer, 8, (stream_id)); //streamID
bits_write( &bitsBuffer, 16,(payload_len)+13); //packet_len pes剩余头部以及后面的es长度之和,比如SPS长度+13
bits_write( &bitsBuffer, 2, 2 ); //'10'
bits_write( &bitsBuffer, 2, 0 ); //scrambling_control
bits_write( &bitsBuffer, 1, 1 ); //priority
bits_write( &bitsBuffer, 1, 1 ); //data_alignment_indicator
bits_write( &bitsBuffer, 1, 0 ); //copyright
bits_write( &bitsBuffer, 1, 0 ); //original_or_copy
bits_write( &bitsBuffer, 1, 1 ); //PTS_flag 是否有PTS
bits_write( &bitsBuffer, 1, 1 ); //DTS_flag 是否有DTS信息
bits_write( &bitsBuffer, 1, 0 ); //ESCR_flag
bits_write( &bitsBuffer, 1, 0 ); //ES_rate_flag
bits_write( &bitsBuffer, 1, 0 ); //DSM_trick_mode_flag
bits_write( &bitsBuffer, 1, 0 ); //additional_copy_info_flag
bits_write( &bitsBuffer, 1, 0 ); //PES_CRC_flag
bits_write( &bitsBuffer, 1, 0 ); //PES_extension_flag
bits_write( &bitsBuffer, 8, 10); //header_data_length

//PTS,DTS
bits_write( &bitsBuffer, 4, 3 ); //'0011'
bits_write( &bitsBuffer, 3, ((pts)>>30)&0x07 ); //PTS[32..30]
bits_write( &bitsBuffer, 1, 1 );
bits_write( &bitsBuffer, 15,((pts)>>15)&0x7FFF); //PTS[29..15]
bits_write( &bitsBuffer, 1, 1 );
bits_write( &bitsBuffer, 15,(pts)&0x7FFF); //PTS[14..0]
bits_write( &bitsBuffer, 1, 1 );
bits_write( &bitsBuffer, 4, 1 ); //'0001'
bits_write( &bitsBuffer, 3, ((dts)>>30)&0x07 ); //DTS[32..30]
bits_write( &bitsBuffer, 1, 1 );
bits_write( &bitsBuffer, 15,((dts)>>15)&0x7FFF); //DTS[29..15]
bits_write( &bitsBuffer, 1, 1 );
bits_write( &bitsBuffer, 15,(dts)&0x7FFF); //DTS[14..0]
bits_write( &bitsBuffer, 1, 1 );
return 0;
}

时间戳的比特位结构,下图表示的比较清晰

难度太高,暂时搁置