package rtcp import ( "encoding/binary" ) // RTCP packet types. const ( typeSenderReport = 200 typeReceiverReport = 201 typeSourceDescription = 202 ) // SDES Item types. const ( typeCName = 1 ) // MISC. const ( reportBlockSize = 6 senderReportSize = 28 ) // ReceiverReport describes an RTCP receiver report packet. type ReceiverReport struct { Header // Standard RTCP packet header. SenderSSRC uint32 // SSRC of the sender of this report. Blocks []ReportBlock // Report blocks. Extensions [][4]byte // Contains any extensions to the packet. } // Bytes returns a []byte of the ReceiverReport r. func (r *ReceiverReport) Bytes(buf []byte) []byte { l := 8 + 4*reportBlockSize*len(r.Blocks) + 4*len(r.Extensions) if buf == nil || cap(buf) < l { buf = make([]byte, l) } buf = buf[:l] l = 1 + reportBlockSize*len(r.Blocks) + len(r.Extensions) r.writeHeader(buf, l) binary.BigEndian.PutUint32(buf[4:], r.SenderSSRC) idx := 8 for _, b := range r.Blocks { binary.BigEndian.PutUint32(buf[idx:], b.SSRC) idx += 4 binary.BigEndian.PutUint32(buf[idx:], b.PacketsLost) buf[idx] = b.FractionLost idx += 4 binary.BigEndian.PutUint32(buf[idx:], b.HighestSequence) idx += 4 binary.BigEndian.PutUint32(buf[idx:], b.Jitter) idx += 4 binary.BigEndian.PutUint32(buf[idx:], b.LSR) idx += 4 binary.BigEndian.PutUint32(buf[idx:], b.DLSR) idx += 4 } for _, e := range r.Extensions { copy(buf[idx:], e[:]) idx += 4 } return buf } // ReportBlock describes an RTCP report block used in Sender/Receiver Reports. type ReportBlock struct { SSRC uint32 // Source identifier. FractionLost uint8 // Fraction of packets lost. PacketsLost uint32 // Cumulative number of packets lost. HighestSequence uint32 // Extended highest sequence number received. Jitter uint32 // Interarrival jitter. LSR uint32 // Last sender report timestamp. DLSR uint32 // Delay since last sender report. } // SourceDescription describes a source description RTCP packet. type SourceDescription struct { Header // Standard RTCP packet header. Chunks []Chunk // Chunks to describe items of each SSRC. } // Bytes returns an []byte of the SourceDescription d. func (d *SourceDescription) Bytes(buf []byte) []byte { bodyLen := d.bodyLen() rem := bodyLen % 4 if rem != 0 { bodyLen += 4 - rem } l := 4 + bodyLen if buf == nil || cap(buf) < l { buf = make([]byte, l) } buf = buf[:l] d.writeHeader(buf, bodyLen/4) idx := 4 for _, c := range d.Chunks { binary.BigEndian.PutUint32(buf[idx:], c.SSRC) idx += 4 for _, i := range c.Items { buf[idx] = i.Type idx++ buf[idx] = byte(len(i.Text)) idx++ copy(buf[idx:], i.Text) idx += len(i.Text) } } return buf } // bodyLen calculates the body length of a source description packet in bytes. func (d *SourceDescription) bodyLen() int { l := 0 for _, c := range d.Chunks { l += c.len() } return l } // SenderReport describes an RTCP sender report. type SenderReport struct { Header // Standard RTCP header. SSRC uint32 // SSRC of sender. TimestampMSW uint32 // Most significant word of timestamp. TimestampLSW uint32 // Least significant word of timestamp. RTPTimestamp uint32 // Current RTP timestamp. PacketCount uint32 // Senders packet count. OctetCount uint32 // Senders octet count. // Report blocks (unimplemented) // ... } // Bytes returns a []byte of the SenderReport. func (r *SenderReport) Bytes() []byte { buf := make([]byte, senderReportSize) r.writeHeader(buf, senderReportSize-1) for i, w := range []uint32{ r.SSRC, r.TimestampMSW, r.TimestampLSW, r.RTPTimestamp, r.PacketCount, r.OctetCount, } { binary.BigEndian.PutUint32(buf[i+1:], w) } return buf } // Header describes a standard RTCP packet header. type Header struct { Version uint8 // RTCP version. Padding bool // Padding indicator. ReportCount uint8 // Number of reports contained. Type uint8 // Type of RTCP packet. } // SDESItem describes a source description item. type SDESItem struct { Type uint8 // Type of item. Text []byte // Item text. } // Chunk describes a source description chunk for a given SSRC. type Chunk struct { SSRC uint32 // SSRC of the source being described by the below items. Items []SDESItem // Items describing the source. } // len returns the len of a chunk in bytes. func (c *Chunk) len() int { tot := 4 for _, i := range c.Items { tot += 2 + len(i.Text) } return tot } // writeHeader writes the standard RTCP header given a buffer to write to and l // the RTCP body length that needs to be encoded into the header. func (h Header) writeHeader(buf []byte, l int) { buf[0] = h.Version<<6 | asByte(h.Padding)<<5 | 0x1f&h.ReportCount buf[1] = h.Type binary.BigEndian.PutUint16(buf[2:], uint16(l)) } func asByte(b bool) byte { if b { return 0x01 } return 0x00 }