mirror of https://bitbucket.org/ausocean/av.git
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:
commit
62defd0e8f
|
@ -34,8 +34,6 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
yes = 1
|
||||
no = 0
|
||||
defaultPktType = 33
|
||||
timestampFreq = 90000 // Hz
|
||||
mtsSize = 188
|
||||
|
@ -101,15 +99,15 @@ func min(a, b int) int {
|
|||
func (e *Encoder) Encode(payload []byte) error {
|
||||
pkt := Pkt{
|
||||
V: rtpVer, // version
|
||||
X: no, // header extension
|
||||
CC: no, // CSRC count
|
||||
M: no, // NOTE: need to check if this works (decoders should ignore this)
|
||||
X: false, // header extension
|
||||
CC: 0, // CSRC count
|
||||
M: false, // NOTE: need to check if this works (decoders should ignore this)
|
||||
PT: defaultPktType, // 33 for mpegts
|
||||
SN: e.nxtSeqNo(), // sequence number
|
||||
TS: e.nxtTimestamp(), // timestamp
|
||||
SSRC: e.ssrc, // source identifier
|
||||
Payload: payload,
|
||||
Padding: no,
|
||||
Padding: nil,
|
||||
}
|
||||
_, err := e.dst.Write(pkt.Bytes(e.pktSpace[:defPktSize]))
|
||||
if err != nil {
|
||||
|
|
|
@ -32,75 +32,105 @@ for rtp-h264 and rtp standards.
|
|||
*/
|
||||
package rtp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
const (
|
||||
rtpVer = 2
|
||||
headSize = 3 * 4 // Header size of an rtp packet.
|
||||
defPayloadSize = sendSize // Default payload size for the rtp packet.
|
||||
defPktSize = headSize + defPayloadSize // Default packet size is header size + payload size.
|
||||
rtpVer = 2
|
||||
defaultHeadSize = 3 * 4 // Header size of an rtp packet.
|
||||
defPayloadSize = sendSize // Default payload size for the rtp packet.
|
||||
defPktSize = defaultHeadSize + defPayloadSize // Default packet size is header size + payload size.
|
||||
)
|
||||
|
||||
// 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
|
||||
type Pkt struct {
|
||||
V byte // Version (currently 2)
|
||||
p byte // Padding indicator (0 => padding, 1 => padding)
|
||||
X byte // Extension header indicator
|
||||
CC byte // CSRC count
|
||||
M byte // Marker bit
|
||||
PT byte // Packet type
|
||||
SN uint16 // Synch number
|
||||
TS uint32 // Timestamp
|
||||
SSRC uint32 // Synchronisation source identifier
|
||||
Payload []byte // H264 Payload data
|
||||
Padding byte // No of bytes of padding
|
||||
V uint8 // Version (currently 2).
|
||||
p bool // Padding indicator (0 => padding, 1 => padding).
|
||||
X bool // Extension header indicator.
|
||||
CC uint8 // CSRC count.
|
||||
M bool // Marker bit.
|
||||
PT uint8 // Packet type.
|
||||
SN uint16 // Synch number.
|
||||
TS uint32 // Timestamp.
|
||||
SSRC uint32 // Synchronisation source identifier.
|
||||
CSRC [][4]byte // Contributing source identifier.
|
||||
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
|
||||
func (p *Pkt) Bytes(buf []byte) []byte {
|
||||
if buf == nil || len(buf) != defPktSize {
|
||||
buf = make([]byte, headSize, defPktSize)
|
||||
}
|
||||
buf = buf[:headSize]
|
||||
if p.V == 0 {
|
||||
p.V = rtpVer
|
||||
// Calculate the required length for the RTP packet.
|
||||
headerExtensionLen := 0
|
||||
if p.X {
|
||||
headerExtensionLen = int(4 + 4*len(p.Extension.Header))
|
||||
}
|
||||
requiredPktLen := defaultHeadSize + uint8(4*p.CC) + uint8(headerExtensionLen) + uint8(len(p.Payload)) + uint8(len(p.Padding))
|
||||
|
||||
if p.Padding > 0 {
|
||||
p.p = 1
|
||||
// Create new space if no buffer is given, or it doesn't have sufficient capacity.
|
||||
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 {
|
||||
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 {
|
||||
panic("rtp: X (extension header indicator) not 0, but extensiion headers not currently supported.")
|
||||
// This is our current index for writing to the buffer.
|
||||
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 {
|
||||
panic("rtp: CC (CSRC count) not 0, but CSRC headers not yet supported.")
|
||||
// If there is payload, add to the buffer.
|
||||
if p.Payload != nil {
|
||||
copy(buf[idx:], p.Payload)
|
||||
idx += len(p.Payload)
|
||||
}
|
||||
|
||||
buf[0] = p.V<<6 | p.p<<5 | p.CC
|
||||
buf[1] = p.M<<7 | p.PT
|
||||
buf[2] = byte(p.SN >> 8)
|
||||
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)
|
||||
// Finally, if there is padding, add to the buffer.
|
||||
if p.Padding != nil {
|
||||
copy(buf[idx:], p.Padding)
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
func asByte(b bool) byte {
|
||||
if b {
|
||||
return 0x01
|
||||
}
|
||||
return 0x00
|
||||
}
|
||||
|
|
|
@ -38,20 +38,23 @@ var rtpTests = []struct {
|
|||
pkt Pkt
|
||||
want []byte
|
||||
}{
|
||||
// No padding, no CSRC and no extension.
|
||||
{
|
||||
num: 1,
|
||||
pkt: Pkt{
|
||||
V: 2,
|
||||
p: 0,
|
||||
X: 0,
|
||||
CC: 0,
|
||||
M: 0,
|
||||
PT: 6,
|
||||
SN: 167,
|
||||
TS: 160,
|
||||
SSRC: 10,
|
||||
Payload: []byte{0x00, 0x01, 0x07, 0xf0, 0x56, 0x37, 0x0a, 0x0f},
|
||||
Padding: 0,
|
||||
V: 2,
|
||||
p: false,
|
||||
X: false,
|
||||
CC: 0,
|
||||
M: false,
|
||||
PT: 6,
|
||||
SN: 167,
|
||||
TS: 160,
|
||||
SSRC: 10,
|
||||
Payload: []byte{
|
||||
0x00, 0x01, 0x07, 0xf0,
|
||||
0x56, 0x37, 0x0a, 0x0f,
|
||||
},
|
||||
},
|
||||
want: []byte{
|
||||
0x80, 0x06, 0x00, 0xa7,
|
||||
|
@ -61,13 +64,137 @@ var rtpTests = []struct {
|
|||
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) {
|
||||
for _, test := range rtpTests {
|
||||
got := test.pkt.Bytes(nil)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue