Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions application_defined.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ func (a *ApplicationDefined) Unmarshal(rawPacket []byte) error {
return nil
}

func appendApplicationDefinedSSRCs(dst []uint32, header Header, pkt []byte) ([]uint32, error) {
if header.Type != TypeApplicationDefined {
return dst, errWrongType
}
if len(pkt) < 12 {
return dst, errPacketTooShort
}

return append(dst, binary.BigEndian.Uint32(pkt[4:8])), nil
}

// MarshalSize returns the size of the packet once marshaled.
func (a *ApplicationDefined) MarshalSize() int {
dataLength := len(a.Data)
Expand Down
46 changes: 46 additions & 0 deletions extended_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package rtcp

import (
"encoding/binary"
"fmt"
)

Expand Down Expand Up @@ -661,6 +662,51 @@ func (x *ExtendedReport) Unmarshal(b []byte) error {
return nil
}

const (
xrHeaderLength = 4
dlrrReportLength = 12
)

func appendXRSSRCs(dst []uint32, header Header, pkt []byte) ([]uint32, error) {
if header.Type != TypeExtendedReport {
return dst, errWrongType
}
if len(pkt) < headerLength+ssrcLength {
return dst, errPacketTooShort
}
// The sender's SSRC, then one SSRC per report block that refers to a
// source (the receiver reference time block does not); see
// ExtendedReport for the packet layout.
dst = append(dst, binary.BigEndian.Uint32(pkt[headerLength:]))
for offset := headerLength + ssrcLength; offset < len(pkt); {
if offset+xrHeaderLength > len(pkt) {
return dst, errPacketTooShort
}
// Each block begins with an XRHeader.
blockLen := (int(binary.BigEndian.Uint16(pkt[offset+2:])) + 1) * 4
// A block whose declared length overruns the packet is truncated at
// the packet end, mirroring packetBuffer.split.
if offset+blockLen > len(pkt) {
blockLen = len(pkt) - offset
}
switch pkt[offset] {
case LossRLEReportBlockType, DuplicateRLEReportBlockType, PacketReceiptTimesReportBlockType,
StatisticsSummaryReportBlockType, VoIPMetricsReportBlockType:
if blockLen < xrHeaderLength+ssrcLength {
return dst, errPacketTooShort
}
dst = append(dst, binary.BigEndian.Uint32(pkt[offset+xrHeaderLength:]))
case DLRRReportBlockType:
for sub := offset + xrHeaderLength; sub+dlrrReportLength <= offset+blockLen; sub += dlrrReportLength {
dst = append(dst, binary.BigEndian.Uint32(pkt[sub:]))
}
}
offset += blockLen
}

return dst, nil
}

// DestinationSSRC returns an array of SSRC values that this packet refers to.
func (x *ExtendedReport) DestinationSSRC() []uint32 {
ssrc := make([]uint32, 0, len(x.Reports)+1)
Expand Down
15 changes: 15 additions & 0 deletions full_intra_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,21 @@ func (p *FullIntraRequest) Unmarshal(rawPacket []byte) error {
return nil
}

func appendFIRSSRCs(dst []uint32, header Header, pkt []byte) ([]uint32, error) {
if header.Type != TypePayloadSpecificFeedback || header.Count != FormatFIR {
return dst, errWrongType
}
if 4*int(header.Length)-firOffset <= 0 || (4*int(header.Length))%8 != 0 {
return dst, errBadLength
}
for i := headerLength + firOffset; i < (headerLength + 4*int(header.Length)); i += 8 {
entry := pkt[i : i+8]
dst = append(dst, binary.BigEndian.Uint32(entry))
}

return dst, nil
}

// Header returns the Header associated with this packet.
func (p *FullIntraRequest) Header() Header {
return Header{
Expand Down
17 changes: 17 additions & 0 deletions goodbye.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,23 @@ func (g *Goodbye) Unmarshal(rawPacket []byte) error {
return nil
}

func appendGoodbyeSSRCs(dst []uint32, header Header, pkt []byte) ([]uint32, error) {
if header.Type != TypeGoodbye {
return dst, errWrongType
}
reasonOffset := int(headerLength + header.Count*ssrcLength)
if reasonOffset > len(pkt) {
return dst, errPacketTooShort
}
for i := 0; i < int(header.Count); i++ {
offset := headerLength + i*ssrcLength

dst = append(dst, binary.BigEndian.Uint32(pkt[offset:]))
}

return dst, nil
}

// Header returns the Header associated with this packet.
func (g *Goodbye) Header() Header {
return Header{
Expand Down
138 changes: 137 additions & 1 deletion raw_packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

package rtcp

import "fmt"
import (
"encoding/binary"
"fmt"
)

// RawPacket represents an unparsed RTCP packet. It's returned by Unmarshal when
// a packet with an unknown type is encountered.
Expand Down Expand Up @@ -51,3 +54,136 @@ func (r RawPacket) String() string {
func (r RawPacket) MarshalSize() int {
return len(r)
}

// UnmarshalRaw takes an entire udp datagram (which may consist of multiple
// RTCP packets) and returns the raw packets it contains, without unmarshaling
// their contents.
//
// The returned packets are subslices of data, not copies.
func UnmarshalRaw(data []byte) ([]RawPacket, error) {
return AppendRawPackets(nil, data)
}

// AppendRawPackets appends the raw packets of the datagram data, as produced
// by UnmarshalRaw, to dst and returns the extended slice. On error dst is
// returned unchanged.
func AppendRawPackets(dst []RawPacket, data []byte) ([]RawPacket, error) {
orig := dst
found := 0
for rest := data; len(rest) > 0; {
var header Header
if err := header.Unmarshal(rest); err != nil {
return orig, err
}
size := (int(header.Length) + 1) * 4
if size > len(rest) {
return orig, errPacketTooShort
}
dst = append(dst, RawPacket(rest[:size:size]))
rest = rest[size:]
found++
}
if found == 0 {
return orig, errInvalidHeader
}

return dst, nil
}

// ParseDestinationSSRC parses the destination SSRCs of the packet out of its
// raw bytes, appends them to dst, and returns the extended slice. It reports
// the SSRCs that Unmarshal followed by Packet.DestinationSSRC would for every
// packet type known to this package; unknown packet types have none. r must
// be a single RTCP packet, as produced by UnmarshalRaw.
//
// Only the structure needed to locate the SSRCs is validated; a packet
// accepted by this method may still fail a full Unmarshal.
//
// ParseDestinationSSRC will only allocate in order to resize dst as needed;
// the parsing itself is allocation-free.
//
//nolint:cyclop
func (r RawPacket) ParseDestinationSSRC(dst []uint32) ([]uint32, error) {
pkt := []byte(r)
var header Header
if err := header.Unmarshal(pkt); err != nil {
return dst, err
}
if size := (int(header.Length) + 1) * 4; size != len(pkt) {
return dst, errBadLength
}

switch header.Type {
case TypeSenderReport:
return appendSenderReportSSRCs(dst, header, pkt)
case TypeReceiverReport:
return appendReceiverReportSSRCs(dst, header, pkt)
case TypeSourceDescription:
return appendSourceDescriptionSSRCs(dst, header, pkt)
case TypeGoodbye:
return appendGoodbyeSSRCs(dst, header, pkt)
case TypeApplicationDefined:
return appendApplicationDefinedSSRCs(dst, header, pkt)
case TypeTransportSpecificFeedback:
switch header.Count {
case FormatRRR:
var rrr RapidResynchronizationRequest
if err := rrr.Unmarshal(pkt); err != nil {
return dst, err
}

return append(dst, rrr.MediaSSRC), nil
case FormatTLN, FormatTCC:
return appendMediaSSRC(dst, header, pkt)
case FormatCCFB:
return appendCCFBSSRCs(dst, header, pkt)
}
case TypePayloadSpecificFeedback:
switch header.Count {
case FormatPLI:
var pli PictureLossIndication
if err := pli.Unmarshal(pkt); err != nil {
return dst, err
}

return append(dst, pli.MediaSSRC), nil
case FormatSLI:
return appendMediaSSRC(dst, header, pkt)
case FormatFIR:
return appendFIRSSRCs(dst, header, pkt)
case FormatREMB:
return appendREMBSSRCs(dst, header, pkt)
}
case TypeExtendedReport:
return appendXRSSRCs(dst, header, pkt)
}

return dst, nil
}

// appendMediaSSRC handles the feedback packets whose only destination is the
// media SSRC in the fixed part of the packet and whose full Unmarshal
// allocates (SLI, NACK, TWCC). They share the common packet format for
// feedback messages:
//
// https://tools.ietf.org/html/rfc4585#section-6.1
//
// 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| FMT | PT | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of packet sender |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | SSRC of media source |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// : Feedback Control Information (FCI) :
// : :
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
func appendMediaSSRC(dst []uint32, _ Header, pkt []byte) ([]uint32, error) {
if len(pkt) < (headerLength + (ssrcLength * 2)) {
return dst, errPacketTooShort
}

return append(dst, binary.BigEndian.Uint32(pkt[headerLength+ssrcLength:])), nil
}
Loading
Loading