av/protocol/rtp/rtp.go

138 lines
4.3 KiB
Go

/*
NAME
rtp.go - provides a data structure intended to encapsulate the properties
of an rtp packet and also functions to allow manipulation of these packets.
DESCRIPTION
See Readme.md
AUTHOR
Saxon A. Nelson-Milton <saxon@ausocean.org>
LICENSE
rtp.go is Copyright (C) 2018 the Australian Ocean Lab (AusOcean)
It is free software: you can redistribute it and/or modify them
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
It is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with revid in gpl.txt. If not, see [GNU licenses](http://www.gnu.org/licenses).
*/
/*
See https://tools.ietf.org/html/rfc6184 and https://tools.ietf.org/html/rfc3550
for rtp-h264 and rtp standards.
*/
package rtp
import (
"encoding/binary"
)
const (
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.
optionalFieldIdx = 12 // This is the idx of optional fields including CSRC and extension header in 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
type Pkt struct {
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 {
// 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))
// 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 {
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][:])
}
}
// 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 there is payload, add to the buffer.
if p.Payload != nil {
copy(buf[idx:], p.Payload)
idx += len(p.Payload)
}
// 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
}