diff --git a/pkg/rtcp/goodbye.go b/pkg/rtcp/goodbye.go index 0cb0e8ba..49224011 100644 --- a/pkg/rtcp/goodbye.go +++ b/pkg/rtcp/goodbye.go @@ -30,14 +30,15 @@ func (g Goodbye) Marshal() ([]byte, error) { * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ - rawPacket := make([]byte, len(g.Sources)*ssrcLength) + rawPacket := make([]byte, g.len()) + packetBody := rawPacket[headerLength:] if len(g.Sources) > countMax { return nil, errTooManySources } for i, s := range g.Sources { - binary.BigEndian.PutUint32(rawPacket[i*ssrcLength:], s) + binary.BigEndian.PutUint32(packetBody[i*ssrcLength:], s) } if g.Reason != "" { @@ -47,25 +48,16 @@ func (g Goodbye) Marshal() ([]byte, error) { return nil, errReasonTooLong } - rawPacket = append(rawPacket, uint8(len(reason))) - rawPacket = append(rawPacket, reason...) - - // align to 32-bit boundary - rawPacket = append(rawPacket, make([]byte, util.GetPadding(len(rawPacket)))...) + reasonOffset := len(g.Sources) * ssrcLength + packetBody[reasonOffset] = uint8(len(reason)) + copy(packetBody[reasonOffset+1:], reason) } - h := Header{ - Padding: false, - Count: uint8(len(g.Sources)), - Type: TypeGoodbye, - Length: uint16(((headerLength + len(rawPacket)) / 4) - 1), - } - hData, err := h.Marshal() + hData, err := g.Header().Marshal() if err != nil { return nil, err } - - rawPacket = append(hData, rawPacket...) + copy(rawPacket, hData) return rawPacket, nil } @@ -126,3 +118,23 @@ func (g *Goodbye) Unmarshal(rawPacket []byte) error { return nil } + +// Header returns the Header associated with this packet. +func (g *Goodbye) Header() Header { + return Header{ + Padding: false, + Count: uint8(len(g.Sources)), + Type: TypeGoodbye, + Length: uint16((g.len() / 4) - 1), + } +} + +func (g *Goodbye) len() int { + srcsLength := len(g.Sources) * ssrcLength + reasonLength := len(g.Reason) + 1 + + l := headerLength + srcsLength + reasonLength + + // align to 32-bit boundary + return l + util.GetPadding(l) +} diff --git a/pkg/rtcp/packet.go b/pkg/rtcp/packet.go index f43f1fe9..bd09e619 100644 --- a/pkg/rtcp/packet.go +++ b/pkg/rtcp/packet.go @@ -2,6 +2,8 @@ package rtcp // Packet represents an RTCP packet, a protocol used for out-of-band statistics and control information for an RTP session type Packet interface { + Header() Header + Marshal() ([]byte, error) Unmarshal(rawPacket []byte) error } diff --git a/pkg/rtcp/picture_loss_indication.go b/pkg/rtcp/picture_loss_indication.go index c462ceca..32a89f72 100644 --- a/pkg/rtcp/picture_loss_indication.go +++ b/pkg/rtcp/picture_loss_indication.go @@ -11,6 +11,8 @@ type PictureLossIndication struct { // SSRC where the loss was experienced MediaSSRC uint32 + + header Header } const ( @@ -26,9 +28,11 @@ func (p PictureLossIndication) Marshal() ([]byte, error) { * * The semantics of this FB message is independent of the payload type. */ - rawPacket := make([]byte, 8) - binary.BigEndian.PutUint32(rawPacket, p.SenderSSRC) - binary.BigEndian.PutUint32(rawPacket[4:], p.MediaSSRC) + rawPacket := make([]byte, p.len()) + packetBody := rawPacket[headerLength:] + + binary.BigEndian.PutUint32(packetBody, p.SenderSSRC) + binary.BigEndian.PutUint32(packetBody[4:], p.MediaSSRC) h := Header{ Count: pliFMT, @@ -39,8 +43,9 @@ func (p PictureLossIndication) Marshal() ([]byte, error) { if err != nil { return nil, err } + copy(rawPacket, hData) - return append(hData, rawPacket...), nil + return rawPacket, nil } // Unmarshal decodes the PictureLossIndication from binary @@ -62,3 +67,12 @@ func (p *PictureLossIndication) Unmarshal(rawPacket []byte) error { p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:]) return nil } + +// Header returns the Header associated with this packet. +func (p *PictureLossIndication) Header() Header { + return p.header +} + +func (p *PictureLossIndication) len() int { + return headerLength + ssrcLength*2 +} diff --git a/pkg/rtcp/rapid_resynchronization_request.go b/pkg/rtcp/rapid_resynchronization_request.go index 91117433..a4337358 100644 --- a/pkg/rtcp/rapid_resynchronization_request.go +++ b/pkg/rtcp/rapid_resynchronization_request.go @@ -14,8 +14,10 @@ type RapidResynchronizationRequest struct { } const ( - rrrFMT = 5 - rrrLength = 2 + rrrFMT = 5 + rrrLength = 2 + rrrHeaderLength = ssrcLength * 2 + rrrMediaOffset = 4 ) // Marshal encodes the RapidResynchronizationRequest in binary @@ -26,21 +28,19 @@ func (p RapidResynchronizationRequest) Marshal() ([]byte, error) { * * The semantics of this FB message is independent of the payload type. */ - rawPacket := make([]byte, 8) - binary.BigEndian.PutUint32(rawPacket, p.SenderSSRC) - binary.BigEndian.PutUint32(rawPacket[4:], p.MediaSSRC) + rawPacket := make([]byte, p.len()) + packetBody := rawPacket[headerLength:] - h := Header{ - Count: rrrFMT, - Type: TypeTransportSpecificFeedback, - Length: rrrLength, - } - hData, err := h.Marshal() + binary.BigEndian.PutUint32(packetBody, p.SenderSSRC) + binary.BigEndian.PutUint32(packetBody[rrrMediaOffset:], p.MediaSSRC) + + hData, err := p.Header().Marshal() if err != nil { return nil, err } + copy(rawPacket, hData) - return append(hData, rawPacket...), nil + return rawPacket, nil } // Unmarshal decodes the RapidResynchronizationRequest from binary @@ -63,3 +63,16 @@ func (p *RapidResynchronizationRequest) Unmarshal(rawPacket []byte) error { p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:]) return nil } + +func (p *RapidResynchronizationRequest) len() int { + return headerLength + rrrHeaderLength +} + +// Header returns the Header associated with this packet. +func (p *RapidResynchronizationRequest) Header() Header { + return Header{ + Count: rrrFMT, + Type: TypeTransportSpecificFeedback, + Length: rrrLength, + } +} diff --git a/pkg/rtcp/raw_packet.go b/pkg/rtcp/raw_packet.go index 1556b381..304542d0 100644 --- a/pkg/rtcp/raw_packet.go +++ b/pkg/rtcp/raw_packet.go @@ -23,3 +23,12 @@ func (r *RawPacket) Unmarshal(b []byte) error { return nil } + +// Header returns the Header associated with this packet. +func (r RawPacket) Header() Header { + var h Header + if err := h.Unmarshal(r); err != nil { + return Header{} + } + return h +} diff --git a/pkg/rtcp/receiver_report.go b/pkg/rtcp/receiver_report.go index 5b05c9ed..6cb30ed5 100644 --- a/pkg/rtcp/receiver_report.go +++ b/pkg/rtcp/receiver_report.go @@ -49,33 +49,29 @@ func (r ReceiverReport) Marshal() ([]byte, error) { * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ - rawPacket := make([]byte, ssrcLength) + rawPacket := make([]byte, r.len()) + packetBody := rawPacket[headerLength:] - binary.BigEndian.PutUint32(rawPacket, r.SSRC) + binary.BigEndian.PutUint32(packetBody, r.SSRC) - for _, rp := range r.Reports { + for i, rp := range r.Reports { data, err := rp.Marshal() if err != nil { return nil, err } - rawPacket = append(rawPacket, data...) + offset := ssrcLength + receptionReportLength*i + copy(packetBody[offset:], data) } if len(r.Reports) > countMax { return nil, errTooManyReports } - h := Header{ - Count: uint8(len(r.Reports)), - Type: TypeReceiverReport, - Length: uint16(((headerLength + len(rawPacket)) / 4) - 1), - } - hData, err := h.Marshal() + hData, err := r.Header().Marshal() if err != nil { return nil, err } - - rawPacket = append(hData, rawPacket...) + copy(rawPacket, hData) return rawPacket, nil } @@ -139,3 +135,20 @@ func (r *ReceiverReport) Unmarshal(rawPacket []byte) error { return nil } + +func (r *ReceiverReport) len() int { + repsLength := 0 + for _, rep := range r.Reports { + repsLength += rep.len() + } + return headerLength + ssrcLength + repsLength +} + +// Header returns the Header associated with this packet. +func (r *ReceiverReport) Header() Header { + return Header{ + Count: uint8(len(r.Reports)), + Type: TypeReceiverReport, + Length: uint16((r.len() / 4) - 1), + } +} diff --git a/pkg/rtcp/reception_report.go b/pkg/rtcp/reception_report.go index 26517416..96e14032 100644 --- a/pkg/rtcp/reception_report.go +++ b/pkg/rtcp/reception_report.go @@ -124,3 +124,7 @@ func (r *ReceptionReport) Unmarshal(rawPacket []byte) error { return nil } + +func (r *ReceptionReport) len() int { + return receptionReportLength +} diff --git a/pkg/rtcp/sender_report.go b/pkg/rtcp/sender_report.go index 4cf76e11..c2688ef4 100644 --- a/pkg/rtcp/sender_report.go +++ b/pkg/rtcp/sender_report.go @@ -31,6 +31,8 @@ type SenderReport struct { // block conveys statistics on the reception of RTP packets from a // single synchronization source. Reports []ReceptionReport + + header Header } var ( @@ -87,37 +89,33 @@ func (r SenderReport) Marshal() ([]byte, error) { * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ - rawPacket := make([]byte, srHeaderLength) + rawPacket := make([]byte, r.len()) + packetBody := rawPacket[headerLength:] - binary.BigEndian.PutUint32(rawPacket[srSSRCOffset:], r.SSRC) - binary.BigEndian.PutUint64(rawPacket[srNTPOffset:], r.NTPTime) - binary.BigEndian.PutUint32(rawPacket[srRTPOffset:], r.RTPTime) - binary.BigEndian.PutUint32(rawPacket[srPacketCountOffset:], r.PacketCount) - binary.BigEndian.PutUint32(rawPacket[srOctetCountOffset:], r.OctetCount) + binary.BigEndian.PutUint32(packetBody[srSSRCOffset:], r.SSRC) + binary.BigEndian.PutUint64(packetBody[srNTPOffset:], r.NTPTime) + binary.BigEndian.PutUint32(packetBody[srRTPOffset:], r.RTPTime) + binary.BigEndian.PutUint32(packetBody[srPacketCountOffset:], r.PacketCount) + binary.BigEndian.PutUint32(packetBody[srOctetCountOffset:], r.OctetCount) - for _, rp := range r.Reports { + for i, rp := range r.Reports { data, err := rp.Marshal() if err != nil { return nil, err } - rawPacket = append(rawPacket, data...) + offset := srHeaderLength + receptionReportLength*i + copy(packetBody[offset:], data) } if len(r.Reports) > countMax { return nil, errTooManyReports } - h := Header{ - Count: uint8(len(r.Reports)), - Type: TypeSenderReport, - Length: uint16(((headerLength + len(rawPacket)) / 4) - 1), - } - hData, err := h.Marshal() + hData, err := r.Header().Marshal() if err != nil { return nil, err } - - rawPacket = append(hData, rawPacket...) + copy(rawPacket, hData) return rawPacket, nil } @@ -197,3 +195,20 @@ func (r *SenderReport) Unmarshal(rawPacket []byte) error { return nil } + +func (r *SenderReport) len() int { + repsLength := 0 + for _, rep := range r.Reports { + repsLength += rep.len() + } + return headerLength + srHeaderLength + repsLength +} + +// Header returns the Header associated with this packet. +func (r *SenderReport) Header() Header { + return Header{ + Count: uint8(len(r.Reports)), + Type: TypeSenderReport, + Length: uint16((r.len() / 4) - 1), + } +} diff --git a/pkg/rtcp/source_description.go b/pkg/rtcp/source_description.go index 858cc4fc..c0815444 100644 --- a/pkg/rtcp/source_description.go +++ b/pkg/rtcp/source_description.go @@ -82,30 +82,28 @@ func (s SourceDescription) Marshal() ([]byte, error) { * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ */ - rawPacket := make([]byte, 0) + rawPacket := make([]byte, s.len()) + packetBody := rawPacket[headerLength:] + + chunkOffset := 0 for _, c := range s.Chunks { data, err := c.Marshal() if err != nil { return nil, err } - rawPacket = append(rawPacket, data...) + copy(packetBody[chunkOffset:], data) + chunkOffset += len(data) } if len(s.Chunks) > countMax { return nil, errTooManyChunks } - h := Header{ - Count: uint8(len(s.Chunks)), - Type: TypeSourceDescription, - Length: uint16(((headerLength + len(rawPacket)) / 4) - 1), - } - hData, err := h.Marshal() + hData, err := s.Header().Marshal() if err != nil { return nil, err } - - rawPacket = append(hData, rawPacket...) + copy(rawPacket, hData) return rawPacket, nil } @@ -156,6 +154,23 @@ func (s *SourceDescription) Unmarshal(rawPacket []byte) error { return nil } +func (s *SourceDescription) len() int { + chunksLength := 0 + for _, c := range s.Chunks { + chunksLength += c.len() + } + return headerLength + chunksLength +} + +// Header returns the Header associated with this packet. +func (s *SourceDescription) Header() Header { + return Header{ + Count: uint8(len(s.Chunks)), + Type: TypeSourceDescription, + Length: uint16((s.len() / 4) - 1), + } +} + // A SourceDescriptionChunk contains items describing a single RTP source type SourceDescriptionChunk struct { // The source (ssrc) or contributing source (csrc) identifier this packet describes