summaryrefslogtreecommitdiff
path: root/vendor/github.com/pion/rtcp/goodbye.go
blob: b8718b357bd9a24f0307d46149ec964767d428c9 (plain)
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
package rtcp

import (
	"encoding/binary"
)

// 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
}

var _ Packet = (*Goodbye)(nil) // assert is a Packet

// 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
}