diff options
Diffstat (limited to 'vendor/github.com/pion/rtcp/transport_layer_cc.go')
-rw-r--r-- | vendor/github.com/pion/rtcp/transport_layer_cc.go | 560 |
1 files changed, 560 insertions, 0 deletions
diff --git a/vendor/github.com/pion/rtcp/transport_layer_cc.go b/vendor/github.com/pion/rtcp/transport_layer_cc.go new file mode 100644 index 0000000..9606581 --- /dev/null +++ b/vendor/github.com/pion/rtcp/transport_layer_cc.go @@ -0,0 +1,560 @@ +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 _ Packet = (*TransportLayerCC)(nil) // assert is a Packet + +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 { + delta := d.Delta / TypeTCCDeltaScaleFactor + + // small delta + if delta >= 0 && delta <= math.MaxUint8 { + n++ + } else { + n += 2 + } + } + return n +} + +// Len return total bytes with padding +func (t *TransportLayerCC) Len() uint16 { + n := t.packetLen() + // has padding + if n%4 != 0 { + n = (n/4 + 1) * 4 + } + + return 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 +} + +// Marshal encodes the TransportLayerCC in binary +func (t TransportLayerCC) Marshal() ([]byte, error) { + header, err := t.Header.Marshal() + if err != nil { + return nil, err + } + + payload := make([]byte, t.Len()-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.Len() - t.packetLen()) + } + + 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 recvDeltasPos >= totalLength { + return errPacketTooShort + } + if delta.Type == TypeTCCPacketReceivedSmallDelta { + err := delta.Unmarshal(rawPacket[recvDeltasPos : recvDeltasPos+1]) + if err != nil { + return err + } + recvDeltasPos++ + } + if delta.Type == TypeTCCPacketReceivedLargeDelta { + 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 +} |