1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
package rtcp
import (
"encoding/binary"
"fmt"
)
// The Goodbye packet indicates that one or more sources are no longer active.
type Goodbye struct {
// The SSRC/CSRC identifiers that are no longer active
Sources []uint32
// Optional text indicating the reason for leaving, e.g., "camera malfunction" or "RTP loop detected"
Reason string
}
// Marshal encodes the Goodbye packet in binary
func (g Goodbye) Marshal() ([]byte, error) {
/*
* 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| SC | PT=BYE=203 | length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | SSRC/CSRC |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* : ... :
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
* (opt) | length | reason for leaving ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
rawPacket := make([]byte, g.len())
packetBody := rawPacket[headerLength:]
if len(g.Sources) > countMax {
return nil, errTooManySources
}
for i, s := range g.Sources {
binary.BigEndian.PutUint32(packetBody[i*ssrcLength:], s)
}
if g.Reason != "" {
reason := []byte(g.Reason)
if len(reason) > sdesMaxOctetCount {
return nil, errReasonTooLong
}
reasonOffset := len(g.Sources) * ssrcLength
packetBody[reasonOffset] = uint8(len(reason))
copy(packetBody[reasonOffset+1:], reason)
}
hData, err := g.Header().Marshal()
if err != nil {
return nil, err
}
copy(rawPacket, hData)
return rawPacket, nil
}
// Unmarshal decodes the Goodbye packet from binary
func (g *Goodbye) Unmarshal(rawPacket []byte) error {
/*
* 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| SC | PT=BYE=203 | length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | SSRC/CSRC |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* : ... :
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
* (opt) | length | reason for leaving ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
var header Header
if err := header.Unmarshal(rawPacket); err != nil {
return err
}
if header.Type != TypeGoodbye {
return errWrongType
}
if getPadding(len(rawPacket)) != 0 {
return errPacketTooShort
}
g.Sources = make([]uint32, header.Count)
reasonOffset := int(headerLength + header.Count*ssrcLength)
if reasonOffset > len(rawPacket) {
return errPacketTooShort
}
for i := 0; i < int(header.Count); i++ {
offset := headerLength + i*ssrcLength
g.Sources[i] = binary.BigEndian.Uint32(rawPacket[offset:])
}
if reasonOffset < len(rawPacket) {
reasonLen := int(rawPacket[reasonOffset])
reasonEnd := reasonOffset + 1 + reasonLen
if reasonEnd > len(rawPacket) {
return errPacketTooShort
}
g.Reason = string(rawPacket[reasonOffset+1 : reasonEnd])
}
return nil
}
// Header returns the Header associated with this packet.
func (g *Goodbye) Header() Header {
return Header{
Padding: false,
Count: uint8(len(g.Sources)),
Type: TypeGoodbye,
Length: uint16((g.len() / 4) - 1),
}
}
func (g *Goodbye) len() int {
srcsLength := len(g.Sources) * ssrcLength
reasonLength := len(g.Reason) + 1
l := headerLength + srcsLength + reasonLength
// align to 32-bit boundary
return l + getPadding(l)
}
// DestinationSSRC returns an array of SSRC values that this packet refers to.
func (g *Goodbye) DestinationSSRC() []uint32 {
out := make([]uint32, len(g.Sources))
copy(out, g.Sources)
return out
}
func (g Goodbye) String() string {
out := "Goodbye\n"
for i, s := range g.Sources {
out += fmt.Sprintf("\tSource %d: %x\n", i, s)
}
out += fmt.Sprintf("\tReason: %s\n", g.Reason)
return out
}
|