2024-06-05 20:10:03 +00:00
|
|
|
|
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
2022-03-10 09:44:48 +00:00
|
|
|
|
package rtcp
|
|
|
|
|
|
|
|
|
|
// Author: adwpc
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/binary"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"math"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5
|
|
|
|
|
// 0 1 2 3
|
|
|
|
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
// |V=2|P| FMT=15 | PT=205 | length |
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
// | SSRC of packet sender |
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
// | SSRC of media source |
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
// | base sequence number | packet status count |
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
// | reference time | fb pkt. count |
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
// | packet chunk | packet chunk |
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
// . .
|
|
|
|
|
// . .
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
// | packet chunk | recv delta | recv delta |
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
// . .
|
|
|
|
|
// . .
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
// | recv delta | recv delta | zero padding |
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
|
|
|
|
|
// for packet status chunk
|
|
|
|
|
const (
|
|
|
|
|
// type of packet status chunk
|
|
|
|
|
TypeTCCRunLengthChunk = 0
|
|
|
|
|
TypeTCCStatusVectorChunk = 1
|
|
|
|
|
|
|
|
|
|
// len of packet status chunk
|
|
|
|
|
packetStatusChunkLength = 2
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// type of packet status symbol and recv delta
|
|
|
|
|
const (
|
|
|
|
|
// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.1
|
|
|
|
|
TypeTCCPacketNotReceived = uint16(iota)
|
|
|
|
|
TypeTCCPacketReceivedSmallDelta
|
|
|
|
|
TypeTCCPacketReceivedLargeDelta
|
|
|
|
|
// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-7
|
|
|
|
|
// see Example 2: "packet received, w/o recv delta"
|
|
|
|
|
TypeTCCPacketReceivedWithoutDelta
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// for status vector chunk
|
|
|
|
|
const (
|
|
|
|
|
// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.4
|
|
|
|
|
TypeTCCSymbolSizeOneBit = 0
|
|
|
|
|
TypeTCCSymbolSizeTwoBit = 1
|
|
|
|
|
|
|
|
|
|
// Notice: RFC is wrong: "packet received" (0) and "packet not received" (1)
|
|
|
|
|
// if S == TypeTCCSymbolSizeOneBit, symbol list will be: TypeTCCPacketNotReceived TypeTCCPacketReceivedSmallDelta
|
|
|
|
|
// if S == TypeTCCSymbolSizeTwoBit, symbol list will be same as above:
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// PacketStatusChunk has two kinds:
|
|
|
|
|
// RunLengthChunk and StatusVectorChunk
|
|
|
|
|
type PacketStatusChunk interface {
|
|
|
|
|
Marshal() ([]byte, error)
|
|
|
|
|
Unmarshal(rawPacket []byte) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RunLengthChunk T=TypeTCCRunLengthChunk
|
|
|
|
|
// 0 1
|
|
|
|
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
// |T| S | Run Length |
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
type RunLengthChunk struct {
|
|
|
|
|
PacketStatusChunk
|
|
|
|
|
|
|
|
|
|
// T = TypeTCCRunLengthChunk
|
|
|
|
|
Type uint16
|
|
|
|
|
|
|
|
|
|
// S: type of packet status
|
|
|
|
|
// kind: TypeTCCPacketNotReceived or...
|
|
|
|
|
PacketStatusSymbol uint16
|
|
|
|
|
|
|
|
|
|
// RunLength: count of S
|
|
|
|
|
RunLength uint16
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Marshal ..
|
|
|
|
|
func (r RunLengthChunk) Marshal() ([]byte, error) {
|
|
|
|
|
chunk := make([]byte, 2)
|
|
|
|
|
|
|
|
|
|
// append 1 bit '0'
|
|
|
|
|
dst, err := setNBitsOfUint16(0, 1, 0, 0)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// append 2 bit PacketStatusSymbol
|
|
|
|
|
dst, err = setNBitsOfUint16(dst, 2, 1, r.PacketStatusSymbol)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// append 13 bit RunLength
|
|
|
|
|
dst, err = setNBitsOfUint16(dst, 13, 3, r.RunLength)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
binary.BigEndian.PutUint16(chunk, dst)
|
|
|
|
|
return chunk, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unmarshal ..
|
|
|
|
|
func (r *RunLengthChunk) Unmarshal(rawPacket []byte) error {
|
|
|
|
|
if len(rawPacket) != packetStatusChunkLength {
|
|
|
|
|
return errPacketStatusChunkLength
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// record type
|
|
|
|
|
r.Type = TypeTCCRunLengthChunk
|
|
|
|
|
|
|
|
|
|
// get PacketStatusSymbol
|
|
|
|
|
// r.PacketStatusSymbol = uint16(rawPacket[0] >> 5 & 0x03)
|
|
|
|
|
r.PacketStatusSymbol = getNBitsFromByte(rawPacket[0], 1, 2)
|
|
|
|
|
|
|
|
|
|
// get RunLength
|
|
|
|
|
// r.RunLength = uint16(rawPacket[0]&0x1F)*256 + uint16(rawPacket[1])
|
|
|
|
|
r.RunLength = getNBitsFromByte(rawPacket[0], 3, 5)<<8 + uint16(rawPacket[1])
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StatusVectorChunk T=typeStatusVecotrChunk
|
|
|
|
|
// 0 1
|
|
|
|
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
// |T|S| symbol list |
|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
type StatusVectorChunk struct {
|
|
|
|
|
PacketStatusChunk
|
|
|
|
|
// T = TypeTCCRunLengthChunk
|
|
|
|
|
Type uint16
|
|
|
|
|
|
|
|
|
|
// TypeTCCSymbolSizeOneBit or TypeTCCSymbolSizeTwoBit
|
|
|
|
|
SymbolSize uint16
|
|
|
|
|
|
|
|
|
|
// when SymbolSize = TypeTCCSymbolSizeOneBit, SymbolList is 14*1bit:
|
|
|
|
|
// TypeTCCSymbolListPacketReceived or TypeTCCSymbolListPacketNotReceived
|
|
|
|
|
// when SymbolSize = TypeTCCSymbolSizeTwoBit, SymbolList is 7*2bit:
|
|
|
|
|
// TypeTCCPacketNotReceived TypeTCCPacketReceivedSmallDelta TypeTCCPacketReceivedLargeDelta or typePacketReserved
|
|
|
|
|
SymbolList []uint16
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Marshal ..
|
|
|
|
|
func (r StatusVectorChunk) Marshal() ([]byte, error) {
|
|
|
|
|
chunk := make([]byte, 2)
|
|
|
|
|
|
|
|
|
|
// set first bit '1'
|
|
|
|
|
dst, err := setNBitsOfUint16(0, 1, 0, 1)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set second bit SymbolSize
|
|
|
|
|
dst, err = setNBitsOfUint16(dst, 1, 1, r.SymbolSize)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
numOfBits := numOfBitsOfSymbolSize()[r.SymbolSize]
|
|
|
|
|
// append 14 bit SymbolList
|
|
|
|
|
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)
|
|
|
|
|
// set SymbolList(bit8-15)
|
|
|
|
|
// chunk[1] = uint8(r.SymbolList) & 0x0f
|
|
|
|
|
return chunk, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unmarshal ..
|
|
|
|
|
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 https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.5
|
|
|
|
|
TypeTCCDeltaScaleFactor = 250
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// RecvDelta are represented as multiples of 250us
|
|
|
|
|
// small delta is 1 byte: [0,63.75]ms = [0, 63750]us = [0, 255]*250us
|
|
|
|
|
// big delta is 2 bytes: [-8192.0, 8191.75]ms = [-8192000, 8191750]us = [-32768, 32767]*250us
|
|
|
|
|
// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.5
|
|
|
|
|
type RecvDelta struct {
|
|
|
|
|
Type uint16
|
|
|
|
|
// us
|
|
|
|
|
Delta int64
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Marshal ..
|
|
|
|
|
func (r RecvDelta) Marshal() ([]byte, error) {
|
|
|
|
|
delta := r.Delta / TypeTCCDeltaScaleFactor
|
|
|
|
|
|
|
|
|
|
// small delta
|
|
|
|
|
if r.Type == TypeTCCPacketReceivedSmallDelta && delta >= 0 && delta <= math.MaxUint8 {
|
|
|
|
|
deltaChunk := make([]byte, 1)
|
|
|
|
|
deltaChunk[0] = byte(delta)
|
|
|
|
|
return deltaChunk, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// big delta
|
|
|
|
|
if r.Type == TypeTCCPacketReceivedLargeDelta && delta >= math.MinInt16 && delta <= math.MaxInt16 {
|
|
|
|
|
deltaChunk := make([]byte, 2)
|
|
|
|
|
binary.BigEndian.PutUint16(deltaChunk, uint16(delta))
|
|
|
|
|
return deltaChunk, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// overflow
|
|
|
|
|
return nil, errDeltaExceedLimit
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unmarshal ..
|
|
|
|
|
func (r *RecvDelta) Unmarshal(rawPacket []byte) error {
|
|
|
|
|
chunkLen := len(rawPacket)
|
|
|
|
|
|
|
|
|
|
// must be 1 or 2 bytes
|
|
|
|
|
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 (
|
|
|
|
|
// the offset after header
|
|
|
|
|
baseSequenceNumberOffset = 8
|
|
|
|
|
packetStatusCountOffset = 10
|
|
|
|
|
referenceTimeOffset = 12
|
|
|
|
|
fbPktCountOffset = 15
|
|
|
|
|
packetChunkOffset = 16
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// TransportLayerCC for sender-BWE
|
|
|
|
|
// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5
|
|
|
|
|
type TransportLayerCC struct {
|
|
|
|
|
// header
|
|
|
|
|
Header Header
|
|
|
|
|
|
|
|
|
|
// SSRC of sender
|
|
|
|
|
SenderSSRC uint32
|
|
|
|
|
|
|
|
|
|
// SSRC of the media source
|
|
|
|
|
MediaSSRC uint32
|
|
|
|
|
|
|
|
|
|
// Transport wide sequence of rtp extension
|
|
|
|
|
BaseSequenceNumber uint16
|
|
|
|
|
|
|
|
|
|
// PacketStatusCount
|
|
|
|
|
PacketStatusCount uint16
|
|
|
|
|
|
|
|
|
|
// ReferenceTime
|
|
|
|
|
ReferenceTime uint32
|
|
|
|
|
|
|
|
|
|
// FbPktCount
|
|
|
|
|
FbPktCount uint8
|
|
|
|
|
|
|
|
|
|
// PacketChunks
|
|
|
|
|
PacketChunks []PacketStatusChunk
|
|
|
|
|
|
|
|
|
|
// RecvDeltas
|
|
|
|
|
RecvDeltas []*RecvDelta
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Header returns the Header associated with this packet.
|
|
|
|
|
// func (t *TransportLayerCC) Header() Header {
|
|
|
|
|
// return t.Header
|
|
|
|
|
// return Header{
|
|
|
|
|
// Padding: true,
|
|
|
|
|
// Count: FormatTCC,
|
|
|
|
|
// Type: TypeTCCTransportSpecificFeedback,
|
|
|
|
|
// // https://tools.ietf.org/html/rfc4585#page-33
|
|
|
|
|
// Length: uint16((t.len() / 4) - 1),
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Len return total bytes with padding
|
|
|
|
|
func (t *TransportLayerCC) Len() uint16 {
|
2024-06-05 20:10:03 +00:00
|
|
|
|
return uint16(t.MarshalSize())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MarshalSize returns the size of the packet once marshaled
|
|
|
|
|
func (t *TransportLayerCC) MarshalSize() int {
|
2022-03-10 09:44:48 +00:00
|
|
|
|
n := t.packetLen()
|
|
|
|
|
// has padding
|
|
|
|
|
if n%4 != 0 {
|
|
|
|
|
n = (n/4 + 1) * 4
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
|
return int(n)
|
2022-03-10 09:44:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Marshal encodes the TransportLayerCC in binary
|
|
|
|
|
func (t TransportLayerCC) Marshal() ([]byte, error) {
|
|
|
|
|
header, err := t.Header.Marshal()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
|
payload := make([]byte, t.MarshalSize()-headerLength)
|
2022-03-10 09:44:48 +00:00
|
|
|
|
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 {
|
2024-06-05 20:10:03 +00:00
|
|
|
|
payload[len(payload)-1] = uint8(t.MarshalSize() - int(t.packetLen()))
|
2022-03-10 09:44:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return append(header, payload...), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Unmarshal ..
|
|
|
|
|
func (t *TransportLayerCC) Unmarshal(rawPacket []byte) error { //nolint:gocognit
|
|
|
|
|
if len(rawPacket) < (headerLength + ssrcLength) {
|
|
|
|
|
return errPacketTooShort
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := t.Header.Unmarshal(rawPacket); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://tools.ietf.org/html/rfc4585#page-33
|
|
|
|
|
// header's length + payload's length
|
|
|
|
|
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 := min(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 {
|
2024-06-05 20:10:03 +00:00
|
|
|
|
if recvDeltasPos+1 > totalLength {
|
|
|
|
|
return errPacketTooShort
|
|
|
|
|
}
|
2022-03-10 09:44:48 +00:00
|
|
|
|
err := delta.Unmarshal(rawPacket[recvDeltasPos : recvDeltasPos+1])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
recvDeltasPos++
|
|
|
|
|
}
|
|
|
|
|
if delta.Type == TypeTCCPacketReceivedLargeDelta {
|
2024-06-05 20:10:03 +00:00
|
|
|
|
if recvDeltasPos+2 > totalLength {
|
|
|
|
|
return errPacketTooShort
|
|
|
|
|
}
|
2022-03-10 09:44:48 +00:00
|
|
|
|
err := delta.Unmarshal(rawPacket[recvDeltasPos : recvDeltasPos+2])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
recvDeltasPos += 2
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
|
|
|
|
func (t TransportLayerCC) DestinationSSRC() []uint32 {
|
|
|
|
|
return []uint32{t.MediaSSRC}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func min(x, y uint16) uint16 {
|
|
|
|
|
if x < y {
|
|
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
return y
|
|
|
|
|
}
|