summaryrefslogtreecommitdiff
path: root/vendor/github.com/pion/rtcp/full_intra_request.go
blob: 74ca928d6e2ea9772c16fe78d7023ad726902821 (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
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
	}

	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.len() / 4) - 1),
	}
}

func (p *FullIntraRequest) len() 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
}