protocol/rtp: extended functionality of rtp pkg

rtp.Bytes can now deal with CSRC fields, extension fields and padding. Tests have been updated to be more extensive in checks.
This commit is contained in:
Saxon 2019-03-29 15:48:34 +10:30
parent 7c31f6fd6c
commit 95a9e4a2ef
3 changed files with 226 additions and 66 deletions

View File

@ -34,8 +34,6 @@ import (
) )
const ( const (
yes = 1
no = 0
defaultPktType = 33 defaultPktType = 33
timestampFreq = 90000 // Hz timestampFreq = 90000 // Hz
mtsSize = 188 mtsSize = 188
@ -101,15 +99,15 @@ func min(a, b int) int {
func (e *Encoder) Encode(payload []byte) error { func (e *Encoder) Encode(payload []byte) error {
pkt := Pkt{ pkt := Pkt{
V: rtpVer, // version V: rtpVer, // version
X: no, // header extension X: false, // header extension
CC: no, // CSRC count CC: 0, // CSRC count
M: no, // NOTE: need to check if this works (decoders should ignore this) M: false, // NOTE: need to check if this works (decoders should ignore this)
PT: defaultPktType, // 33 for mpegts PT: defaultPktType, // 33 for mpegts
SN: e.nxtSeqNo(), // sequence number SN: e.nxtSeqNo(), // sequence number
TS: e.nxtTimestamp(), // timestamp TS: e.nxtTimestamp(), // timestamp
SSRC: e.ssrc, // source identifier SSRC: e.ssrc, // source identifier
Payload: payload, Payload: payload,
Padding: no, Padding: nil,
} }
_, err := e.dst.Write(pkt.Bytes(e.pktSpace[:defPktSize])) _, err := e.dst.Write(pkt.Bytes(e.pktSpace[:defPktSize]))
if err != nil { if err != nil {

View File

@ -32,75 +32,109 @@ for rtp-h264 and rtp standards.
*/ */
package rtp package rtp
import (
"encoding/binary"
)
const ( const (
rtpVer = 2 rtpVer = 2
headSize = 3 * 4 // Header size of an rtp packet. defaultHeadSize = 3 * 4 // Header size of an rtp packet.
defPayloadSize = sendSize // Default payload size for the rtp packet. defPayloadSize = sendSize // Default payload size for the rtp packet.
defPktSize = headSize + defPayloadSize // Default packet size is header size + payload size. defPktSize = defaultHeadSize + defPayloadSize // Default packet size is header size + payload size.
) )
// Pkt provides fields consistent with RFC3550 definition of an rtp packet // Pkt provides fields consistent with RFC3550 definition of an rtp packet
// The padding indicator does not need to be set manually, only the padding length // The padding indicator does not need to be set manually, only the padding length
type Pkt struct { type Pkt struct {
V byte // Version (currently 2) V uint8 // Version (currently 2).
p byte // Padding indicator (0 => padding, 1 => padding) p bool // Padding indicator (0 => padding, 1 => padding).
X byte // Extension header indicator X bool // Extension header indicator.
CC byte // CSRC count CC uint8 // CSRC count.
M byte // Marker bit M bool // Marker bit.
PT byte // Packet type PT uint8 // Packet type.
SN uint16 // Synch number SN uint16 // Synch number.
TS uint32 // Timestamp TS uint32 // Timestamp.
SSRC uint32 // Synchronisation source identifier SSRC uint32 // Synchronisation source identifier.
Payload []byte // H264 Payload data CSRC [][4]byte // Contributing source identifier.
Padding byte // No of bytes of padding Extension ExtensionHeader // Header extension.
Payload []byte // H264 Payload data.
Padding []byte // No of bytes of padding.
}
// ExtensionHeader header provides fields for an RTP packet extension header.
type ExtensionHeader struct {
ID uint16
Len uint16
Header [][4]byte
} }
// Bytes provides a byte slice of the packet // Bytes provides a byte slice of the packet
func (p *Pkt) Bytes(buf []byte) []byte { func (p *Pkt) Bytes(buf []byte) []byte {
if buf == nil || len(buf) != defPktSize { // Calculate the required length for the RTP packet.
buf = make([]byte, headSize, defPktSize) headerExtensionLen := 0
if p.X {
if p.Extension.Len != uint16(len(p.Extension.Header)) {
panic("bad rtp packet extension length field")
} }
buf = buf[:headSize] headerExtensionLen = int(4 + 4*p.Extension.Len)
if p.V == 0 {
p.V = rtpVer
} }
requiredPktLen := defaultHeadSize + uint8(4*p.CC) + uint8(headerExtensionLen) + uint8(len(p.Payload)) + uint8(len(p.Padding))
if p.Padding > 0 { // Create new space if no buffer is given, or it doesn't have sufficient capacity.
p.p = 1 if buf == nil || requiredPktLen > uint8(cap(buf)) {
buf = make([]byte, requiredPktLen, defPktSize)
} }
buf = buf[:requiredPktLen]
// Start encoding fields into the buffer.
buf[0] = p.V<<6 | asByte(p.p)<<5 | asByte(p.X)<<4 | p.CC
buf[1] = asByte(p.M)<<7 | p.PT
binary.BigEndian.PutUint16(buf[2:4], p.SN)
binary.BigEndian.PutUint32(buf[4:8], p.TS)
binary.BigEndian.PutUint32(buf[8:12], p.SSRC)
// If there is a CSRC count, add the CSRC to the buffer.
if p.CC != 0 { if p.CC != 0 {
panic("CC has been set to something other than 0 - this is not supported yet.") if p.CC != uint8(len(p.CSRC)) {
panic("CSRC count in RTP packet is incorrect")
}
for i := 0; i < int(p.CC); i++ {
copy(buf[12+i*4:], p.CSRC[i][:])
}
} }
if p.X != 0 { // This is our current index for writing to the buffer.
panic("rtp: X (extension header indicator) not 0, but extensiion headers not currently supported.") idx := int(12 + 4*p.CC)
// If there is an extension field, add this to the buffer.
if p.X {
binary.BigEndian.PutUint16(buf[idx:idx+2], p.Extension.ID)
idx += 2
binary.BigEndian.PutUint16(buf[idx:idx+2], p.Extension.Len)
idx += 2
for i := 0; i < int(p.Extension.Len); i++ {
copy(buf[idx+i*4:], p.Extension.Header[i][:])
}
idx += int(p.Extension.Len * 4)
} }
if p.CC != 0 { // If there is payload, add to the buffer.
panic("rtp: CC (CSRC count) not 0, but CSRC headers not yet supported.") if p.Payload != nil {
copy(buf[idx:], p.Payload)
idx += len(p.Payload)
} }
buf[0] = p.V<<6 | p.p<<5 | p.CC // Finally, if there is padding, add to the buffer.
buf[1] = p.M<<7 | p.PT if p.Padding != nil {
buf[2] = byte(p.SN >> 8) copy(buf[idx:], p.Padding)
buf[3] = byte(p.SN)
buf[4] = byte(p.TS >> 24)
buf[5] = byte(p.TS >> 16)
buf[6] = byte(p.TS >> 8)
buf[7] = byte(p.TS)
buf[8] = byte(p.SSRC >> 24)
buf[9] = byte(p.SSRC >> 16)
buf[10] = byte(p.SSRC >> 8)
buf[11] = byte(p.SSRC)
buf = append(buf, p.Payload...)
// see https://tools.ietf.org/html/rfc3550 section 5.1 (padding). At end of
// rtp packet, padding may exist, with the last octet being the length of the
// padding including itself.
if p.Padding != 0 {
buf = buf[:cap(buf)]
buf[len(buf)-1] = byte(p.Padding)
} }
return buf return buf
} }
func asByte(b bool) byte {
if b {
return 0x01
}
return 0x00
}

View File

@ -38,20 +38,23 @@ var rtpTests = []struct {
pkt Pkt pkt Pkt
want []byte want []byte
}{ }{
// No padding, no CSRC and no extension.
{ {
num: 1, num: 1,
pkt: Pkt{ pkt: Pkt{
V: 2, V: 2,
p: 0, p: false,
X: 0, X: false,
CC: 0, CC: 0,
M: 0, M: false,
PT: 6, PT: 6,
SN: 167, SN: 167,
TS: 160, TS: 160,
SSRC: 10, SSRC: 10,
Payload: []byte{0x00, 0x01, 0x07, 0xf0, 0x56, 0x37, 0x0a, 0x0f}, Payload: []byte{
Padding: 0, 0x00, 0x01, 0x07, 0xf0,
0x56, 0x37, 0x0a, 0x0f,
},
}, },
want: []byte{ want: []byte{
0x80, 0x06, 0x00, 0xa7, 0x80, 0x06, 0x00, 0xa7,
@ -61,13 +64,138 @@ var rtpTests = []struct {
0x56, 0x37, 0x0a, 0x0f, 0x56, 0x37, 0x0a, 0x0f,
}, },
}, },
// With padding.
{
num: 2,
pkt: Pkt{
V: 2,
p: true,
X: false,
CC: 0,
M: false,
PT: 6,
SN: 167,
TS: 160,
SSRC: 10,
Payload: []byte{
0x00, 0x01, 0x07, 0xf0,
0x56, 0x37, 0x0a, 0x0f,
},
Padding: []byte{
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0b,
},
},
want: []byte{
0xa0, 0x06, 0x00, 0xa7,
0x00, 0x00, 0x00, 0xa0,
0x00, 0x00, 0x00, 0x0a,
0x00, 0x01, 0x07, 0xf0,
0x56, 0x37, 0x0a, 0x0f,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0b,
},
},
// With padding and CSRC.
{
num: 3,
pkt: Pkt{
V: 2,
p: true,
X: false,
CC: 2,
M: false,
PT: 6,
SN: 167,
TS: 160,
SSRC: 10,
CSRC: [][4]byte{
{0x01, 0x02, 0x03, 0x04},
{0x05, 0x06, 0x07, 0x08},
},
Payload: []byte{
0x00, 0x01, 0x07, 0xf0,
0x56, 0x37, 0x0a, 0x0f,
},
Padding: []byte{
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0b,
},
},
want: []byte{
0xa2, 0x06, 0x00, 0xa7,
0x00, 0x00, 0x00, 0xa0,
0x00, 0x00, 0x00, 0x0a,
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08,
0x00, 0x01, 0x07, 0xf0,
0x56, 0x37, 0x0a, 0x0f,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0b,
},
},
// With padding, CSRC and extension.
{
num: 4,
pkt: Pkt{
V: 2,
p: true,
X: true,
CC: 2,
M: false,
PT: 6,
SN: 167,
TS: 160,
SSRC: 10,
CSRC: [][4]byte{
{0x01, 0x02, 0x03, 0x04},
{0x05, 0x06, 0x07, 0x08},
},
Extension: ExtensionHeader{
ID: 15,
Len: 2,
Header: [][4]byte{
{0x01, 0x02, 0x03, 0x04},
{0x05, 0x06, 0x07, 0x08},
},
},
Payload: []byte{
0x00, 0x01, 0x07, 0xf0,
0x56, 0x37, 0x0a, 0x0f,
},
Padding: []byte{
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0b,
},
},
want: []byte{
0xb2, 0x06, 0x00, 0xa7,
0x00, 0x00, 0x00, 0xa0,
0x00, 0x00, 0x00, 0x0a,
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08,
0x00, 0x0f, 0x00, 0x02,
0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08,
0x00, 0x01, 0x07, 0xf0,
0x56, 0x37, 0x0a, 0x0f,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0b,
},
},
} }
func TestRtpPktToByteSlice(t *testing.T) { func TestRtpPktToByteSlice(t *testing.T) {
for _, test := range rtpTests { for _, test := range rtpTests {
got := test.pkt.Bytes(nil) got := test.pkt.Bytes(nil)
if !reflect.DeepEqual(got, test.want) { if !reflect.DeepEqual(got, test.want) {
t.Errorf("unexpected error for test %v: got:%v want:%v", test.num, got, t.Errorf("unexpected error for test %v: \ngot :%v\n want:%v\n", test.num, got,
test.want) test.want)
} }
} }