-
Notifications
You must be signed in to change notification settings - Fork 54
/
full_intra_request.go
116 lines (97 loc) · 2.93 KB
/
full_intra_request.go
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
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package rtcp
import (
"encoding/binary"
"fmt"
)
// A FIREntry is a (SSRC, seqno) pair, as carried by FullIntraRequest.
type FIREntry struct {
SSRC uint32
SequenceNumber uint8
}
// The FullIntraRequest packet is used to reliably request an Intra frame
// in a video stream. See RFC 5104 Section 3.5.1. This is not for loss
// recovery, which should use PictureLossIndication (PLI) instead.
type FullIntraRequest struct {
SenderSSRC uint32
MediaSSRC uint32
FIR []FIREntry
}
const (
firOffset = 8
)
var _ Packet = (*FullIntraRequest)(nil)
// Marshal encodes the FullIntraRequest
func (p FullIntraRequest) Marshal() ([]byte, error) {
rawPacket := make([]byte, firOffset+(len(p.FIR)*8))
binary.BigEndian.PutUint32(rawPacket, p.SenderSSRC)
binary.BigEndian.PutUint32(rawPacket[4:], p.MediaSSRC)
for i, fir := range p.FIR {
binary.BigEndian.PutUint32(rawPacket[firOffset+8*i:], fir.SSRC)
rawPacket[firOffset+8*i+4] = fir.SequenceNumber
}
h := p.Header()
hData, err := h.Marshal()
if err != nil {
return nil, err
}
return append(hData, rawPacket...), nil
}
// Unmarshal decodes the TransportLayerNack
func (p *FullIntraRequest) 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 != TypePayloadSpecificFeedback || h.Count != FormatFIR {
return errWrongType
}
// The FCI field MUST contain one or more FIR entries
if 4*h.Length-firOffset <= 0 || (4*h.Length)%8 != 0 {
return errBadLength
}
p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:])
for i := headerLength + firOffset; i < (headerLength + int(h.Length*4)); i += 8 {
p.FIR = append(p.FIR, FIREntry{
binary.BigEndian.Uint32(rawPacket[i:]),
rawPacket[i+4],
})
}
return nil
}
// Header returns the Header associated with this packet.
func (p *FullIntraRequest) Header() Header {
return Header{
Count: FormatFIR,
Type: TypePayloadSpecificFeedback,
Length: uint16((p.MarshalSize() / 4) - 1),
}
}
// MarshalSize returns the size of the packet once marshaled
func (p *FullIntraRequest) MarshalSize() int {
return headerLength + firOffset + len(p.FIR)*8
}
func (p *FullIntraRequest) String() string {
out := fmt.Sprintf("FullIntraRequest %x %x",
p.SenderSSRC, p.MediaSSRC)
for _, e := range p.FIR {
out += fmt.Sprintf(" (%x %v)", e.SSRC, e.SequenceNumber)
}
return out
}
// DestinationSSRC returns an array of SSRC values that this packet refers to.
func (p *FullIntraRequest) DestinationSSRC() []uint32 {
ssrcs := make([]uint32, 0, len(p.FIR))
for _, entry := range p.FIR {
ssrcs = append(ssrcs, entry.SSRC)
}
return ssrcs
}