package rtcp
import (
"encoding/binary"
"errors"
"fmt"
"math"
)
const (
TypeTCCRunLengthChunk = 0
TypeTCCStatusVectorChunk = 1
packetStatusChunkLength = 2
)
const (
TypeTCCPacketNotReceived = uint16 (iota )
TypeTCCPacketReceivedSmallDelta
TypeTCCPacketReceivedLargeDelta
TypeTCCPacketReceivedWithoutDelta
)
const (
TypeTCCSymbolSizeOneBit = 0
TypeTCCSymbolSizeTwoBit = 1
)
func numOfBitsOfSymbolSize() map [uint16 ]uint16 {
return map [uint16 ]uint16 {
TypeTCCSymbolSizeOneBit : 1 ,
TypeTCCSymbolSizeTwoBit : 2 ,
}
}
var (
errPacketStatusChunkLength = errors .New ("packet status chunk must be 2 bytes" )
errDeltaExceedLimit = errors .New ("delta exceed limit" )
)
type PacketStatusChunk interface {
Marshal () ([]byte , error )
Unmarshal (rawPacket []byte ) error
}
type RunLengthChunk struct {
PacketStatusChunk
Type uint16
PacketStatusSymbol uint16
RunLength uint16
}
func (r RunLengthChunk ) Marshal () ([]byte , error ) {
chunk := make ([]byte , 2 )
dst , err := setNBitsOfUint16 (0 , 1 , 0 , 0 )
if err != nil {
return nil , err
}
dst , err = setNBitsOfUint16 (dst , 2 , 1 , r .PacketStatusSymbol )
if err != nil {
return nil , err
}
dst , err = setNBitsOfUint16 (dst , 13 , 3 , r .RunLength )
if err != nil {
return nil , err
}
binary .BigEndian .PutUint16 (chunk , dst )
return chunk , nil
}
func (r *RunLengthChunk ) Unmarshal (rawPacket []byte ) error {
if len (rawPacket ) != packetStatusChunkLength {
return errPacketStatusChunkLength
}
r .Type = TypeTCCRunLengthChunk
r .PacketStatusSymbol = getNBitsFromByte (rawPacket [0 ], 1 , 2 )
r .RunLength = getNBitsFromByte (rawPacket [0 ], 3 , 5 )<<8 + uint16 (rawPacket [1 ])
return nil
}
type StatusVectorChunk struct {
PacketStatusChunk
Type uint16
SymbolSize uint16
SymbolList []uint16
}
func (r StatusVectorChunk ) Marshal () ([]byte , error ) {
chunk := make ([]byte , 2 )
dst , err := setNBitsOfUint16 (0 , 1 , 0 , 1 )
if err != nil {
return nil , err
}
dst , err = setNBitsOfUint16 (dst , 1 , 1 , r .SymbolSize )
if err != nil {
return nil , err
}
numOfBits := numOfBitsOfSymbolSize ()[r .SymbolSize ]
for i , s := range r .SymbolList {
index := numOfBits *uint16 (i ) + 2
dst , err = setNBitsOfUint16 (dst , numOfBits , index , s )
if err != nil {
return nil , err
}
}
binary .BigEndian .PutUint16 (chunk , dst )
return chunk , nil
}
func (r *StatusVectorChunk ) Unmarshal (rawPacket []byte ) error {
if len (rawPacket ) != packetStatusChunkLength {
return errPacketStatusChunkLength
}
r .Type = TypeTCCStatusVectorChunk
r .SymbolSize = getNBitsFromByte (rawPacket [0 ], 1 , 1 )
if r .SymbolSize == TypeTCCSymbolSizeOneBit {
for i := uint16 (0 ); i < 6 ; i ++ {
r .SymbolList = append (r .SymbolList , getNBitsFromByte (rawPacket [0 ], 2 +i , 1 ))
}
for i := uint16 (0 ); i < 8 ; i ++ {
r .SymbolList = append (r .SymbolList , getNBitsFromByte (rawPacket [1 ], i , 1 ))
}
return nil
}
if r .SymbolSize == TypeTCCSymbolSizeTwoBit {
for i := uint16 (0 ); i < 3 ; i ++ {
r .SymbolList = append (r .SymbolList , getNBitsFromByte (rawPacket [0 ], 2 +i *2 , 2 ))
}
for i := uint16 (0 ); i < 4 ; i ++ {
r .SymbolList = append (r .SymbolList , getNBitsFromByte (rawPacket [1 ], i *2 , 2 ))
}
return nil
}
r .SymbolSize = getNBitsFromByte (rawPacket [0 ], 2 , 6 )<<8 + uint16 (rawPacket [1 ])
return nil
}
const (
TypeTCCDeltaScaleFactor = 250
)
type RecvDelta struct {
Type uint16
Delta int64
}
func (r RecvDelta ) Marshal () ([]byte , error ) {
delta := r .Delta / TypeTCCDeltaScaleFactor
if r .Type == TypeTCCPacketReceivedSmallDelta && delta >= 0 && delta <= math .MaxUint8 {
deltaChunk := make ([]byte , 1 )
deltaChunk [0 ] = byte (delta )
return deltaChunk , nil
}
if r .Type == TypeTCCPacketReceivedLargeDelta && delta >= math .MinInt16 && delta <= math .MaxInt16 {
deltaChunk := make ([]byte , 2 )
binary .BigEndian .PutUint16 (deltaChunk , uint16 (delta ))
return deltaChunk , nil
}
return nil , errDeltaExceedLimit
}
func (r *RecvDelta ) Unmarshal (rawPacket []byte ) error {
chunkLen := len (rawPacket )
if chunkLen != 1 && chunkLen != 2 {
return errDeltaExceedLimit
}
if chunkLen == 1 {
r .Type = TypeTCCPacketReceivedSmallDelta
r .Delta = TypeTCCDeltaScaleFactor * int64 (rawPacket [0 ])
return nil
}
r .Type = TypeTCCPacketReceivedLargeDelta
r .Delta = TypeTCCDeltaScaleFactor * int64 (int16 (binary .BigEndian .Uint16 (rawPacket )))
return nil
}
const (
baseSequenceNumberOffset = 8
packetStatusCountOffset = 10
referenceTimeOffset = 12
fbPktCountOffset = 15
packetChunkOffset = 16
)
type TransportLayerCC struct {
Header Header
SenderSSRC uint32
MediaSSRC uint32
BaseSequenceNumber uint16
PacketStatusCount uint16
ReferenceTime uint32
FbPktCount uint8
PacketChunks []PacketStatusChunk
RecvDeltas []*RecvDelta
}
func (t *TransportLayerCC ) packetLen () uint16 {
n := uint16 (headerLength + packetChunkOffset + len (t .PacketChunks )*2 )
for _ , d := range t .RecvDeltas {
if d .Type == TypeTCCPacketReceivedSmallDelta {
n ++
} else {
n += 2
}
}
return n
}
func (t *TransportLayerCC ) Len () uint16 {
return uint16 (t .MarshalSize ())
}
func (t *TransportLayerCC ) MarshalSize () int {
n := t .packetLen ()
if n %4 != 0 {
n = (n /4 + 1 ) * 4
}
return int (n )
}
func (t TransportLayerCC ) String () string {
out := fmt .Sprintf ("TransportLayerCC:\n\tHeader %v\n" , t .Header )
out += fmt .Sprintf ("TransportLayerCC:\n\tSender Ssrc %d\n" , t .SenderSSRC )
out += fmt .Sprintf ("\tMedia Ssrc %d\n" , t .MediaSSRC )
out += fmt .Sprintf ("\tBase Sequence Number %d\n" , t .BaseSequenceNumber )
out += fmt .Sprintf ("\tStatus Count %d\n" , t .PacketStatusCount )
out += fmt .Sprintf ("\tReference Time %d\n" , t .ReferenceTime )
out += fmt .Sprintf ("\tFeedback Packet Count %d\n" , t .FbPktCount )
out += "\tPacketChunks "
for _ , chunk := range t .PacketChunks {
out += fmt .Sprintf ("%+v " , chunk )
}
out += "\n\tRecvDeltas "
for _ , delta := range t .RecvDeltas {
out += fmt .Sprintf ("%+v " , delta )
}
out += "\n"
return out
}
func (t TransportLayerCC ) Marshal () ([]byte , error ) {
header , err := t .Header .Marshal ()
if err != nil {
return nil , err
}
payload := make ([]byte , t .MarshalSize ()-headerLength )
binary .BigEndian .PutUint32 (payload , t .SenderSSRC )
binary .BigEndian .PutUint32 (payload [4 :], t .MediaSSRC )
binary .BigEndian .PutUint16 (payload [baseSequenceNumberOffset :], t .BaseSequenceNumber )
binary .BigEndian .PutUint16 (payload [packetStatusCountOffset :], t .PacketStatusCount )
ReferenceTimeAndFbPktCount := appendNBitsToUint32 (0 , 24 , t .ReferenceTime )
ReferenceTimeAndFbPktCount = appendNBitsToUint32 (ReferenceTimeAndFbPktCount , 8 , uint32 (t .FbPktCount ))
binary .BigEndian .PutUint32 (payload [referenceTimeOffset :], ReferenceTimeAndFbPktCount )
for i , chunk := range t .PacketChunks {
b , err := chunk .Marshal ()
if err != nil {
return nil , err
}
copy (payload [packetChunkOffset +i *2 :], b )
}
recvDeltaOffset := packetChunkOffset + len (t .PacketChunks )*2
var i int
for _ , delta := range t .RecvDeltas {
b , err := delta .Marshal ()
if err == nil {
copy (payload [recvDeltaOffset +i :], b )
i ++
if delta .Type == TypeTCCPacketReceivedLargeDelta {
i ++
}
}
}
if t .Header .Padding {
payload [len (payload )-1 ] = uint8 (t .MarshalSize () - int (t .packetLen ()))
}
return append (header , payload ...), nil
}
func (t *TransportLayerCC ) Unmarshal (rawPacket []byte ) error {
if len (rawPacket ) < (headerLength + ssrcLength ) {
return errPacketTooShort
}
if err := t .Header .Unmarshal (rawPacket ); err != nil {
return err
}
totalLength := 4 * (t .Header .Length + 1 )
if totalLength < headerLength +packetChunkOffset {
return errPacketTooShort
}
if len (rawPacket ) < int (totalLength ) {
return errPacketTooShort
}
if t .Header .Type != TypeTransportSpecificFeedback || t .Header .Count != FormatTCC {
return errWrongType
}
t .SenderSSRC = binary .BigEndian .Uint32 (rawPacket [headerLength :])
t .MediaSSRC = binary .BigEndian .Uint32 (rawPacket [headerLength +ssrcLength :])
t .BaseSequenceNumber = binary .BigEndian .Uint16 (rawPacket [headerLength +baseSequenceNumberOffset :])
t .PacketStatusCount = binary .BigEndian .Uint16 (rawPacket [headerLength +packetStatusCountOffset :])
t .ReferenceTime = get24BitsFromBytes (rawPacket [headerLength +referenceTimeOffset : headerLength +referenceTimeOffset +3 ])
t .FbPktCount = rawPacket [headerLength +fbPktCountOffset ]
packetStatusPos := uint16 (headerLength + packetChunkOffset )
var processedPacketNum uint16
for processedPacketNum < t .PacketStatusCount {
if packetStatusPos +packetStatusChunkLength >= totalLength {
return errPacketTooShort
}
typ := getNBitsFromByte (rawPacket [packetStatusPos : packetStatusPos +1 ][0 ], 0 , 1 )
var iPacketStatus PacketStatusChunk
switch typ {
case TypeTCCRunLengthChunk :
packetStatus := &RunLengthChunk {Type : typ }
iPacketStatus = packetStatus
err := packetStatus .Unmarshal (rawPacket [packetStatusPos : packetStatusPos +2 ])
if err != nil {
return err
}
packetNumberToProcess := localMin (t .PacketStatusCount -processedPacketNum , packetStatus .RunLength )
if packetStatus .PacketStatusSymbol == TypeTCCPacketReceivedSmallDelta ||
packetStatus .PacketStatusSymbol == TypeTCCPacketReceivedLargeDelta {
for j := uint16 (0 ); j < packetNumberToProcess ; j ++ {
t .RecvDeltas = append (t .RecvDeltas , &RecvDelta {Type : packetStatus .PacketStatusSymbol })
}
}
processedPacketNum += packetNumberToProcess
case TypeTCCStatusVectorChunk :
packetStatus := &StatusVectorChunk {Type : typ }
iPacketStatus = packetStatus
err := packetStatus .Unmarshal (rawPacket [packetStatusPos : packetStatusPos +2 ])
if err != nil {
return err
}
if packetStatus .SymbolSize == TypeTCCSymbolSizeOneBit {
for j := 0 ; j < len (packetStatus .SymbolList ); j ++ {
if packetStatus .SymbolList [j ] == TypeTCCPacketReceivedSmallDelta {
t .RecvDeltas = append (t .RecvDeltas , &RecvDelta {Type : TypeTCCPacketReceivedSmallDelta })
}
}
}
if packetStatus .SymbolSize == TypeTCCSymbolSizeTwoBit {
for j := 0 ; j < len (packetStatus .SymbolList ); j ++ {
if packetStatus .SymbolList [j ] == TypeTCCPacketReceivedSmallDelta || packetStatus .SymbolList [j ] == TypeTCCPacketReceivedLargeDelta {
t .RecvDeltas = append (t .RecvDeltas , &RecvDelta {Type : packetStatus .SymbolList [j ]})
}
}
}
processedPacketNum += uint16 (len (packetStatus .SymbolList ))
}
packetStatusPos += packetStatusChunkLength
t .PacketChunks = append (t .PacketChunks , iPacketStatus )
}
recvDeltasPos := packetStatusPos
for _ , delta := range t .RecvDeltas {
if delta .Type == TypeTCCPacketReceivedSmallDelta {
if recvDeltasPos +1 > totalLength {
return errPacketTooShort
}
err := delta .Unmarshal (rawPacket [recvDeltasPos : recvDeltasPos +1 ])
if err != nil {
return err
}
recvDeltasPos ++
}
if delta .Type == TypeTCCPacketReceivedLargeDelta {
if recvDeltasPos +2 > totalLength {
return errPacketTooShort
}
err := delta .Unmarshal (rawPacket [recvDeltasPos : recvDeltasPos +2 ])
if err != nil {
return err
}
recvDeltasPos += 2
}
}
return nil
}
func (t TransportLayerCC ) DestinationSSRC () []uint32 {
return []uint32 {t .MediaSSRC }
}
func localMin(x , y uint16 ) uint16 {
if x < y {
return x
}
return y
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .