Merged in rtp-improve (pull request #177)

protocol/rtp: extended functionality of rtp pkg

Approved-by: kortschak <dan@kortschak.io>
This commit is contained in:
Saxon Milton 2019-04-03 01:52:31 +00:00
commit 62defd0e8f
3 changed files with 221 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,105 @@ 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 // Payload data.
Padding []byte // No of bytes of padding.
}
// ExtensionHeader header provides fields for an RTP packet extension header.
type ExtensionHeader struct {
ID 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 {
buf = buf[:headSize] headerExtensionLen = int(4 + 4*len(p.Extension.Header))
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], uint16(len(p.Extension.Header)))
idx += 2
for i := 0; i < len(p.Extension.Header); i++ {
copy(buf[idx+i*4:], p.Extension.Header[i][:])
}
idx += len(p.Extension.Header) * 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,137 @@ 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,
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)
} }
} }