Skip to content

Commit ba1ec9c

Browse files
committed
基础版本,解析ps内存零拷贝
0 parents  commit ba1ec9c

File tree

9 files changed

+1029
-0
lines changed

9 files changed

+1029
-0
lines changed

README.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# streams
2+
3+
轻便的gb28181协议中的rtp+ps格式视频流的封装和解析
4+
5+
## packet
6+
7+
packet实现ps的相关封装和解析,
8+
9+
### example/dec
10+
11+
截取了部分ps封装码流,通过`RtpParsePacket`进行解析,验证
12+
13+
```golang
14+
// ps + system + map + sps + pps
15+
0x00, 0x00, 0x01, 0xba, 0x5e, 0xee, 0x05, 0x55, 0x74, 0x01, 0x0e, 0xb3, 0x37, 0xfe, 0xff, 0xff,
16+
0x00, 0xeb, 0xc1, 0x62, 0x00, 0x00, 0x01, 0xbb, 0x00, 0x12, 0x87, 0x59, 0x9b, 0x04, 0xe1, 0x7f,
17+
0xe0, 0xe0, 0x80, 0xc0, 0xc0, 0x08, 0xbd, 0xe0, 0x80, 0xbf, 0xe0, 0x80, 0x00, 0x00, 0x01, 0xbc,
18+
0x00, 0x5a, 0xfa, 0xff, 0x00, 0x24, 0x40, 0x0e, 0x48, 0x4b, 0x01, 0x00, 0x13, 0x37, 0xc7, 0x3e,
19+
0xa7, 0x9f, 0x00, 0xff, 0xff, 0xff, 0x41, 0x12, 0x48, 0x4b, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
20+
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x2c, 0x1b, 0xe0, 0x00, 0x28,
21+
0x42, 0x0e, 0x07, 0x10, 0x10, 0xea, 0x07, 0x80, 0x04, 0x38, 0x11, 0x10, 0xc0, 0x00, 0x1c, 0x21,
22+
0x44, 0x0a, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2a, 0x0a, 0x7f, 0xff,
23+
0x00, 0x00, 0x07, 0x08, 0x1f, 0xfe, 0xf0, 0x87, 0x0c, 0x9a, 0x36, 0x23, 0x00, 0x00, 0x01, 0xe0,
24+
0x00, 0x26, 0x8c, 0x80, 0x07, 0x27, 0xbb, 0x81, 0x55, 0x5d, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x01,
25+
0x67, 0x4d, 0x00, 0x2a, 0x96, 0x35, 0x40, 0xf0, 0x04, 0x4f, 0xcb, 0x37, 0x01, 0x01, 0x01, 0x40,
26+
0x00, 0x01, 0xc2, 0x00, 0x00, 0x57, 0xe4, 0x01, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x0e, 0x8c, 0x00,
27+
0x03, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x3c, 0x80, 0x00, 0x00, 0x01, 0xe0,
28+
0x00, 0x0e, 0x8c, 0x00, 0x02, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x01, 0x06, 0xe5, 0x01, 0x67, 0x80
29+
30+
```

buffer/buffer.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package buffer
2+
3+
import (
4+
"encoding/binary"
5+
"io"
6+
)
7+
8+
type RawBuffer struct {
9+
raw []byte
10+
buffer int
11+
}
12+
13+
func (r *RawBuffer) LoadBuffer(data []byte) {
14+
r.raw = data
15+
r.buffer = len(r.raw)
16+
}
17+
18+
func (r *RawBuffer) Skip(length int) error {
19+
if err := r.checkLen(length); err != nil {
20+
return err
21+
}
22+
r.skip(length)
23+
return nil
24+
}
25+
26+
func (r *RawBuffer) Uint8() (uint8, error) {
27+
if err := r.checkLen(1); err != nil {
28+
return 0, err
29+
}
30+
res := r.raw[0]
31+
r.skip(1)
32+
33+
return res, nil
34+
}
35+
36+
func (r *RawBuffer) Uint16() (uint16, error) {
37+
if err := r.checkLen(2); err != nil {
38+
return 0, err
39+
}
40+
res := binary.BigEndian.Uint16(r.raw[:2])
41+
r.skip(2)
42+
43+
return res, nil
44+
}
45+
46+
func (r *RawBuffer) Uint32() (uint32, error) {
47+
if err := r.checkLen(4); err != nil {
48+
return 0, err
49+
}
50+
res := binary.BigEndian.Uint32(r.raw[:4])
51+
r.skip(4)
52+
53+
return res, nil
54+
}
55+
56+
func (r *RawBuffer) Bytes(length int) ([]byte, error) {
57+
if err := r.checkLen(length); err != nil {
58+
return nil, err
59+
}
60+
res := r.raw[:length]
61+
r.skip(length)
62+
63+
return res, nil
64+
}
65+
66+
func (r *RawBuffer) checkLen(length int) error {
67+
if length > r.buffer {
68+
return io.ErrUnexpectedEOF
69+
}
70+
return nil
71+
}
72+
73+
func (r *RawBuffer) skip(length int) {
74+
r.buffer -= length
75+
r.raw = r.raw[length:]
76+
}

