package rtcp
import (
"encoding/binary"
"fmt"
"math"
)
type PacketBitmap uint16
type NackPair struct {
PacketID uint16
LostPackets PacketBitmap
}
type TransportLayerNack struct {
SenderSSRC uint32
MediaSSRC uint32
Nacks []NackPair
}
func NackPairsFromSequenceNumbers (sequenceNumbers []uint16 ) (pairs []NackPair ) {
if len (sequenceNumbers ) == 0 {
return []NackPair {}
}
nackPair := &NackPair {PacketID : sequenceNumbers [0 ]}
for i := 1 ; i < len (sequenceNumbers ); i ++ {
m := sequenceNumbers [i ]
if m -nackPair .PacketID > 16 {
pairs = append (pairs , *nackPair )
nackPair = &NackPair {PacketID : m }
continue
}
nackPair .LostPackets |= 1 << (m - nackPair .PacketID - 1 )
}
pairs = append (pairs , *nackPair )
return
}
func (n *NackPair ) Range (f func (seqno uint16 ) bool ) {
more := f (n .PacketID )
if !more {
return
}
b := n .LostPackets
for i := uint16 (0 ); b != 0 ; i ++ {
if (b & (1 << i )) != 0 {
b &^= (1 << i )
more = f (n .PacketID + i + 1 )
if !more {
return
}
}
}
}
func (n *NackPair ) PacketList () []uint16 {
out := make ([]uint16 , 0 , 17 )
n .Range (func (seqno uint16 ) bool {
out = append (out , seqno )
return true
})
return out
}
const (
tlnLength = 2
nackOffset = 8
)
func (p TransportLayerNack ) Marshal () ([]byte , error ) {
if len (p .Nacks )+tlnLength > math .MaxUint8 {
return nil , errTooManyReports
}
rawPacket := make ([]byte , nackOffset +(len (p .Nacks )*4 ))
binary .BigEndian .PutUint32 (rawPacket , p .SenderSSRC )
binary .BigEndian .PutUint32 (rawPacket [4 :], p .MediaSSRC )
for i := 0 ; i < len (p .Nacks ); i ++ {
binary .BigEndian .PutUint16 (rawPacket [nackOffset +(4 *i ):], p .Nacks [i ].PacketID )
binary .BigEndian .PutUint16 (rawPacket [nackOffset +(4 *i )+2 :], uint16 (p .Nacks [i ].LostPackets ))
}
h := p .Header ()
hData , err := h .Marshal ()
if err != nil {
return nil , err
}
return append (hData , rawPacket ...), nil
}
func (p *TransportLayerNack ) Unmarshal (rawPacket []byte ) error {
if len (rawPacket ) < (headerLength + ssrcLength ) {
return errPacketTooShort
}
var h Header
if err := h .Unmarshal (rawPacket ); err != nil {
return err
}
if len (rawPacket ) < (headerLength + int (4 *h .Length )) {
return errPacketTooShort
}
if h .Type != TypeTransportSpecificFeedback || h .Count != FormatTLN {
return errWrongType
}
if 4 *h .Length <= nackOffset {
return errBadLength
}
p .SenderSSRC = binary .BigEndian .Uint32 (rawPacket [headerLength :])
p .MediaSSRC = binary .BigEndian .Uint32 (rawPacket [headerLength +ssrcLength :])
for i := headerLength + nackOffset ; i < (headerLength + int (h .Length *4 )); i += 4 {
p .Nacks = append (p .Nacks , NackPair {
binary .BigEndian .Uint16 (rawPacket [i :]),
PacketBitmap (binary .BigEndian .Uint16 (rawPacket [i +2 :])),
})
}
return nil
}
func (p *TransportLayerNack ) MarshalSize () int {
return headerLength + nackOffset + (len (p .Nacks ) * 4 )
}
func (p *TransportLayerNack ) Header () Header {
return Header {
Count : FormatTLN ,
Type : TypeTransportSpecificFeedback ,
Length : uint16 ((p .MarshalSize () / 4 ) - 1 ),
}
}
func (p TransportLayerNack ) String () string {
out := fmt .Sprintf ("TransportLayerNack from %x\n" , p .SenderSSRC )
out += fmt .Sprintf ("\tMedia Ssrc %x\n" , p .MediaSSRC )
out += "\tID\tLostPackets\n"
for _ , i := range p .Nacks {
out += fmt .Sprintf ("\t%d\t%b\n" , i .PacketID , i .LostPackets )
}
return out
}
func (p *TransportLayerNack ) DestinationSSRC () []uint32 {
return []uint32 {p .MediaSSRC }
}
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 .