mirror of
https://github.com/pion/webrtc.git
synced 2025-10-16 12:10:44 +08:00
Add RTCP Messages
*Avoid a type switch in srtp.go *Add support for transport layer nack and slice loss indication messages *Add String() functions for several RTCP packets to aid in debugging *Add support for transparent profile extensions to ReceiverReport messages Relates to #119
This commit is contained in:

committed by
Woodrow Douglass

parent
0ef6602adb
commit
f62abe50e8
@@ -56,7 +56,7 @@ func handleRTCP(getBufferTransports func(uint32) *TransportPair, buffer []byte)
|
|||||||
//decrypted packets can also be compound packets, so we have to nest our reader loop here.
|
//decrypted packets can also be compound packets, so we have to nest our reader loop here.
|
||||||
compoundPacket := rtcp.NewReader(bytes.NewReader(buffer))
|
compoundPacket := rtcp.NewReader(bytes.NewReader(buffer))
|
||||||
for {
|
for {
|
||||||
header, rawrtcp, err := compoundPacket.ReadPacket()
|
_, rawrtcp, err := compoundPacket.ReadPacket()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@@ -67,7 +67,7 @@ func handleRTCP(getBufferTransports func(uint32) *TransportPair, buffer []byte)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var report rtcp.Packet
|
var report rtcp.Packet
|
||||||
report, header, err = rtcp.Unmarshal(rawrtcp)
|
report, _, err = rtcp.Unmarshal(rawrtcp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
@@ -83,28 +83,9 @@ func handleRTCP(getBufferTransports func(uint32) *TransportPair, buffer []byte)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch header.Type {
|
for _, ssrc := range report.DestinationSSRC() {
|
||||||
case rtcp.TypeSenderReport:
|
|
||||||
for _, ssrc := range report.(*rtcp.SenderReport).Reports {
|
|
||||||
f(ssrc.SSRC)
|
|
||||||
}
|
|
||||||
case rtcp.TypeReceiverReport:
|
|
||||||
for _, ssrc := range report.(*rtcp.ReceiverReport).Reports {
|
|
||||||
f(ssrc.SSRC)
|
|
||||||
}
|
|
||||||
case rtcp.TypeSourceDescription:
|
|
||||||
for _, ssrc := range report.(*rtcp.SourceDescription).Chunks {
|
|
||||||
f(ssrc.Source)
|
|
||||||
}
|
|
||||||
case rtcp.TypeGoodbye:
|
|
||||||
for _, ssrc := range report.(*rtcp.Goodbye).Sources {
|
|
||||||
f(ssrc)
|
f(ssrc)
|
||||||
}
|
}
|
||||||
case rtcp.TypeTransportSpecificFeedback:
|
|
||||||
f(report.(*rtcp.RapidResynchronizationRequest).MediaSSRC)
|
|
||||||
case rtcp.TypePayloadSpecificFeedback:
|
|
||||||
f(report.(*rtcp.PictureLossIndication).MediaSSRC)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -138,3 +138,10 @@ func (g *Goodbye) len() int {
|
|||||||
// align to 32-bit boundary
|
// align to 32-bit boundary
|
||||||
return l + util.GetPadding(l)
|
return l + util.GetPadding(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
||||||
|
func (g *Goodbye) DestinationSSRC() []uint32 {
|
||||||
|
out := make([]uint32, len(g.Sources))
|
||||||
|
copy(out, g.Sources)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
@@ -3,6 +3,8 @@ package rtcp
|
|||||||
// Packet represents an RTCP packet, a protocol used for out-of-band statistics and control information for an RTP session
|
// Packet represents an RTCP packet, a protocol used for out-of-band statistics and control information for an RTP session
|
||||||
type Packet interface {
|
type Packet interface {
|
||||||
Header() Header
|
Header() Header
|
||||||
|
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
||||||
|
DestinationSSRC() []uint32
|
||||||
|
|
||||||
Marshal() ([]byte, error)
|
Marshal() ([]byte, error)
|
||||||
Unmarshal(rawPacket []byte) error
|
Unmarshal(rawPacket []byte) error
|
||||||
@@ -32,10 +34,24 @@ func Unmarshal(rawPacket []byte) (Packet, Header, error) {
|
|||||||
p = new(Goodbye)
|
p = new(Goodbye)
|
||||||
|
|
||||||
case TypeTransportSpecificFeedback:
|
case TypeTransportSpecificFeedback:
|
||||||
|
switch h.Count {
|
||||||
|
case tlnFMT:
|
||||||
|
p = new(TransportLayerNack)
|
||||||
|
case rrrFMT:
|
||||||
p = new(RapidResynchronizationRequest)
|
p = new(RapidResynchronizationRequest)
|
||||||
|
default:
|
||||||
|
p = new(RawPacket)
|
||||||
|
}
|
||||||
|
|
||||||
case TypePayloadSpecificFeedback:
|
case TypePayloadSpecificFeedback:
|
||||||
|
switch h.Count {
|
||||||
|
case pliFMT:
|
||||||
p = new(PictureLossIndication)
|
p = new(PictureLossIndication)
|
||||||
|
case sliFMT:
|
||||||
|
p = new(SliceLossIndication)
|
||||||
|
default:
|
||||||
|
p = new(RawPacket)
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
p = new(RawPacket)
|
p = new(RawPacket)
|
||||||
|
@@ -2,6 +2,7 @@ package rtcp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The PictureLossIndication packet informs the encoder about the loss of an undefined amount of coded video data belonging to one or more pictures
|
// The PictureLossIndication packet informs the encoder about the loss of an undefined amount of coded video data belonging to one or more pictures
|
||||||
@@ -76,3 +77,12 @@ func (p *PictureLossIndication) Header() Header {
|
|||||||
func (p *PictureLossIndication) len() int {
|
func (p *PictureLossIndication) len() int {
|
||||||
return headerLength + ssrcLength*2
|
return headerLength + ssrcLength*2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PictureLossIndication) String() string {
|
||||||
|
return fmt.Sprintf("PictureLossIndication %x %x", p.SenderSSRC, p.MediaSSRC)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
||||||
|
func (p *PictureLossIndication) DestinationSSRC() []uint32 {
|
||||||
|
return []uint32{p.MediaSSRC}
|
||||||
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package rtcp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The RapidResynchronizationRequest packet informs the encoder about the loss of an undefined amount of coded video data belonging to one or more pictures
|
// The RapidResynchronizationRequest packet informs the encoder about the loss of an undefined amount of coded video data belonging to one or more pictures
|
||||||
@@ -76,3 +77,12 @@ func (p *RapidResynchronizationRequest) Header() Header {
|
|||||||
Length: rrrLength,
|
Length: rrrLength,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
||||||
|
func (p *RapidResynchronizationRequest) DestinationSSRC() []uint32 {
|
||||||
|
return []uint32{p.MediaSSRC}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RapidResynchronizationRequest) String() string {
|
||||||
|
return fmt.Sprintf("RapidResynchronizationRequest %x %x", p.SenderSSRC, p.MediaSSRC)
|
||||||
|
}
|
||||||
|
@@ -32,3 +32,8 @@ func (r RawPacket) Header() Header {
|
|||||||
}
|
}
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
||||||
|
func (r *RawPacket) DestinationSSRC() []uint32 {
|
||||||
|
return []uint32{}
|
||||||
|
}
|
||||||
|
@@ -96,6 +96,7 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
LastSenderReport: 0x9f36432,
|
LastSenderReport: 0x9f36432,
|
||||||
Delay: 150137,
|
Delay: 150137,
|
||||||
}},
|
}},
|
||||||
|
ProfileExtensions: []uint8{},
|
||||||
}
|
}
|
||||||
if got, want := wantRR, parsed; !reflect.DeepEqual(got, want) {
|
if got, want := wantRR, parsed; !reflect.DeepEqual(got, want) {
|
||||||
t.Errorf("Unmarshal rr: got %#v, want %#v", got, want)
|
t.Errorf("Unmarshal rr: got %#v, want %#v", got, want)
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
package rtcp
|
package rtcp
|
||||||
|
|
||||||
import "encoding/binary"
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// A ReceiverReport (RR) packet provides reception quality feedback for an RTP stream
|
// A ReceiverReport (RR) packet provides reception quality feedback for an RTP stream
|
||||||
type ReceiverReport struct {
|
type ReceiverReport struct {
|
||||||
@@ -11,6 +14,8 @@ type ReceiverReport struct {
|
|||||||
// block conveys statistics on the reception of RTP packets from a
|
// block conveys statistics on the reception of RTP packets from a
|
||||||
// single synchronization source.
|
// single synchronization source.
|
||||||
Reports []ReceptionReport
|
Reports []ReceptionReport
|
||||||
|
// extra data from the end of the packet; the application can parse this if needed.
|
||||||
|
ProfileExtensions []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -67,7 +72,10 @@ func (r ReceiverReport) Marshal() ([]byte, error) {
|
|||||||
return nil, errTooManyReports
|
return nil, errTooManyReports
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rawPacket = append(rawPacket, r.ProfileExtensions...)
|
||||||
|
|
||||||
hData, err := r.Header().Marshal()
|
hData, err := r.Header().Marshal()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -128,6 +136,7 @@ func (r *ReceiverReport) Unmarshal(rawPacket []byte) error {
|
|||||||
}
|
}
|
||||||
r.Reports = append(r.Reports, rr)
|
r.Reports = append(r.Reports, rr)
|
||||||
}
|
}
|
||||||
|
r.ProfileExtensions = rawPacket[rrReportOffset+(len(r.Reports)*receptionReportLength):]
|
||||||
|
|
||||||
if uint8(len(r.Reports)) != h.Count {
|
if uint8(len(r.Reports)) != h.Count {
|
||||||
return errInvalidHeader
|
return errInvalidHeader
|
||||||
@@ -152,3 +161,22 @@ func (r *ReceiverReport) Header() Header {
|
|||||||
Length: uint16((r.len() / 4) - 1),
|
Length: uint16((r.len() / 4) - 1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
||||||
|
func (r *ReceiverReport) DestinationSSRC() []uint32 {
|
||||||
|
out := make([]uint32, len(r.Reports))
|
||||||
|
for i, v := range r.Reports {
|
||||||
|
out[i] = v.SSRC
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r ReceiverReport) String() string {
|
||||||
|
out := fmt.Sprintf("ReceiverReport from %x\n", r.SSRC)
|
||||||
|
out += fmt.Sprintf("\tSSRC \tLost\tLastSequence\n")
|
||||||
|
for _, i := range r.Reports {
|
||||||
|
out += fmt.Sprintf("\t%x\t%d/%d\t%d\n", i.SSRC, i.FractionLost, i.TotalLost, i.LastSequenceNumber)
|
||||||
|
}
|
||||||
|
out += fmt.Sprintf("\tProfile Extension Data: %v\n", r.ProfileExtensions)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
@@ -43,6 +43,7 @@ func TestReceiverReportUnmarshal(t *testing.T) {
|
|||||||
LastSenderReport: 0x9f36432,
|
LastSenderReport: 0x9f36432,
|
||||||
Delay: 150137,
|
Delay: 150137,
|
||||||
}},
|
}},
|
||||||
|
ProfileExtensions: []uint8{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -79,6 +80,9 @@ func TestReceiverReportUnmarshal(t *testing.T) {
|
|||||||
LastSenderReport: 0x9f36432,
|
LastSenderReport: 0x9f36432,
|
||||||
Delay: 150137,
|
Delay: 150137,
|
||||||
}},
|
}},
|
||||||
|
ProfileExtensions: []uint8{
|
||||||
|
0x54, 0x45, 0x53, 0x54,
|
||||||
|
0x44, 0x41, 0x54, 0x41},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -147,14 +151,14 @@ func TestReceiverReportUnmarshal(t *testing.T) {
|
|||||||
var rr ReceiverReport
|
var rr ReceiverReport
|
||||||
err := rr.Unmarshal(test.Data)
|
err := rr.Unmarshal(test.Data)
|
||||||
if got, want := err, test.WantError; got != want {
|
if got, want := err, test.WantError; got != want {
|
||||||
t.Fatalf("Unmarshal %q rr: err = %v, want %v", test.Name, got, want)
|
t.Fatalf("Unmarshal %q rr: err = %#v, want %#v", test.Name, got, want)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if got, want := rr, test.Want; !reflect.DeepEqual(got, want) {
|
if got, want := rr, test.Want; !reflect.DeepEqual(got, want) {
|
||||||
t.Fatalf("Unmarshal %q rr: got %v, want %v", test.Name, got, want)
|
t.Fatalf("Unmarshal %q rr: got %#v, want %#v", test.Name, got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,6 +187,7 @@ func TestReceiverReportRoundTrip(t *testing.T) {
|
|||||||
SSRC: 0,
|
SSRC: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ProfileExtensions: []uint8{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -200,6 +205,7 @@ func TestReceiverReportRoundTrip(t *testing.T) {
|
|||||||
Delay: 46,
|
Delay: 46,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ProfileExtensions: []uint8{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -223,7 +229,7 @@ func TestReceiverReportRoundTrip(t *testing.T) {
|
|||||||
} {
|
} {
|
||||||
data, err := test.Report.Marshal()
|
data, err := test.Report.Marshal()
|
||||||
if got, want := err, test.WantError; got != want {
|
if got, want := err, test.WantError; got != want {
|
||||||
t.Fatalf("Marshal %q: err = %v, want %v", test.Name, got, want)
|
t.Fatalf("Marshal %q: err = %#v, want %#v", test.Name, got, want)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
@@ -231,7 +237,7 @@ func TestReceiverReportRoundTrip(t *testing.T) {
|
|||||||
|
|
||||||
var decoded ReceiverReport
|
var decoded ReceiverReport
|
||||||
if err := decoded.Unmarshal(data); err != nil {
|
if err := decoded.Unmarshal(data); err != nil {
|
||||||
t.Fatalf("Unmarshal %q: %v", test.Name, err)
|
t.Fatalf("Unmarshal %q: %#v", test.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if got, want := decoded, test.Report; !reflect.DeepEqual(got, want) {
|
if got, want := decoded, test.Report; !reflect.DeepEqual(got, want) {
|
||||||
|
@@ -31,8 +31,6 @@ type SenderReport struct {
|
|||||||
// block conveys statistics on the reception of RTP packets from a
|
// block conveys statistics on the reception of RTP packets from a
|
||||||
// single synchronization source.
|
// single synchronization source.
|
||||||
Reports []ReceptionReport
|
Reports []ReceptionReport
|
||||||
|
|
||||||
header Header
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -196,6 +194,15 @@ func (r *SenderReport) Unmarshal(rawPacket []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
||||||
|
func (r *SenderReport) DestinationSSRC() []uint32 {
|
||||||
|
out := make([]uint32, len(r.Reports))
|
||||||
|
for i, v := range r.Reports {
|
||||||
|
out[i] = v.SSRC
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
func (r *SenderReport) len() int {
|
func (r *SenderReport) len() int {
|
||||||
repsLength := 0
|
repsLength := 0
|
||||||
for _, rep := range r.Reports {
|
for _, rep := range r.Reports {
|
||||||
|
115
pkg/rtcp/slice_loss_indication.go
Normal file
115
pkg/rtcp/slice_loss_indication.go
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
package rtcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The SliceLossIndication packet informs the encoder about the loss of a picture slice
|
||||||
|
type SliceLossIndication struct {
|
||||||
|
// SSRC of sender
|
||||||
|
SenderSSRC uint32
|
||||||
|
|
||||||
|
// SSRC of the media source
|
||||||
|
MediaSSRC uint32
|
||||||
|
|
||||||
|
SLI []struct {
|
||||||
|
// ID of first lost slice
|
||||||
|
First uint16
|
||||||
|
|
||||||
|
// Number of lost slices
|
||||||
|
Number uint16
|
||||||
|
|
||||||
|
// ID of related picture
|
||||||
|
Picture uint8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
sliFMT = 2
|
||||||
|
sliLength = 2
|
||||||
|
sliOffset = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// Marshal encodes the SliceLossIndication in binary
|
||||||
|
func (p SliceLossIndication) Marshal() ([]byte, error) {
|
||||||
|
|
||||||
|
if len(p.SLI)+sliLength > math.MaxUint8 {
|
||||||
|
return nil, errTooManyReports
|
||||||
|
}
|
||||||
|
|
||||||
|
rawPacket := make([]byte, sliOffset+(len(p.SLI)*4))
|
||||||
|
binary.BigEndian.PutUint32(rawPacket, p.SenderSSRC)
|
||||||
|
binary.BigEndian.PutUint32(rawPacket[4:], p.MediaSSRC)
|
||||||
|
for i := 0; i < len(p.SLI); i++ {
|
||||||
|
sli := ((uint32(p.SLI[i].First) & 0x1FFF) << 19) |
|
||||||
|
((uint32(p.SLI[i].Number) & 0x1FFF) << 6) |
|
||||||
|
(uint32(p.SLI[i].Picture) & 0x3F)
|
||||||
|
binary.BigEndian.PutUint32(rawPacket[sliOffset+(4*i):], sli)
|
||||||
|
}
|
||||||
|
h := Header{
|
||||||
|
Count: tlnFMT,
|
||||||
|
Type: TypeTransportSpecificFeedback,
|
||||||
|
Length: uint16(tlnLength + len(p.SLI)),
|
||||||
|
}
|
||||||
|
hData, err := h.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(hData, rawPacket...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal decodes the SliceLossIndication from binary
|
||||||
|
func (p *SliceLossIndication) Unmarshal(rawPacket []byte) error {
|
||||||
|
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 != TypeTransportSpecificFeedback || h.Count != tlnFMT {
|
||||||
|
return errWrongType
|
||||||
|
}
|
||||||
|
|
||||||
|
p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
|
||||||
|
p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:])
|
||||||
|
for i := headerLength + sliOffset; i < (headerLength + int(h.Length*4)); i += 4 {
|
||||||
|
sli := binary.BigEndian.Uint32(rawPacket[i:])
|
||||||
|
p.SLI = append(p.SLI, struct {
|
||||||
|
First uint16
|
||||||
|
Number uint16
|
||||||
|
Picture uint8
|
||||||
|
}{
|
||||||
|
uint16((sli >> 19) & 0x1FFF),
|
||||||
|
uint16((sli >> 6) & 0x1FFF),
|
||||||
|
uint8(sli & 0x3F)})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SliceLossIndication) len() int {
|
||||||
|
return headerLength + sliOffset + (len(p.SLI) * 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header returns the Header associated with this packet.
|
||||||
|
func (p *SliceLossIndication) Header() Header {
|
||||||
|
return Header{
|
||||||
|
Count: sliFMT,
|
||||||
|
Type: TypeTransportSpecificFeedback,
|
||||||
|
Length: uint16((p.len() / 4) - 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SliceLossIndication) String() string {
|
||||||
|
return fmt.Sprintf("SliceLossIndication %x %x %+v", p.SenderSSRC, p.MediaSSRC, p.SLI)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
||||||
|
func (p *SliceLossIndication) DestinationSSRC() []uint32 {
|
||||||
|
return []uint32{p.MediaSSRC}
|
||||||
|
}
|
@@ -332,3 +332,12 @@ func (s *SourceDescriptionItem) Unmarshal(rawPacket []byte) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
||||||
|
func (s *SourceDescription) DestinationSSRC() []uint32 {
|
||||||
|
out := make([]uint32, len(s.Chunks))
|
||||||
|
for i, v := range s.Chunks {
|
||||||
|
out[i] = v.Source
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
114
pkg/rtcp/transport_layer_nack.go
Normal file
114
pkg/rtcp/transport_layer_nack.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package rtcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The TransportLayerNack packet informs the encoder about the loss of a transport packet
|
||||||
|
type TransportLayerNack struct {
|
||||||
|
// SSRC of sender
|
||||||
|
SenderSSRC uint32
|
||||||
|
|
||||||
|
// SSRC of the media source
|
||||||
|
MediaSSRC uint32
|
||||||
|
|
||||||
|
Nacks []struct {
|
||||||
|
// ID of lost packets
|
||||||
|
PacketID uint16
|
||||||
|
|
||||||
|
// Bitmask of following lost packets
|
||||||
|
BLP uint16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
tlnFMT = 1
|
||||||
|
tlnLength = 2
|
||||||
|
nackOffset = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// Marshal encodes the TransportLayerNack in binary
|
||||||
|
func (p TransportLayerNack) Marshal() ([]byte, error) {
|
||||||
|
|
||||||
|
if len(p.Nacks)+tlnLength > math.MaxUint8 {
|
||||||
|
return nil, errTooManyReports
|
||||||
|
}
|
||||||
|
|
||||||
|
rawPacket := make([]byte, nackOffset+(len(p.Nacks)*4))
|
||||||
|
binary.BigEndian.PutUint32(rawPacket, p.SenderSSRC)
|
||||||
|
binary.BigEndian.PutUint32(rawPacket[4:], p.MediaSSRC)
|
||||||
|
for i := 0; i < len(p.Nacks); i++ {
|
||||||
|
binary.BigEndian.PutUint16(rawPacket[nackOffset+(4*i):], p.Nacks[i].PacketID)
|
||||||
|
binary.BigEndian.PutUint16(rawPacket[nackOffset+(4*i)+2:], p.Nacks[i].BLP)
|
||||||
|
}
|
||||||
|
h := p.Header()
|
||||||
|
hData, err := h.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(hData, rawPacket...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal decodes the TransportLayerNack from binary
|
||||||
|
func (p *TransportLayerNack) Unmarshal(rawPacket []byte) error {
|
||||||
|
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 != TypeTransportSpecificFeedback || h.Count != tlnFMT {
|
||||||
|
return errWrongType
|
||||||
|
}
|
||||||
|
|
||||||
|
p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
|
||||||
|
p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:])
|
||||||
|
for i := headerLength + nackOffset; i < (headerLength + int(h.Length*4)); i += 4 {
|
||||||
|
p.Nacks = append(p.Nacks, struct {
|
||||||
|
PacketID uint16
|
||||||
|
BLP uint16
|
||||||
|
}{
|
||||||
|
binary.BigEndian.Uint16(rawPacket[i:]),
|
||||||
|
binary.BigEndian.Uint16(rawPacket[i+2:])})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TransportLayerNack) len() int {
|
||||||
|
return headerLength + nackOffset + (len(p.Nacks) * 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header returns the Header associated with this packet.
|
||||||
|
func (p *TransportLayerNack) Header() Header {
|
||||||
|
return Header{
|
||||||
|
Count: tlnFMT,
|
||||||
|
Type: TypeTransportSpecificFeedback,
|
||||||
|
Length: uint16((p.len() / 4) - 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TransportLayerNack) String() string {
|
||||||
|
o := "Packets Lost:\n"
|
||||||
|
for _, n := range p.Nacks {
|
||||||
|
b := n.BLP
|
||||||
|
o += fmt.Sprintf("\t%d\n", n.PacketID)
|
||||||
|
for i := uint16(0); b != 0; i++ {
|
||||||
|
if (b & (1 << i)) != 0 {
|
||||||
|
b &^= (1 << i)
|
||||||
|
o += fmt.Sprintf("\t%d\n", n.PacketID+i+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestinationSSRC returns an array of SSRC values that this packet refers to.
|
||||||
|
func (p *TransportLayerNack) DestinationSSRC() []uint32 {
|
||||||
|
return []uint32{p.MediaSSRC}
|
||||||
|
}
|
@@ -2,6 +2,7 @@ package rtp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
@@ -47,6 +48,21 @@ const (
|
|||||||
csrcLength = 4
|
csrcLength = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// String helps with debugging by printing packet information in a readable way
|
||||||
|
func (p Packet) String() string {
|
||||||
|
out := "RTP PACKET:\n"
|
||||||
|
|
||||||
|
out += fmt.Sprintf("\tVersion: %v\n", p.Version)
|
||||||
|
out += fmt.Sprintf("\tMarker: %v\n", p.Marker)
|
||||||
|
out += fmt.Sprintf("\tPayload Type: %d\n", p.PayloadType)
|
||||||
|
out += fmt.Sprintf("\tSequence Number: %d\n", p.SequenceNumber)
|
||||||
|
out += fmt.Sprintf("\tTimestamp: %d\n", p.Timestamp)
|
||||||
|
out += fmt.Sprintf("\tSSRC: %d (%x)\n", p.SSRC, p.SSRC)
|
||||||
|
out += fmt.Sprintf("\tPayload Length: %d\n", len(p.Payload))
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// Unmarshal parses the passed byte slice and stores the result in the Packet this method is called upon
|
// Unmarshal parses the passed byte slice and stores the result in the Packet this method is called upon
|
||||||
func (p *Packet) Unmarshal(rawPacket []byte) error {
|
func (p *Packet) Unmarshal(rawPacket []byte) error {
|
||||||
if len(rawPacket) < headerLength {
|
if len(rawPacket) < headerLength {
|
||||||
|
Reference in New Issue
Block a user