example/dec/main.go

+59
Large diffs are not rendered by default.

packet/Readme.md

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# PS结构介绍
2+
3+
### Key Point
4+
1. 一帧原始数据只能包含一个`ps`, `map`,`sys`(关键帧需要)等结构.当一帧数据超过pes最大的负载时(`0xFFFF`),则一帧数据可能有多个`pes`结构
5+
2. rtp结构是包含在封装的最外层,所以 一帧数据或者一片pes结构,可能会被分成多个rtp包
6+
3. 各个结构中可能都有扩充字段,在解析的时候可以通过扩充字段来解析到表示的长度,从而得到真实的原始数据的长度
7+
| ps | system header | psm | pesv | pesa |
8+
`pesv`为视频pes包, `pesa`为音频pes包
9+
**非关键帧的ps包结构一般不包含系统头和PSM**
10+
11+
针对PSM中流类型的取值如下:
12+
| 流类型 | 十六进制值|
13+
|-----------|-----------|
14+
| MPEG-4 | 0x10 |
15+
| H.264 | 0x1B |
16+
| SVAC | 0x80 |
17+
| G.711 | 0x90 |
18+
| G.722.1 | 0x92 |
19+
| G.723.1 | 0x93 |
20+
| G.729 | 0x99 |
21+
| SVAC 音频 | 0x9B |
22+
具体可以参看`ISO/IEC 13818-1:2000`
23+
24+
### 各个结构的头字段的说明
25+
26+
#### PS
27+
| 字段名 | 比特位长度 | 含义|
28+
|------------------|:-----:|----------------------------------------|
29+
| pack_start_code | 32 | 起始码字段`0x000001BA` 标志为一包的开始 |
30+
| marker_bit |2|标记位字段2位字段,取值`01`|
31+
| system_clock_reference_base (32..30) |3|系统时钟参考字段|
32+
| marker_bit | 1| 标记位字段取值`1` |
33+
| system_clock_reference_base(29..15) |15|系统时钟参考字段 |
34+
| marker_bit |1 |标记位字段取值`1` |
35+
| system_clock_reference_base(14..0) |15|系统时钟参考字段 |
36+
| marker_bit |1 |标记位字段取值`1`|
37+
| system_clock_reference_extension |9|系统始终参考字段 |
38+
| marker_bit |1|标记位字段取值`1`|
39+
| program_mux_rate |22|节目复合速率字段 |
40+
| marker_bit |1| 标记記位字段取值`1`|
41+
| marker_bit |1| 标记位字段取值`1`|
42+
| reserved |5| 填充字段 |
43+
| pack_stuffing_length |3 |包填充长度字段|
44+
45+
46+
47+
#### System
48+
| 字段名 | 比特位长度 | 含义|
49+
|------------------|:-----:|----------------------------------------|
50+
| system_header_start_code | 32| 开始码 `0x000001BB` |
51+
| header_length |16|該字段後的系統標題的字節長度 |
52+
| marker_bit |1| 标记位字段取值`1` |
53+
| rate_bound |22|速率界限字段 |
54+
| marker_bit |1|标记位字段取值`1`|
55+
| audio_bound |6| 音频界限字段 |
56+
| fixed_flag |1|固定标志字段 |
57+
| CSPS_flag |1|CSPS标志字段 |
58+
| system_audio_lock_flag |1|系統音频锁定标志字段 |
59+
| system_video_lock_flag |1| 系統视频锁定标志字段|
60+
| marker_bit |1| 标记位字段取值`1`|
61+
| vedio_bound |5| 视频界限字段 |
62+
| packet_rate_restriction_flag|1 |分组速率限制标记字段|
63+
| reserved_bits |7| 保留位字段|
64+
| stream_id |8| 流标志字段 一般`0xe0`指视频,`0xc0`音频|
65+
| marker_bit |2|取值`11` |
66+
| P-STD_buffer_bound_scale |1b| P-STD界限比例字段|
67+
|P-STD_buffer_size_bound |13|P-STD大小界限字段|
68+
注:可能会有多个stream , 通过stream_id的第一个bit判断是否存在stream
69+
70+
#### Map
71+
| 字段名 | 比特位长度 | 含义|
72+
|------------------|:-----:|----------------------------------------|
73+
| packet start code |32|映射流起始码标志字段|
74+
| program stream map length |16|节目流映射长度字段 |
75+
| current_next_indicator |1|当前下一個指示符字段 |
76+
| reserved |2|填充字段 |
77+
| program_stream_map_version|5|节目流映射版本字段 |
78+
| reserved |7|预留填充字段|
79+
| marker_bit |1|标记位字段取值`1`|
80+
| program_stream_info_length |16| 节目流信息长度字段 |
81+
| elementary_stream_map_length|16|基本流映射长度字段 |
82+
| stream_type |8| 流类型字段 `0x1b H264``0x24 H265`|
83+
| elementary_stream_id |8|视频取值`0xe0-0xef`,通常为`0xe0`,音频取值`0xc0-0xdf`,通常为`0xc0`|
84+
| elementary_stream_info_length |16|基本流信息长度字段 |
85+
| CRC_32 |32| CRC字段|
86+
87+
88+
#### PES
89+
| 字段名 | 比特位长度 | 含义|
90+
|------------------|:-----:|----------------------------------------|
91+
| packet_start_code_prefix |24| 分组起码码`0x000001` |
92+
| stream_id |8|流标志字段stream_id,其中0x(C0~DF)指音频,0x(E0~EF)视频|
93+
| PES_packet_length |16| PES分组长度字段|
94+
| marker_bit|2| 标志位,`10`|
95+
| PES_scrambling_control |2|PES加扰控制字段 |
96+
| PES_priority |1| PES优先级字段 |
97+
| data_alignment_indicator |1|数据对其指示符字段|
98+
| copyright |1| 盘权字段|
99+
| original_or_copy |1| 原始或拷贝字段 |
100+
| PTS_DTS_flags |2| `PTS DTS`标志字段|
101+
| ESCR_flag |1|ESCR标志字段|
102+
| ES_rate_flag |1|ES速率标志字段|
103+
| DSM_trick_mode_flag |1|DSM特技方式标志字段 |
104+
| additional_copy_info_flag |1|附加版权信息标志字段 |
105+
| PES_CRC_flag |1|PES CRC标志字段 |
106+
| PES_extension_flag|1| PES扩张标志字段 |
107+
| PES_header_data_length| 8| PES标题数据长度字段 |
108+
109+
注:后面会根据此长度来填写 pts, dts等

