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
2 changes: 1 addition & 1 deletion application_defined.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func (a *ApplicationDefined) Unmarshal(rawPacket []byte) error {
return err
}
if len(rawPacket) < 12 {
return errPacketTooShort
return errPacketTooShortFor(a)
}

if int(header.Length+1)*4 != len(rawPacket) {
Expand Down
56 changes: 55 additions & 1 deletion errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

package rtcp

import "errors"
import (
"errors"
"fmt"
"reflect"
)

var (
errWrongMarshalSize = errors.New("rtcp: wrong marshal size")
Expand Down Expand Up @@ -39,3 +43,53 @@ var (
errAppDefinedDataTooLarge = errors.New("rtcp: application defined data is too large")
errAppDefinedInvalidName = errors.New("rtcp: application defined name must be 4 ASCII chars")
)

type packetTooShortError struct {
packet string
}

func (e packetTooShortError) Error() string {
if e.packet == "" {
return errPacketTooShort.Error()
}

return fmt.Sprintf("%s (%s)", errPacketTooShort.Error(), e.packet)
}

func (e packetTooShortError) Unwrap() error {
return errPacketTooShort
}

func errPacketTooShortFor(packet any) error {
name := packetTypeName(packet)
if name == "" {
return errPacketTooShort
}

return packetTooShortError{packet: name}
}

func packetTypeName(packet any) string {
if packet == nil {
return ""
}

if name, ok := packet.(string); ok {
return name
}

typ := reflect.TypeOf(packet)
if typ == nil {
return ""
}

for typ.Kind() == reflect.Pointer {
typ = typ.Elem()
}

if typ.Name() != "" {
return typ.Name()
}

return typ.String()
}
103 changes: 103 additions & 0 deletions errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package rtcp

import (
"errors"
"testing"

"github.com/stretchr/testify/assert"
)

func TestErrPacketTooShortForPacket(t *testing.T) {
t.Parallel()

err := errPacketTooShortFor(&PictureLossIndication{})

assert.ErrorIs(t, err, errPacketTooShort)
assert.Contains(t, err.Error(), "PictureLossIndication")
}

func TestErrPacketTooShortForString(t *testing.T) {
t.Parallel()

err := errPacketTooShortFor("CustomPacket")

assert.ErrorIs(t, err, errPacketTooShort)
assert.Contains(t, err.Error(), "CustomPacket")
}

func TestErrPacketTooShortForNil(t *testing.T) {
t.Parallel()

err := errPacketTooShortFor(nil)

assert.True(t, errors.Is(err, errPacketTooShort))
assert.Equal(t, errPacketTooShort, err)
}

func TestPacketNameFromHeader(t *testing.T) {
t.Parallel()

tests := []struct {
name string
header Header
want string
}{
{
name: "sender report",
header: Header{
Type: TypeSenderReport,
},
want: "SenderReport",
},
{
name: "transport feedback",
header: Header{
Type: TypeTransportSpecificFeedback,
Count: FormatTLN,
},
want: "TransportLayerNack",
},
{
name: "transport feedback fallback",
header: Header{
Type: TypeTransportSpecificFeedback,
Count: 99,
},
want: "TransportSpecificFeedback(FMT=99)",
},
{
name: "payload specific fallback",
header: Header{
Type: TypePayloadSpecificFeedback,
Count: 5,
},
want: "PayloadSpecificFeedback(FMT=5)",
},
{
name: "payload specific known",
header: Header{
Type: TypePayloadSpecificFeedback,
Count: FormatPLI,
},
want: "PictureLossIndication",
},
{
name: "unknown type",
header: Header{
Type: 199,
},
want: "PacketType(199)",
},
}

for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
assert.Equal(t, test.want, packetNameFromHeader(test.header))
})
}
}
4 changes: 2 additions & 2 deletions full_intra_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (p FullIntraRequest) Marshal() ([]byte, error) {
// Unmarshal decodes the TransportLayerNack.
func (p *FullIntraRequest) Unmarshal(rawPacket []byte) error {
if len(rawPacket) < (headerLength + ssrcLength) {
return errPacketTooShort
return errPacketTooShortFor(p)
}

var header Header
Expand All @@ -60,7 +60,7 @@ func (p *FullIntraRequest) Unmarshal(rawPacket []byte) error {
}

if len(rawPacket) < (headerLength + int(4*header.Length)) {
return errPacketTooShort
return errPacketTooShortFor(p)
}

if header.Type != TypePayloadSpecificFeedback || header.Count != FormatFIR {
Expand Down
6 changes: 3 additions & 3 deletions goodbye.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ func (g *Goodbye) Unmarshal(rawPacket []byte) error {
}

if getPadding(len(rawPacket)) != 0 {
return errPacketTooShort
return errPacketTooShortFor(g)
}

g.Sources = make([]uint32, header.Count)

reasonOffset := int(headerLength + header.Count*ssrcLength)
if reasonOffset > len(rawPacket) {
return errPacketTooShort
return errPacketTooShortFor(g)
}

for i := 0; i < int(header.Count); i++ {
Expand All @@ -111,7 +111,7 @@ func (g *Goodbye) Unmarshal(rawPacket []byte) error {
reasonEnd := reasonOffset + 1 + reasonLen

if reasonEnd > len(rawPacket) {
return errPacketTooShort
return errPacketTooShortFor(g)
}

g.Reason = string(rawPacket[reasonOffset+1 : reasonEnd])
Expand Down
2 changes: 1 addition & 1 deletion header.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (h Header) Marshal() ([]byte, error) {
// Unmarshal decodes the Header from binary.
func (h *Header) Unmarshal(rawPacket []byte) error {
if len(rawPacket) < headerLength {
return errPacketTooShort
return errPacketTooShortFor(h)
}

/*
Expand Down
59 changes: 58 additions & 1 deletion packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,31 @@

package rtcp

import "fmt"

var packetTypeNames = map[PacketType]string{
TypeSenderReport: "SenderReport",
TypeReceiverReport: "ReceiverReport",
TypeSourceDescription: "SourceDescription",
TypeGoodbye: "Goodbye",
TypeExtendedReport: "ExtendedReport",
TypeApplicationDefined: "ApplicationDefined",
}

var transportSpecificFeedbackNames = map[uint8]string{
FormatTLN: "TransportLayerNack",
FormatRRR: "RapidResynchronizationRequest",
FormatTCC: "TransportLayerCC",
FormatCCFB: "CCFeedbackReport",
}

var payloadSpecificFeedbackNames = map[uint8]string{
FormatPLI: "PictureLossIndication",
FormatSLI: "SliceLossIndication",
FormatREMB: "ReceiverEstimatedMaximumBitrate",
FormatFIR: "FullIntraRequest",
}

// Packet represents an RTCP packet, a protocol used for out-of-band statistics
// and control information for an RTP session.
type Packet interface {
Expand Down Expand Up @@ -70,7 +95,7 @@ func unmarshal(rawData []byte) (packet Packet, bytesprocessed int, err error) {

bytesprocessed = int(header.Length+1) * 4
if bytesprocessed > len(rawData) {
return nil, 0, errPacketTooShort
return nil, 0, errPacketTooShortFor(packetNameFromHeader(header))
}
inPacket := rawData[:bytesprocessed]

Expand Down Expand Up @@ -129,3 +154,35 @@ func unmarshal(rawData []byte) (packet Packet, bytesprocessed int, err error) {

return packet, bytesprocessed, err
}

func packetNameFromHeader(header Header) string {
if header.Type == TypeTransportSpecificFeedback {
return transportSpecificFeedbackName(header.Count)
}

if header.Type == TypePayloadSpecificFeedback {
return payloadSpecificFeedbackName(header.Count)
}

if name, ok := packetTypeNames[header.Type]; ok {
return name
}

return fmt.Sprintf("PacketType(%d)", header.Type)
}

func transportSpecificFeedbackName(count uint8) string {
if name, ok := transportSpecificFeedbackNames[count]; ok {
return name
}

return fmt.Sprintf("TransportSpecificFeedback(FMT=%d)", count)
}

func payloadSpecificFeedbackName(count uint8) string {
if name, ok := payloadSpecificFeedbackNames[count]; ok {
return name
}

return fmt.Sprintf("PayloadSpecificFeedback(FMT=%d)", count)
}
1 change: 1 addition & 0 deletions packet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,5 @@ func TestInvalidHeaderLength(t *testing.T) {

_, err := Unmarshal(invalidPacket)
assert.ErrorIs(t, err, errPacketTooShort)
assert.Contains(t, err.Error(), "ReceiverReport")
}
2 changes: 1 addition & 1 deletion picture_loss_indication.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (p PictureLossIndication) Marshal() ([]byte, error) {
// Unmarshal decodes the PictureLossIndication from binary.
func (p *PictureLossIndication) Unmarshal(rawPacket []byte) error {
if len(rawPacket) < (headerLength + (ssrcLength * 2)) {
return errPacketTooShort
return errPacketTooShortFor(p)
}

var h Header
Expand Down
2 changes: 1 addition & 1 deletion rapid_resynchronization_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (p RapidResynchronizationRequest) Marshal() ([]byte, error) {
// Unmarshal decodes the RapidResynchronizationRequest from binary.
func (p *RapidResynchronizationRequest) Unmarshal(rawPacket []byte) error {
if len(rawPacket) < (headerLength + (ssrcLength * 2)) {
return errPacketTooShort
return errPacketTooShortFor(p)
}

var h Header
Expand Down
2 changes: 1 addition & 1 deletion raw_packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (r RawPacket) Marshal() ([]byte, error) {
// Unmarshal decodes the packet from binary.
func (r *RawPacket) Unmarshal(b []byte) error {
if len(b) < (headerLength) {
return errPacketTooShort
return errPacketTooShortFor(r)
}
*r = b

Expand Down
6 changes: 3 additions & 3 deletions receiver_estimated_maximum_bitrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (p ReceiverEstimatedMaximumBitrate) MarshalTo(buf []byte) (n int, err error

size := p.MarshalSize()
if len(buf) < size {
return 0, errPacketTooShort
return 0, errPacketTooShortFor(p)
}

buf[0] = 143 // v=2, p=0, fmt=15
Expand Down Expand Up @@ -158,7 +158,7 @@ func (p *ReceiverEstimatedMaximumBitrate) Unmarshal(buf []byte) (err error) {

// 20 bytes is the size of the packet with no SSRCs
if len(buf) < 20 {
return errPacketTooShort
return errPacketTooShortFor(p)
}

// version must be 2
Expand Down Expand Up @@ -195,7 +195,7 @@ func (p *ReceiverEstimatedMaximumBitrate) Unmarshal(buf []byte) (err error) {

// Make sure the buffer is large enough.
if len(buf) < size {
return errPacketTooShort
return errPacketTooShortFor(p)
}

// The sender SSRC is 32-bits
Expand Down
2 changes: 1 addition & 1 deletion receiver_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (r *ReceiverReport) Unmarshal(rawPacket []byte) error {
*/

if len(rawPacket) < (headerLength + ssrcLength) {
return errPacketTooShort
return errPacketTooShortFor(r)
}

var header Header
Expand Down
2 changes: 1 addition & 1 deletion reception_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func (r ReceptionReport) Marshal() ([]byte, error) {
// Unmarshal decodes the ReceptionReport from binary.
func (r *ReceptionReport) Unmarshal(rawPacket []byte) error {
if len(rawPacket) < receptionReportLength {
return errPacketTooShort
return errPacketTooShortFor(r)
}

/*
Expand Down
2 changes: 1 addition & 1 deletion rfc8888.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func (b CCFeedbackReport) String() string {
// Unmarshal decodes the Congestion Control Feedback Report from binary.
func (b *CCFeedbackReport) Unmarshal(rawPacket []byte) error {
if len(rawPacket) < headerLength+ssrcLength+reportTimestampLength {
return errPacketTooShort
return errPacketTooShortFor(b)
}

var h Header
Expand Down
Loading