2024-06-05 20:10:03 +00:00
|
|
|
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
2022-03-10 09:44:48 +00:00
|
|
|
package rtcp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
|
|
|
// The ExtendedReport packet is an Implementation of RTCP Extended
|
|
|
|
// Reports defined in RFC 3611. It is used to convey detailed
|
|
|
|
// information about an RTP stream. Each packet contains one or
|
|
|
|
// more report blocks, each of which conveys a different kind of
|
|
|
|
// information.
|
|
|
|
//
|
2024-06-05 20:10:03 +00:00
|
|
|
// 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
|
|
|
|
//
|
2022-03-10 09:44:48 +00:00
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// |V=2|P|reserved | PT=XR=207 | length |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | SSRC |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// : report blocks :
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
type ExtendedReport struct {
|
|
|
|
SenderSSRC uint32 `fmt:"0x%X"`
|
|
|
|
Reports []ReportBlock
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReportBlock represents a single report within an ExtendedReport
|
|
|
|
// packet
|
|
|
|
type ReportBlock interface {
|
|
|
|
DestinationSSRC() []uint32
|
|
|
|
setupBlockHeader()
|
|
|
|
unpackBlockHeader()
|
|
|
|
}
|
|
|
|
|
|
|
|
// TypeSpecificField as described in RFC 3611 section 4.5. In typical
|
|
|
|
// cases, users of ExtendedReports shouldn't need to access this,
|
|
|
|
// and should instead use the corresponding fields in the actual
|
|
|
|
// report blocks themselves.
|
|
|
|
type TypeSpecificField uint8
|
|
|
|
|
|
|
|
// XRHeader defines the common fields that must appear at the start
|
|
|
|
// of each report block. In typical cases, users of ExtendedReports
|
|
|
|
// shouldn't need to access this. For locally-constructed report
|
|
|
|
// blocks, these values will not be accurate until the corresponding
|
|
|
|
// packet is marshaled.
|
|
|
|
type XRHeader struct {
|
|
|
|
BlockType BlockTypeType
|
|
|
|
TypeSpecific TypeSpecificField `fmt:"0x%X"`
|
|
|
|
BlockLength uint16
|
|
|
|
}
|
|
|
|
|
|
|
|
// BlockTypeType specifies the type of report in a report block
|
|
|
|
type BlockTypeType uint8
|
|
|
|
|
|
|
|
// Extended Report block types from RFC 3611.
|
|
|
|
const (
|
|
|
|
LossRLEReportBlockType = 1 // RFC 3611, section 4.1
|
|
|
|
DuplicateRLEReportBlockType = 2 // RFC 3611, section 4.2
|
|
|
|
PacketReceiptTimesReportBlockType = 3 // RFC 3611, section 4.3
|
|
|
|
ReceiverReferenceTimeReportBlockType = 4 // RFC 3611, section 4.4
|
|
|
|
DLRRReportBlockType = 5 // RFC 3611, section 4.5
|
|
|
|
StatisticsSummaryReportBlockType = 6 // RFC 3611, section 4.6
|
|
|
|
VoIPMetricsReportBlockType = 7 // RFC 3611, section 4.7
|
|
|
|
)
|
|
|
|
|
|
|
|
// String converts the Extended report block types into readable strings
|
|
|
|
func (t BlockTypeType) String() string {
|
|
|
|
switch t {
|
|
|
|
case LossRLEReportBlockType:
|
|
|
|
return "LossRLEReportBlockType"
|
|
|
|
case DuplicateRLEReportBlockType:
|
|
|
|
return "DuplicateRLEReportBlockType"
|
|
|
|
case PacketReceiptTimesReportBlockType:
|
|
|
|
return "PacketReceiptTimesReportBlockType"
|
|
|
|
case ReceiverReferenceTimeReportBlockType:
|
|
|
|
return "ReceiverReferenceTimeReportBlockType"
|
|
|
|
case DLRRReportBlockType:
|
|
|
|
return "DLRRReportBlockType"
|
|
|
|
case StatisticsSummaryReportBlockType:
|
|
|
|
return "StatisticsSummaryReportBlockType"
|
|
|
|
case VoIPMetricsReportBlockType:
|
|
|
|
return "VoIPMetricsReportBlockType"
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("invalid value %d", t)
|
|
|
|
}
|
|
|
|
|
|
|
|
// rleReportBlock defines the common structure used by both
|
|
|
|
// Loss RLE report blocks (RFC 3611 §4.1) and Duplicate RLE
|
|
|
|
// report blocks (RFC 3611 §4.2).
|
|
|
|
//
|
2024-06-05 20:10:03 +00:00
|
|
|
// 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
|
|
|
|
//
|
2022-03-10 09:44:48 +00:00
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | BT = 1 or 2 | rsvd. | T | block length |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | SSRC of source |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | begin_seq | end_seq |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | chunk 1 | chunk 2 |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// : ... :
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | chunk n-1 | chunk n |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
type rleReportBlock struct {
|
|
|
|
XRHeader
|
|
|
|
T uint8 `encoding:"omit"`
|
|
|
|
SSRC uint32 `fmt:"0x%X"`
|
|
|
|
BeginSeq uint16
|
|
|
|
EndSeq uint16
|
|
|
|
Chunks []Chunk
|
|
|
|
}
|
|
|
|
|
|
|
|
// Chunk as defined in RFC 3611, section 4.1. These represent information
|
|
|
|
// about packet losses and packet duplication. They have three representations:
|
|
|
|
//
|
|
|
|
// Run Length Chunk:
|
|
|
|
//
|
2024-06-05 20:10:03 +00:00
|
|
|
// 0 1
|
|
|
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// |C|R| run length |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
2022-03-10 09:44:48 +00:00
|
|
|
//
|
|
|
|
// Bit Vector Chunk:
|
|
|
|
//
|
2024-06-05 20:10:03 +00:00
|
|
|
// 0 1
|
|
|
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// |C| bit vector |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
2022-03-10 09:44:48 +00:00
|
|
|
//
|
|
|
|
// Terminating Null Chunk:
|
|
|
|
//
|
2024-06-05 20:10:03 +00:00
|
|
|
// 0 1
|
|
|
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
2022-03-10 09:44:48 +00:00
|
|
|
type Chunk uint16
|
|
|
|
|
|
|
|
// LossRLEReportBlock is used to report information about packet
|
|
|
|
// losses, as described in RFC 3611, section 4.1
|
|
|
|
type LossRLEReportBlock rleReportBlock
|
|
|
|
|
|
|
|
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
|
|
|
func (b *LossRLEReportBlock) DestinationSSRC() []uint32 {
|
|
|
|
return []uint32{b.SSRC}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *LossRLEReportBlock) setupBlockHeader() {
|
|
|
|
b.XRHeader.BlockType = LossRLEReportBlockType
|
|
|
|
b.XRHeader.TypeSpecific = TypeSpecificField(b.T & 0x0F)
|
|
|
|
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *LossRLEReportBlock) unpackBlockHeader() {
|
|
|
|
b.T = uint8(b.XRHeader.TypeSpecific) & 0x0F
|
|
|
|
}
|
|
|
|
|
|
|
|
// DuplicateRLEReportBlock is used to report information about packet
|
|
|
|
// duplication, as described in RFC 3611, section 4.1
|
|
|
|
type DuplicateRLEReportBlock rleReportBlock
|
|
|
|
|
|
|
|
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
|
|
|
func (b *DuplicateRLEReportBlock) DestinationSSRC() []uint32 {
|
|
|
|
return []uint32{b.SSRC}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *DuplicateRLEReportBlock) setupBlockHeader() {
|
|
|
|
b.XRHeader.BlockType = DuplicateRLEReportBlockType
|
|
|
|
b.XRHeader.TypeSpecific = TypeSpecificField(b.T & 0x0F)
|
|
|
|
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *DuplicateRLEReportBlock) unpackBlockHeader() {
|
|
|
|
b.T = uint8(b.XRHeader.TypeSpecific) & 0x0F
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChunkType enumerates the three kinds of chunks described in RFC 3611 section 4.1.
|
|
|
|
type ChunkType uint8
|
|
|
|
|
|
|
|
// These are the valid values that ChunkType can assume
|
|
|
|
const (
|
|
|
|
RunLengthChunkType = 0
|
|
|
|
BitVectorChunkType = 1
|
|
|
|
TerminatingNullChunkType = 2
|
|
|
|
)
|
|
|
|
|
|
|
|
func (c Chunk) String() string {
|
|
|
|
switch c.Type() {
|
|
|
|
case RunLengthChunkType:
|
|
|
|
runType, _ := c.RunType()
|
|
|
|
return fmt.Sprintf("[RunLength type=%d, length=%d]", runType, c.Value())
|
|
|
|
case BitVectorChunkType:
|
|
|
|
return fmt.Sprintf("[BitVector 0b%015b]", c.Value())
|
|
|
|
case TerminatingNullChunkType:
|
|
|
|
return "[TerminatingNull]"
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("[0x%X]", uint16(c))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Type returns the ChunkType that this Chunk represents
|
|
|
|
func (c Chunk) Type() ChunkType {
|
|
|
|
if c == 0 {
|
|
|
|
return TerminatingNullChunkType
|
|
|
|
}
|
|
|
|
return ChunkType(c >> 15)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RunType returns the RunType that this Chunk represents. It is
|
|
|
|
// only valid if ChunkType is RunLengthChunkType.
|
|
|
|
func (c Chunk) RunType() (uint, error) {
|
|
|
|
if c.Type() != RunLengthChunkType {
|
|
|
|
return 0, errWrongChunkType
|
|
|
|
}
|
|
|
|
return uint((c >> 14) & 0x01), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Value returns the value represented in this Chunk
|
|
|
|
func (c Chunk) Value() uint {
|
|
|
|
switch c.Type() {
|
|
|
|
case RunLengthChunkType:
|
|
|
|
return uint(c & 0x3FFF)
|
|
|
|
case BitVectorChunkType:
|
|
|
|
return uint(c & 0x7FFF)
|
|
|
|
case TerminatingNullChunkType:
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return uint(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PacketReceiptTimesReportBlock represents a Packet Receipt Times
|
|
|
|
// report block, as described in RFC 3611 section 4.3.
|
|
|
|
//
|
2024-06-05 20:10:03 +00:00
|
|
|
// 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
|
|
|
|
//
|
2022-03-10 09:44:48 +00:00
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | BT=3 | rsvd. | T | block length |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | SSRC of source |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | begin_seq | end_seq |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | Receipt time of packet begin_seq |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | Receipt time of packet (begin_seq + 1) mod 65536 |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// : ... :
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | Receipt time of packet (end_seq - 1) mod 65536 |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
type PacketReceiptTimesReportBlock struct {
|
|
|
|
XRHeader
|
|
|
|
T uint8 `encoding:"omit"`
|
|
|
|
SSRC uint32 `fmt:"0x%X"`
|
|
|
|
BeginSeq uint16
|
|
|
|
EndSeq uint16
|
|
|
|
ReceiptTime []uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
|
|
|
func (b *PacketReceiptTimesReportBlock) DestinationSSRC() []uint32 {
|
|
|
|
return []uint32{b.SSRC}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *PacketReceiptTimesReportBlock) setupBlockHeader() {
|
|
|
|
b.XRHeader.BlockType = PacketReceiptTimesReportBlockType
|
|
|
|
b.XRHeader.TypeSpecific = TypeSpecificField(b.T & 0x0F)
|
|
|
|
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *PacketReceiptTimesReportBlock) unpackBlockHeader() {
|
|
|
|
b.T = uint8(b.XRHeader.TypeSpecific) & 0x0F
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReceiverReferenceTimeReportBlock encodes a Receiver Reference Time
|
|
|
|
// report block as described in RFC 3611 section 4.4.
|
|
|
|
//
|
2024-06-05 20:10:03 +00:00
|
|
|
// 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
|
|
|
|
//
|
2022-03-10 09:44:48 +00:00
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | BT=4 | reserved | block length = 2 |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | NTP timestamp, most significant word |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | NTP timestamp, least significant word |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
type ReceiverReferenceTimeReportBlock struct {
|
|
|
|
XRHeader
|
|
|
|
NTPTimestamp uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
|
|
|
func (b *ReceiverReferenceTimeReportBlock) DestinationSSRC() []uint32 {
|
|
|
|
return []uint32{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *ReceiverReferenceTimeReportBlock) setupBlockHeader() {
|
|
|
|
b.XRHeader.BlockType = ReceiverReferenceTimeReportBlockType
|
|
|
|
b.XRHeader.TypeSpecific = 0
|
|
|
|
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *ReceiverReferenceTimeReportBlock) unpackBlockHeader() {
|
|
|
|
}
|
|
|
|
|
|
|
|
// DLRRReportBlock encodes a DLRR Report Block as described in
|
|
|
|
// RFC 3611 section 4.5.
|
|
|
|
//
|
2024-06-05 20:10:03 +00:00
|
|
|
// 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
|
|
|
|
//
|
2022-03-10 09:44:48 +00:00
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | BT=5 | reserved | block length |
|
|
|
|
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
|
|
// | SSRC_1 (SSRC of first receiver) | sub-
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
|
|
|
|
// | last RR (LRR) | 1
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | delay since last RR (DLRR) |
|
|
|
|
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
|
|
// | SSRC_2 (SSRC of second receiver) | sub-
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
|
|
|
|
// : ... : 2
|
|
|
|
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
|
|
type DLRRReportBlock struct {
|
|
|
|
XRHeader
|
|
|
|
Reports []DLRRReport
|
|
|
|
}
|
|
|
|
|
|
|
|
// DLRRReport encodes a single report inside a DLRRReportBlock.
|
|
|
|
type DLRRReport struct {
|
|
|
|
SSRC uint32 `fmt:"0x%X"`
|
|
|
|
LastRR uint32
|
|
|
|
DLRR uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
|
|
|
func (b *DLRRReportBlock) DestinationSSRC() []uint32 {
|
|
|
|
ssrc := make([]uint32, len(b.Reports))
|
|
|
|
for i, r := range b.Reports {
|
|
|
|
ssrc[i] = r.SSRC
|
|
|
|
}
|
|
|
|
return ssrc
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *DLRRReportBlock) setupBlockHeader() {
|
|
|
|
b.XRHeader.BlockType = DLRRReportBlockType
|
|
|
|
b.XRHeader.TypeSpecific = 0
|
|
|
|
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *DLRRReportBlock) unpackBlockHeader() {
|
|
|
|
}
|
|
|
|
|
|
|
|
// StatisticsSummaryReportBlock encodes a Statistics Summary Report
|
|
|
|
// Block as described in RFC 3611, section 4.6.
|
|
|
|
//
|
2024-06-05 20:10:03 +00:00
|
|
|
// 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
|
|
|
|
//
|
2022-03-10 09:44:48 +00:00
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | BT=6 |L|D|J|ToH|rsvd.| block length = 9 |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | SSRC of source |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | begin_seq | end_seq |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | lost_packets |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | dup_packets |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | min_jitter |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | max_jitter |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | mean_jitter |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | dev_jitter |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | min_ttl_or_hl | max_ttl_or_hl |mean_ttl_or_hl | dev_ttl_or_hl |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
type StatisticsSummaryReportBlock struct {
|
|
|
|
XRHeader
|
|
|
|
LossReports bool `encoding:"omit"`
|
|
|
|
DuplicateReports bool `encoding:"omit"`
|
|
|
|
JitterReports bool `encoding:"omit"`
|
|
|
|
TTLorHopLimit TTLorHopLimitType `encoding:"omit"`
|
|
|
|
SSRC uint32 `fmt:"0x%X"`
|
|
|
|
BeginSeq uint16
|
|
|
|
EndSeq uint16
|
|
|
|
LostPackets uint32
|
|
|
|
DupPackets uint32
|
|
|
|
MinJitter uint32
|
|
|
|
MaxJitter uint32
|
|
|
|
MeanJitter uint32
|
|
|
|
DevJitter uint32
|
|
|
|
MinTTLOrHL uint8
|
|
|
|
MaxTTLOrHL uint8
|
|
|
|
MeanTTLOrHL uint8
|
|
|
|
DevTTLOrHL uint8
|
|
|
|
}
|
|
|
|
|
|
|
|
// TTLorHopLimitType encodes values for the ToH field in
|
|
|
|
// a StatisticsSummaryReportBlock
|
|
|
|
type TTLorHopLimitType uint8
|
|
|
|
|
|
|
|
// Values for TTLorHopLimitType
|
|
|
|
const (
|
|
|
|
ToHMissing = 0
|
|
|
|
ToHIPv4 = 1
|
|
|
|
ToHIPv6 = 2
|
|
|
|
)
|
|
|
|
|
|
|
|
func (t TTLorHopLimitType) String() string {
|
|
|
|
switch t {
|
|
|
|
case ToHMissing:
|
|
|
|
return "[ToH Missing]"
|
|
|
|
case ToHIPv4:
|
|
|
|
return "[ToH = IPv4]"
|
|
|
|
case ToHIPv6:
|
|
|
|
return "[ToH = IPv6]"
|
|
|
|
}
|
|
|
|
return "[ToH Flag is Invalid]"
|
|
|
|
}
|
|
|
|
|
|
|
|
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
|
|
|
func (b *StatisticsSummaryReportBlock) DestinationSSRC() []uint32 {
|
|
|
|
return []uint32{b.SSRC}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *StatisticsSummaryReportBlock) setupBlockHeader() {
|
|
|
|
b.XRHeader.BlockType = StatisticsSummaryReportBlockType
|
|
|
|
b.XRHeader.TypeSpecific = 0x00
|
|
|
|
if b.LossReports {
|
|
|
|
b.XRHeader.TypeSpecific |= 0x80
|
|
|
|
}
|
|
|
|
if b.DuplicateReports {
|
|
|
|
b.XRHeader.TypeSpecific |= 0x40
|
|
|
|
}
|
|
|
|
if b.JitterReports {
|
|
|
|
b.XRHeader.TypeSpecific |= 0x20
|
|
|
|
}
|
|
|
|
b.XRHeader.TypeSpecific |= TypeSpecificField((b.TTLorHopLimit & 0x03) << 3)
|
|
|
|
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *StatisticsSummaryReportBlock) unpackBlockHeader() {
|
|
|
|
b.LossReports = b.XRHeader.TypeSpecific&0x80 != 0
|
|
|
|
b.DuplicateReports = b.XRHeader.TypeSpecific&0x40 != 0
|
|
|
|
b.JitterReports = b.XRHeader.TypeSpecific&0x20 != 0
|
|
|
|
b.TTLorHopLimit = TTLorHopLimitType((b.XRHeader.TypeSpecific & 0x18) >> 3)
|
|
|
|
}
|
|
|
|
|
|
|
|
// VoIPMetricsReportBlock encodes a VoIP Metrics Report Block as described
|
|
|
|
// in RFC 3611, section 4.7.
|
|
|
|
//
|
2024-06-05 20:10:03 +00:00
|
|
|
// 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
|
|
|
|
//
|
2022-03-10 09:44:48 +00:00
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | BT=7 | reserved | block length = 8 |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | SSRC of source |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | loss rate | discard rate | burst density | gap density |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | burst duration | gap duration |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | round trip delay | end system delay |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | signal level | noise level | RERL | Gmin |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | R factor | ext. R factor | MOS-LQ | MOS-CQ |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | RX config | reserved | JB nominal |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
// | JB maximum | JB abs max |
|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
type VoIPMetricsReportBlock struct {
|
|
|
|
XRHeader
|
|
|
|
SSRC uint32 `fmt:"0x%X"`
|
|
|
|
LossRate uint8
|
|
|
|
DiscardRate uint8
|
|
|
|
BurstDensity uint8
|
|
|
|
GapDensity uint8
|
|
|
|
BurstDuration uint16
|
|
|
|
GapDuration uint16
|
|
|
|
RoundTripDelay uint16
|
|
|
|
EndSystemDelay uint16
|
|
|
|
SignalLevel uint8
|
|
|
|
NoiseLevel uint8
|
|
|
|
RERL uint8
|
|
|
|
Gmin uint8
|
|
|
|
RFactor uint8
|
|
|
|
ExtRFactor uint8
|
|
|
|
MOSLQ uint8
|
|
|
|
MOSCQ uint8
|
|
|
|
RXConfig uint8
|
|
|
|
_ uint8
|
|
|
|
JBNominal uint16
|
|
|
|
JBMaximum uint16
|
|
|
|
JBAbsMax uint16
|
|
|
|
}
|
|
|
|
|
|
|
|
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
|
|
|
func (b *VoIPMetricsReportBlock) DestinationSSRC() []uint32 {
|
|
|
|
return []uint32{b.SSRC}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *VoIPMetricsReportBlock) setupBlockHeader() {
|
|
|
|
b.XRHeader.BlockType = VoIPMetricsReportBlockType
|
|
|
|
b.XRHeader.TypeSpecific = 0
|
|
|
|
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *VoIPMetricsReportBlock) unpackBlockHeader() {
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnknownReportBlock is used to store bytes for any report block
|
|
|
|
// that has an unknown Report Block Type.
|
|
|
|
type UnknownReportBlock struct {
|
|
|
|
XRHeader
|
|
|
|
Bytes []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// DestinationSSRC returns an array of SSRC values that this report block refers to.
|
|
|
|
func (b *UnknownReportBlock) DestinationSSRC() []uint32 {
|
|
|
|
return []uint32{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *UnknownReportBlock) setupBlockHeader() {
|
|
|
|
b.XRHeader.BlockLength = uint16(wireSize(b)/4 - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *UnknownReportBlock) unpackBlockHeader() {
|
|
|
|
}
|
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
// MarshalSize returns the size of the packet once marshaled
|
|
|
|
func (x ExtendedReport) MarshalSize() int {
|
|
|
|
return wireSize(x)
|
|
|
|
}
|
|
|
|
|
2022-03-10 09:44:48 +00:00
|
|
|
// Marshal encodes the ExtendedReport in binary
|
|
|
|
func (x ExtendedReport) Marshal() ([]byte, error) {
|
|
|
|
for _, p := range x.Reports {
|
|
|
|
p.setupBlockHeader()
|
|
|
|
}
|
|
|
|
|
|
|
|
length := wireSize(x)
|
|
|
|
|
|
|
|
// RTCP Header
|
|
|
|
header := Header{
|
|
|
|
Type: TypeExtendedReport,
|
|
|
|
Length: uint16(length / 4),
|
|
|
|
}
|
|
|
|
headerBuffer, err := header.Marshal()
|
|
|
|
if err != nil {
|
|
|
|
return []byte{}, err
|
|
|
|
}
|
|
|
|
length += len(headerBuffer)
|
|
|
|
|
|
|
|
rawPacket := make([]byte, length)
|
|
|
|
buffer := packetBuffer{bytes: rawPacket}
|
|
|
|
|
|
|
|
err = buffer.write(headerBuffer)
|
|
|
|
if err != nil {
|
|
|
|
return []byte{}, err
|
|
|
|
}
|
|
|
|
err = buffer.write(x)
|
|
|
|
if err != nil {
|
|
|
|
return []byte{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return rawPacket, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unmarshal decodes the ExtendedReport from binary
|
|
|
|
func (x *ExtendedReport) Unmarshal(b []byte) error {
|
|
|
|
var header Header
|
|
|
|
if err := header.Unmarshal(b); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if header.Type != TypeExtendedReport {
|
|
|
|
return errWrongType
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer := packetBuffer{bytes: b[headerLength:]}
|
|
|
|
err := buffer.read(&x.SenderSSRC)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for len(buffer.bytes) > 0 {
|
|
|
|
var block ReportBlock
|
|
|
|
|
|
|
|
headerBuffer := buffer
|
|
|
|
xrHeader := XRHeader{}
|
|
|
|
err = headerBuffer.read(&xrHeader)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch xrHeader.BlockType {
|
|
|
|
case LossRLEReportBlockType:
|
|
|
|
block = new(LossRLEReportBlock)
|
|
|
|
case DuplicateRLEReportBlockType:
|
|
|
|
block = new(DuplicateRLEReportBlock)
|
|
|
|
case PacketReceiptTimesReportBlockType:
|
|
|
|
block = new(PacketReceiptTimesReportBlock)
|
|
|
|
case ReceiverReferenceTimeReportBlockType:
|
|
|
|
block = new(ReceiverReferenceTimeReportBlock)
|
|
|
|
case DLRRReportBlockType:
|
|
|
|
block = new(DLRRReportBlock)
|
|
|
|
case StatisticsSummaryReportBlockType:
|
|
|
|
block = new(StatisticsSummaryReportBlock)
|
|
|
|
case VoIPMetricsReportBlockType:
|
|
|
|
block = new(VoIPMetricsReportBlock)
|
|
|
|
default:
|
|
|
|
block = new(UnknownReportBlock)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We need to limit the amount of data available to
|
|
|
|
// this block to the actual length of the block
|
|
|
|
blockLength := (int(xrHeader.BlockLength) + 1) * 4
|
|
|
|
blockBuffer := buffer.split(blockLength)
|
|
|
|
err = blockBuffer.read(block)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
block.unpackBlockHeader()
|
|
|
|
x.Reports = append(x.Reports, block)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
|
|
|
func (x *ExtendedReport) DestinationSSRC() []uint32 {
|
2024-06-05 20:10:03 +00:00
|
|
|
ssrc := make([]uint32, 0, len(x.Reports)+1)
|
|
|
|
ssrc = append(ssrc, x.SenderSSRC)
|
2022-03-10 09:44:48 +00:00
|
|
|
for _, p := range x.Reports {
|
|
|
|
ssrc = append(ssrc, p.DestinationSSRC()...)
|
|
|
|
}
|
|
|
|
return ssrc
|
|
|
|
}
|
|
|
|
|
|
|
|
func (x *ExtendedReport) String() string {
|
|
|
|
return stringify(x)
|
|
|
|
}
|