packet/common.go

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package packet
2+
3+
import "errors"
4+
5+
//
6+
const (
7+
UDPTransfer int = 0
8+
TCPTransferActive int = 1
9+
TCPTransferPassive int = 2
10+
LocalCache int = 3
11+
)
12+
13+
//
14+
const (
15+
StreamTypeH264 = 0x1b
16+
StreamTypeH265 = 0x24
17+
StreamTypeAAC = 0x90
18+
)
19+
20+
//
21+
const (
22+
StreamIDVideo = 0xe0
23+
StreamIDAudio = 0xc0
24+
)
25+
26+
//
27+
const (
28+
StartCodePS = 0x000001ba
29+
StartCodeSYS = 0x000001bb
30+
StartCodeMAP = 0x000001bc
31+
HaiKangCode = 0x000001bd
32+
StartCodeVideo = 0x000001e0
33+
StartCodeAudio = 0x000001c0
34+
MEPGProgramEndCode = 0x000001b9
35+
)
36+
37+
//... len limit
38+
const (
39+
RTPHeaderLength int = 12
40+
PSHeaderLength int = 14
41+
SystemHeaderLength int = 18
42+
MAPHeaderLength int = 24
43+
PESHeaderLength int = 19
44+
RtpLoadLength int = 1460
45+
PESLoadLength int = 0xFFFF
46+
MAXFrameLen int = 1024 * 1024 * 2
47+
)
48+
49+
var (
50+
ErrNotFoundStartCode = errors.New("not found the need start code flag")
51+
ErrMarkerBit = errors.New("marker bit value error")
52+
ErrFormatPack = errors.New("not package standard")
53+
ErrParsePakcet = errors.New("parse ps packet error")
54+
)
55+
56+
/*
57+
This implement from VLC source code
58+
notes: https://github.com/videolan/vlc/blob/master/modules/mux/mpeg/bits.h
59+
*/
60+
61+
//bitsBuffer bits buffer
62+
type bitsBuffer struct {
63+
iSize int
64+
iData int
65+
iMask uint8
66+
pData []byte
67+
}
68+
69+
func bitsInit(isize int, buffer []byte) *bitsBuffer {
70+
71+
bits := &bitsBuffer{
72+
iSize: isize,
73+
iData: 0,
74+
iMask: 0x80,
75+
pData: buffer,
76+
}
77+
if bits.pData == nil {
78+
bits.pData = make([]byte, isize)
79+
}
80+
return bits
81+
}
82+
83+
func bitsAlign(bits *bitsBuffer) {
84+
85+
if bits.iMask != 0x80 && bits.iData < bits.iSize {
86+
bits.iMask = 0x80
87+
bits.iData++
88+
bits.pData[bits.iData] = 0x00
89+
}
90+
}
91+
func bitsWrite(bits *bitsBuffer, count int, src uint64) *bitsBuffer {
92+
93+
for count > 0 {
94+
count--
95+
if ((src >> uint(count)) & 0x01) != 0 {
96+
bits.pData[bits.iData] |= bits.iMask
97+
} else {
98+
bits.pData[bits.iData] &= ^bits.iMask
99+
}
100+
bits.iMask >>= 1
101+
if bits.iMask == 0 {
102+
bits.iData++
103+
bits.iMask = 0x80
104+
}
105+
}
106+
107+
return bits
108+
}

0 commit comments

Comments
 (0)