mirror of
				https://github.com/pion/webrtc.git
				synced 2025-10-31 18:52:55 +08:00 
			
		
		
		
	| @@ -33,6 +33,13 @@ type Header struct { | |||||||
| 	Length uint16 | 	Length uint16 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | var ( | ||||||
|  | 	errInvalidVersion     = errors.New("invalid version") | ||||||
|  | 	errInvalidReportCount = errors.New("invalid report count") | ||||||
|  | 	errInvalidTotalLost   = errors.New("invalid total lost count") | ||||||
|  | 	errPacketTooShort     = errors.New("packet too short") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| const ( | const ( | ||||||
| 	headerLength     = 4 | 	headerLength     = 4 | ||||||
| 	versionShift     = 6 | 	versionShift     = 6 | ||||||
| @@ -43,12 +50,6 @@ const ( | |||||||
| 	reportCountMask  = 0x1f | 	reportCountMask  = 0x1f | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( |  | ||||||
| 	errInvalidVersion     = errors.New("invalid version") |  | ||||||
| 	errInvalidReportCount = errors.New("invalid report count") |  | ||||||
| 	errHeaderTooShort     = errors.New("rtcp header too short") |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Marshal encodes the Header in binary | // Marshal encodes the Header in binary | ||||||
| func (h Header) Marshal() ([]byte, error) { | func (h Header) Marshal() ([]byte, error) { | ||||||
| 	/* | 	/* | ||||||
| @@ -84,7 +85,7 @@ func (h Header) Marshal() ([]byte, error) { | |||||||
| // Unmarshal decodes the Header from binary | // Unmarshal decodes the Header from binary | ||||||
| func (h *Header) Unmarshal(rawPacket []byte) error { | func (h *Header) Unmarshal(rawPacket []byte) error { | ||||||
| 	if len(rawPacket) < headerLength { | 	if len(rawPacket) < headerLength { | ||||||
| 		return errHeaderTooShort | 		return errPacketTooShort | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* | 	/* | ||||||
| @@ -33,8 +33,8 @@ func TestHeaderUnmarshal(t *testing.T) { | |||||||
| func TestHeaderUnmarshalNil(t *testing.T) { | func TestHeaderUnmarshalNil(t *testing.T) { | ||||||
| 	var header Header | 	var header Header | ||||||
| 	err := header.Unmarshal(nil) | 	err := header.Unmarshal(nil) | ||||||
| 	if got, want := err, errHeaderTooShort; got != want { | 	if got, want := err, errPacketTooShort; got != want { | ||||||
| 		t.Errorf("unmarshal nil header: err = %v, want %v", got, want) | 		t.Fatalf("unmarshal nil header: err = %v, want %v", got, want) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| func TestHeaderRoundTrip(t *testing.T) { | func TestHeaderRoundTrip(t *testing.T) { | ||||||
							
								
								
									
										50
									
								
								pkg/rtcp/receiver_report.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								pkg/rtcp/receiver_report.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | package rtcp | ||||||
|  |  | ||||||
|  | import "encoding/binary" | ||||||
|  |  | ||||||
|  | // A ReceiverReport (RR) packet provides reception quality feedback for an RTP stream | ||||||
|  | type ReceiverReport struct { | ||||||
|  | 	// The synchronization source identifier for the originator of this RR packet. | ||||||
|  | 	SSRC uint32 | ||||||
|  | 	// Zero or more reception report blocks depending on the number of other | ||||||
|  | 	// sources heard by this sender since the last report. Each reception report | ||||||
|  | 	// block conveys statistics on the reception of RTP packets from a | ||||||
|  | 	// single synchronization source. | ||||||
|  | 	Reports []ReceptionReport | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Marshal encodes the ReceiverReport in binary | ||||||
|  | func (r ReceiverReport) Marshal() ([]byte, error) { | ||||||
|  | 	rawPacket := make([]byte, 4) | ||||||
|  |  | ||||||
|  | 	binary.BigEndian.PutUint32(rawPacket, r.SSRC) | ||||||
|  |  | ||||||
|  | 	for _, rp := range r.Reports { | ||||||
|  | 		data, err := rp.Marshal() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		rawPacket = append(rawPacket, data...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return rawPacket, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Unmarshal decodes the ReceiverReport from binary | ||||||
|  | func (r *ReceiverReport) Unmarshal(rawPacket []byte) error { | ||||||
|  | 	if len(rawPacket) < 4 { | ||||||
|  | 		return errPacketTooShort | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r.SSRC = binary.BigEndian.Uint32(rawPacket) | ||||||
|  |  | ||||||
|  | 	for i := 4; i < len(rawPacket); i += receptionReportLength { | ||||||
|  | 		var rr ReceptionReport | ||||||
|  | 		if err := rr.Unmarshal(rawPacket[i:]); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		r.Reports = append(r.Reports, rr) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										87
									
								
								pkg/rtcp/receiver_report_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								pkg/rtcp/receiver_report_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | |||||||
|  | package rtcp | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestReceiverReportUnmarshalNil(t *testing.T) { | ||||||
|  | 	var rr ReceiverReport | ||||||
|  | 	err := rr.Unmarshal(nil) | ||||||
|  | 	if got, want := err, errPacketTooShort; got != want { | ||||||
|  | 		t.Fatalf("unmarshal nil rr: err = %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestReceiverReportRoundTrip(t *testing.T) { | ||||||
|  | 	for _, test := range []struct { | ||||||
|  | 		Name      string | ||||||
|  | 		Report    ReceiverReport | ||||||
|  | 		WantError error | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			Name: "valid", | ||||||
|  | 			Report: ReceiverReport{ | ||||||
|  | 				SSRC: 1, | ||||||
|  | 				Reports: []ReceptionReport{ | ||||||
|  | 					{ | ||||||
|  | 						SSRC:         2, | ||||||
|  | 						FractionLost: 2, | ||||||
|  | 						TotalLost:    3, | ||||||
|  | 						LastSeq:      4, | ||||||
|  | 						Jitter:       5, | ||||||
|  | 						LastSR:       6, | ||||||
|  | 						Delay:        7, | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						SSRC: 0, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name: "also valid", | ||||||
|  | 			Report: ReceiverReport{ | ||||||
|  | 				SSRC: 2, | ||||||
|  | 				Reports: []ReceptionReport{ | ||||||
|  | 					{ | ||||||
|  | 						SSRC:         999, | ||||||
|  | 						FractionLost: 30, | ||||||
|  | 						TotalLost:    12345, | ||||||
|  | 						LastSeq:      99, | ||||||
|  | 						Jitter:       22, | ||||||
|  | 						LastSR:       92, | ||||||
|  | 						Delay:        46, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name: "totallost overflow", | ||||||
|  | 			Report: ReceiverReport{ | ||||||
|  | 				SSRC: 1, | ||||||
|  | 				Reports: []ReceptionReport{{ | ||||||
|  | 					TotalLost: 1 << 25, | ||||||
|  | 				}}, | ||||||
|  | 			}, | ||||||
|  | 			WantError: errInvalidTotalLost, | ||||||
|  | 		}, | ||||||
|  | 	} { | ||||||
|  | 		data, err := test.Report.Marshal() | ||||||
|  | 		if got, want := err, test.WantError; got != want { | ||||||
|  | 			t.Fatalf("Marshal %q: err = %v, want %v", test.Name, got, want) | ||||||
|  | 		} | ||||||
|  | 		if err != nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		var decoded ReceiverReport | ||||||
|  | 		if err := decoded.Unmarshal(data); err != nil { | ||||||
|  | 			t.Fatalf("Unmarshal %q: %v", test.Name, err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if got, want := decoded, test.Report; !reflect.DeepEqual(got, want) { | ||||||
|  | 			t.Fatalf("%q rr round trip: got %#v, want %#v", test.Name, got, want) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										126
									
								
								pkg/rtcp/reception_report.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								pkg/rtcp/reception_report.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | |||||||
|  | package rtcp | ||||||
|  |  | ||||||
|  | import "encoding/binary" | ||||||
|  |  | ||||||
|  | // A ReceptionReport block conveys statistics on the reception of RTP packets | ||||||
|  | // from a single synchronization source. | ||||||
|  | type ReceptionReport struct { | ||||||
|  | 	// The SSRC identifier of the source to which the information in this | ||||||
|  | 	// reception report block pertains. | ||||||
|  | 	SSRC uint32 | ||||||
|  | 	// The fraction of RTP data packets from source SSRC lost since the | ||||||
|  | 	// previous SR or RR packet was sent, expressed as a fixed point | ||||||
|  | 	// number with the binary point at the left edge of the field. | ||||||
|  | 	FractionLost uint8 | ||||||
|  | 	// The total number of RTP data packets from source SSRC that have | ||||||
|  | 	// been lost since the beginning of reception. | ||||||
|  | 	TotalLost uint32 | ||||||
|  | 	// The low 16 bits contain the highest sequence number received in an | ||||||
|  | 	// RTP data packet from source SSRC, and the most significant 16 | ||||||
|  | 	// bits extend that sequence number with the corresponding count of | ||||||
|  | 	// sequence number cycles. | ||||||
|  | 	LastSeq uint32 | ||||||
|  | 	// An estimate of the statistical variance of the RTP data packet | ||||||
|  | 	// interarrival time, measured in timestamp units and expressed as an | ||||||
|  | 	// unsigned integer. | ||||||
|  | 	Jitter uint32 | ||||||
|  | 	// The middle 32 bits out of 64 in the NTP timestamp received as part of | ||||||
|  | 	// the most recent RTCP sender report (SR) packet from source SSRC. If no | ||||||
|  | 	// SR has been received yet, the field is set to zero. | ||||||
|  | 	LastSR uint32 | ||||||
|  | 	// The delay, expressed in units of 1/65536 seconds, between receiving the | ||||||
|  | 	// last SR packet from source SSRC and sending this reception report block. | ||||||
|  | 	// If no SR packet has been received yet from SSRC, the field is set to zero. | ||||||
|  | 	Delay uint32 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	receptionReportLength = 24 | ||||||
|  | 	fractionLostOffset    = 4 | ||||||
|  | 	totalLostOffset       = 5 | ||||||
|  | 	lastSeqOffset         = 8 | ||||||
|  | 	jitterOffset          = 12 | ||||||
|  | 	lastSROffset          = 16 | ||||||
|  | 	delayOffset           = 20 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Marshal encodes the ReceptionReport in binary | ||||||
|  | func (r ReceptionReport) Marshal() ([]byte, error) { | ||||||
|  | 	/* | ||||||
|  | 	 *  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 | ||||||
|  | 	 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | ||||||
|  | 	 * |                              SSRC                             | | ||||||
|  | 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||||||
|  | 	 * | fraction lost |       cumulative number of packets lost       | | ||||||
|  | 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||||||
|  | 	 * |           extended highest sequence number received           | | ||||||
|  | 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||||||
|  | 	 * |                      interarrival jitter                      | | ||||||
|  | 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||||||
|  | 	 * |                         last SR (LSR)                         | | ||||||
|  | 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||||||
|  | 	 * |                   delay since last SR (DLSR)                  | | ||||||
|  | 	 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | ||||||
|  | 	 */ | ||||||
|  |  | ||||||
|  | 	rawPacket := make([]byte, receptionReportLength) | ||||||
|  |  | ||||||
|  | 	binary.BigEndian.PutUint32(rawPacket, r.SSRC) | ||||||
|  |  | ||||||
|  | 	rawPacket[fractionLostOffset] = r.FractionLost | ||||||
|  |  | ||||||
|  | 	// pack TotalLost into 24 bits | ||||||
|  | 	if r.TotalLost >= (1 << 25) { | ||||||
|  | 		return nil, errInvalidTotalLost | ||||||
|  | 	} | ||||||
|  | 	tlBytes := rawPacket[totalLostOffset:] | ||||||
|  | 	tlBytes[0] = byte(r.TotalLost >> 16) | ||||||
|  | 	tlBytes[1] = byte(r.TotalLost >> 8) | ||||||
|  | 	tlBytes[2] = byte(r.TotalLost) | ||||||
|  |  | ||||||
|  | 	binary.BigEndian.PutUint32(rawPacket[lastSeqOffset:], r.LastSeq) | ||||||
|  | 	binary.BigEndian.PutUint32(rawPacket[jitterOffset:], r.Jitter) | ||||||
|  | 	binary.BigEndian.PutUint32(rawPacket[lastSROffset:], r.LastSR) | ||||||
|  | 	binary.BigEndian.PutUint32(rawPacket[delayOffset:], r.Delay) | ||||||
|  |  | ||||||
|  | 	return rawPacket, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Unmarshal decodes the ReceptionReport from binary | ||||||
|  | func (r *ReceptionReport) Unmarshal(rawPacket []byte) error { | ||||||
|  | 	if len(rawPacket) < receptionReportLength { | ||||||
|  | 		return errPacketTooShort | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 *  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 | ||||||
|  | 	 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | ||||||
|  | 	 * |                              SSRC                             | | ||||||
|  | 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||||||
|  | 	 * | fraction lost |       cumulative number of packets lost       | | ||||||
|  | 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||||||
|  | 	 * |           extended highest sequence number received           | | ||||||
|  | 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||||||
|  | 	 * |                      interarrival jitter                      | | ||||||
|  | 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||||||
|  | 	 * |                         last SR (LSR)                         | | ||||||
|  | 	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||||||
|  | 	 * |                   delay since last SR (DLSR)                  | | ||||||
|  | 	 * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | ||||||
|  | 	 */ | ||||||
|  |  | ||||||
|  | 	r.SSRC = binary.BigEndian.Uint32(rawPacket) | ||||||
|  | 	r.FractionLost = rawPacket[fractionLostOffset] | ||||||
|  |  | ||||||
|  | 	tlBytes := rawPacket[totalLostOffset:] | ||||||
|  | 	r.TotalLost = uint32(tlBytes[2]) | uint32(tlBytes[1])<<8 | uint32(tlBytes[0])<<16 | ||||||
|  |  | ||||||
|  | 	r.LastSeq = binary.BigEndian.Uint32(rawPacket[lastSeqOffset:]) | ||||||
|  | 	r.Jitter = binary.BigEndian.Uint32(rawPacket[jitterOffset:]) | ||||||
|  | 	r.LastSR = binary.BigEndian.Uint32(rawPacket[lastSROffset:]) | ||||||
|  | 	r.Delay = binary.BigEndian.Uint32(rawPacket[delayOffset:]) | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										86
									
								
								pkg/rtcp/sender_report.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								pkg/rtcp/sender_report.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | package rtcp | ||||||
|  |  | ||||||
|  | import "encoding/binary" | ||||||
|  |  | ||||||
|  | // A SenderReport (SR) packet provides reception quality feedback for an RTP stream | ||||||
|  | type SenderReport struct { | ||||||
|  | 	// The synchronization source identifier for the originator of this SR packet. | ||||||
|  | 	SSRC uint32 | ||||||
|  | 	// The wallclock time when this report was sent so that it may be used in | ||||||
|  | 	// combination with timestamps returned in reception reports from other | ||||||
|  | 	// receivers to measure round-trip propagation to those receivers. | ||||||
|  | 	NTPTime uint64 | ||||||
|  | 	// Corresponds to the same time as the NTP timestamp (above), but in | ||||||
|  | 	// the same units and with the same random offset as the RTP | ||||||
|  | 	// timestamps in data packets. This correspondence may be used for | ||||||
|  | 	// intra- and inter-media synchronization for sources whose NTP | ||||||
|  | 	// timestamps are synchronized, and may be used by media-independent | ||||||
|  | 	// receivers to estimate the nominal RTP clock frequency. | ||||||
|  | 	RTPTime uint32 | ||||||
|  | 	// The total number of RTP data packets transmitted by the sender | ||||||
|  | 	// since starting transmission up until the time this SR packet was | ||||||
|  | 	// generated. | ||||||
|  | 	PacketCount uint32 | ||||||
|  | 	// The total number of payload octets (i.e., not including header or | ||||||
|  | 	// padding) transmitted in RTP data packets by the sender since | ||||||
|  | 	// starting transmission up until the time this SR packet was | ||||||
|  | 	// generated. | ||||||
|  | 	OctetCount uint32 | ||||||
|  | 	// Zero or more reception report blocks depending on the number of other | ||||||
|  | 	// sources heard by this sender since the last report. Each reception report | ||||||
|  | 	// block conveys statistics on the reception of RTP packets from a | ||||||
|  | 	// single synchronization source. | ||||||
|  | 	Reports []ReceptionReport | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	senderReportLength = 24 | ||||||
|  | 	ntpTimeOffset      = 4 | ||||||
|  | 	rtpTimeOffset      = 12 | ||||||
|  | 	packetCountOffset  = 16 | ||||||
|  | 	octetCountOffset   = 20 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Marshal encodes the SenderReport in binary | ||||||
|  | func (r SenderReport) Marshal() ([]byte, error) { | ||||||
|  | 	rawPacket := make([]byte, senderReportLength) | ||||||
|  |  | ||||||
|  | 	binary.BigEndian.PutUint32(rawPacket, r.SSRC) | ||||||
|  | 	binary.BigEndian.PutUint64(rawPacket[ntpTimeOffset:], r.NTPTime) | ||||||
|  | 	binary.BigEndian.PutUint32(rawPacket[rtpTimeOffset:], r.RTPTime) | ||||||
|  | 	binary.BigEndian.PutUint32(rawPacket[packetCountOffset:], r.PacketCount) | ||||||
|  | 	binary.BigEndian.PutUint32(rawPacket[octetCountOffset:], r.OctetCount) | ||||||
|  |  | ||||||
|  | 	for _, rp := range r.Reports { | ||||||
|  | 		data, err := rp.Marshal() | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		rawPacket = append(rawPacket, data...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return rawPacket, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Unmarshal decodes the SenderReport from binary | ||||||
|  | func (r *SenderReport) Unmarshal(rawPacket []byte) error { | ||||||
|  | 	if len(rawPacket) < senderReportLength { | ||||||
|  | 		return errPacketTooShort | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	r.SSRC = binary.BigEndian.Uint32(rawPacket) | ||||||
|  | 	r.NTPTime = binary.BigEndian.Uint64(rawPacket[ntpTimeOffset:]) | ||||||
|  | 	r.RTPTime = binary.BigEndian.Uint32(rawPacket[rtpTimeOffset:]) | ||||||
|  | 	r.PacketCount = binary.BigEndian.Uint32(rawPacket[packetCountOffset:]) | ||||||
|  | 	r.OctetCount = binary.BigEndian.Uint32(rawPacket[octetCountOffset:]) | ||||||
|  |  | ||||||
|  | 	for i := senderReportLength; i < len(rawPacket); i += receptionReportLength { | ||||||
|  | 		var rr ReceptionReport | ||||||
|  | 		if err := rr.Unmarshal(rawPacket[i:]); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		r.Reports = append(r.Reports, rr) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										81
									
								
								pkg/rtcp/sender_report_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								pkg/rtcp/sender_report_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | package rtcp | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestSenderReportUnmarshalNil(t *testing.T) { | ||||||
|  | 	var sr SenderReport | ||||||
|  | 	err := sr.Unmarshal(nil) | ||||||
|  | 	if got, want := err, errPacketTooShort; got != want { | ||||||
|  | 		t.Fatalf("unmarshal nil sr: err = %v, want %v", got, want) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestSenderReportRoundTrip(t *testing.T) { | ||||||
|  | 	for _, test := range []struct { | ||||||
|  | 		Name      string | ||||||
|  | 		Report    SenderReport | ||||||
|  | 		WantError error | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			Name: "valid", | ||||||
|  | 			Report: SenderReport{ | ||||||
|  | 				SSRC:        1, | ||||||
|  | 				NTPTime:     999, | ||||||
|  | 				RTPTime:     555, | ||||||
|  | 				PacketCount: 32, | ||||||
|  | 				OctetCount:  11, | ||||||
|  | 				Reports: []ReceptionReport{ | ||||||
|  | 					{ | ||||||
|  | 						SSRC:         2, | ||||||
|  | 						FractionLost: 2, | ||||||
|  | 						TotalLost:    3, | ||||||
|  | 						LastSeq:      4, | ||||||
|  | 						Jitter:       5, | ||||||
|  | 						LastSR:       6, | ||||||
|  | 						Delay:        7, | ||||||
|  | 					}, | ||||||
|  | 					{ | ||||||
|  | 						SSRC: 0, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name: "also valid", | ||||||
|  | 			Report: SenderReport{ | ||||||
|  | 				SSRC: 2, | ||||||
|  | 				Reports: []ReceptionReport{ | ||||||
|  | 					{ | ||||||
|  | 						SSRC:         999, | ||||||
|  | 						FractionLost: 30, | ||||||
|  | 						TotalLost:    12345, | ||||||
|  | 						LastSeq:      99, | ||||||
|  | 						Jitter:       22, | ||||||
|  | 						LastSR:       92, | ||||||
|  | 						Delay:        46, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} { | ||||||
|  | 		data, err := test.Report.Marshal() | ||||||
|  | 		if got, want := err, test.WantError; got != want { | ||||||
|  | 			t.Fatalf("Marshal %q: err = %v, want %v", test.Name, got, want) | ||||||
|  | 		} | ||||||
|  | 		if err != nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		var decoded SenderReport | ||||||
|  | 		if err := decoded.Unmarshal(data); err != nil { | ||||||
|  | 			t.Fatalf("Unmarshal %q: %v", test.Name, err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if got, want := decoded, test.Report; !reflect.DeepEqual(got, want) { | ||||||
|  | 			t.Fatalf("%q sr round trip: got %#v, want %#v", test.Name, got, want) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Max Hawkins
					Max Hawkins