123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- package rtcp
- import (
- "encoding/binary"
- "fmt"
- )
- // SDESType is the item type used in the RTCP SDES control packet.
- type SDESType uint8
- // RTP SDES item types registered with IANA. See: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-5
- const (
- SDESEnd SDESType = iota // end of SDES list RFC 3550, 6.5
- SDESCNAME // canonical name RFC 3550, 6.5.1
- SDESName // user name RFC 3550, 6.5.2
- SDESEmail // user's electronic mail address RFC 3550, 6.5.3
- SDESPhone // user's phone number RFC 3550, 6.5.4
- SDESLocation // geographic user location RFC 3550, 6.5.5
- SDESTool // name of application or tool RFC 3550, 6.5.6
- SDESNote // notice about the source RFC 3550, 6.5.7
- SDESPrivate // private extensions RFC 3550, 6.5.8 (not implemented)
- )
- func (s SDESType) String() string {
- switch s {
- case SDESEnd:
- return "END"
- case SDESCNAME:
- return "CNAME"
- case SDESName:
- return "NAME"
- case SDESEmail:
- return "EMAIL"
- case SDESPhone:
- return "PHONE"
- case SDESLocation:
- return "LOC"
- case SDESTool:
- return "TOOL"
- case SDESNote:
- return "NOTE"
- case SDESPrivate:
- return "PRIV"
- default:
- return string(s)
- }
- }
- const (
- sdesSourceLen = 4
- sdesTypeLen = 1
- sdesTypeOffset = 0
- sdesOctetCountLen = 1
- sdesOctetCountOffset = 1
- sdesMaxOctetCount = (1 << 8) - 1
- sdesTextOffset = 2
- )
- // A SourceDescription (SDES) packet describes the sources in an RTP stream.
- type SourceDescription struct {
- Chunks []SourceDescriptionChunk
- }
- // NewCNAMESourceDescription creates a new SourceDescription with a single CNAME item.
- func NewCNAMESourceDescription(ssrc uint32, cname string) *SourceDescription {
- return &SourceDescription{
- Chunks: []SourceDescriptionChunk{{
- Source: ssrc,
- Items: []SourceDescriptionItem{{
- Type: SDESCNAME,
- Text: cname,
- }},
- }},
- }
- }
- // Marshal encodes the SourceDescription in binary
- func (s SourceDescription) 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
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * header |V=2|P| SC | PT=SDES=202 | length |
- * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
- * chunk | SSRC/CSRC_1 |
- * 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | SDES items |
- * | ... |
- * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
- * chunk | SSRC/CSRC_2 |
- * 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | SDES items |
- * | ... |
- * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
- */
- 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
- }
- copy(packetBody[chunkOffset:], data)
- chunkOffset += len(data)
- }
- if len(s.Chunks) > countMax {
- return nil, errTooManyChunks
- }
- hData, err := s.Header().Marshal()
- if err != nil {
- return nil, err
- }
- copy(rawPacket, hData)
- return rawPacket, nil
- }
- // Unmarshal decodes the SourceDescription from binary
- func (s *SourceDescription) Unmarshal(rawPacket []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
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * header |V=2|P| SC | PT=SDES=202 | length |
- * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
- * chunk | SSRC/CSRC_1 |
- * 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | SDES items |
- * | ... |
- * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
- * chunk | SSRC/CSRC_2 |
- * 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | SDES items |
- * | ... |
- * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
- */
- var h Header
- if err := h.Unmarshal(rawPacket); err != nil {
- return err
- }
- if h.Type != TypeSourceDescription {
- return errWrongType
- }
- for i := headerLength; i < len(rawPacket); {
- var chunk SourceDescriptionChunk
- if err := chunk.Unmarshal(rawPacket[i:]); err != nil {
- return err
- }
- s.Chunks = append(s.Chunks, chunk)
- i += chunk.len()
- }
- if len(s.Chunks) != int(h.Count) {
- return errInvalidHeader
- }
- 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
- Source uint32
- Items []SourceDescriptionItem
- }
- // Marshal encodes the SourceDescriptionChunk in binary
- func (s SourceDescriptionChunk) Marshal() ([]byte, error) {
- /*
- * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
- * | SSRC/CSRC_1 |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | SDES items |
- * | ... |
- * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
- */
- rawPacket := make([]byte, sdesSourceLen)
- binary.BigEndian.PutUint32(rawPacket, s.Source)
- for _, it := range s.Items {
- data, err := it.Marshal()
- if err != nil {
- return nil, err
- }
- rawPacket = append(rawPacket, data...)
- }
- // The list of items in each chunk MUST be terminated by one or more null octets
- rawPacket = append(rawPacket, uint8(SDESEnd))
- // additional null octets MUST be included if needed to pad until the next 32-bit boundary
- rawPacket = append(rawPacket, make([]byte, getPadding(len(rawPacket)))...)
- return rawPacket, nil
- }
- // Unmarshal decodes the SourceDescriptionChunk from binary
- func (s *SourceDescriptionChunk) Unmarshal(rawPacket []byte) error {
- /*
- * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
- * | SSRC/CSRC_1 |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | SDES items |
- * | ... |
- * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
- */
- if len(rawPacket) < (sdesSourceLen + sdesTypeLen) {
- return errPacketTooShort
- }
- s.Source = binary.BigEndian.Uint32(rawPacket)
- for i := 4; i < len(rawPacket); {
- if pktType := SDESType(rawPacket[i]); pktType == SDESEnd {
- return nil
- }
- var it SourceDescriptionItem
- if err := it.Unmarshal(rawPacket[i:]); err != nil {
- return err
- }
- s.Items = append(s.Items, it)
- i += it.len()
- }
- return errPacketTooShort
- }
- func (s SourceDescriptionChunk) len() int {
- chunkLen := sdesSourceLen
- for _, it := range s.Items {
- chunkLen += it.len()
- }
- chunkLen += sdesTypeLen // for terminating null octet
- // align to 32-bit boundary
- chunkLen += getPadding(chunkLen)
- return chunkLen
- }
- // A SourceDescriptionItem is a part of a SourceDescription that describes a stream.
- type SourceDescriptionItem struct {
- // The type identifier for this item. eg, SDESCNAME for canonical name description.
- //
- // Type zero or SDESEnd is interpreted as the end of an item list and cannot be used.
- Type SDESType
- // Text is a unicode text blob associated with the item. Its meaning varies based on the item's Type.
- Text string
- }
- func (s SourceDescriptionItem) len() int {
- /*
- * 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
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | CNAME=1 | length | user and domain name ...
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
- return sdesTypeLen + sdesOctetCountLen + len([]byte(s.Text))
- }
- // Marshal encodes the SourceDescriptionItem in binary
- func (s SourceDescriptionItem) 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
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | CNAME=1 | length | user and domain name ...
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
- if s.Type == SDESEnd {
- return nil, errSDESMissingType
- }
- rawPacket := make([]byte, sdesTypeLen+sdesOctetCountLen)
- rawPacket[sdesTypeOffset] = uint8(s.Type)
- txtBytes := []byte(s.Text)
- octetCount := len(txtBytes)
- if octetCount > sdesMaxOctetCount {
- return nil, errSDESTextTooLong
- }
- rawPacket[sdesOctetCountOffset] = uint8(octetCount)
- rawPacket = append(rawPacket, txtBytes...)
- return rawPacket, nil
- }
- // Unmarshal decodes the SourceDescriptionItem from binary
- func (s *SourceDescriptionItem) Unmarshal(rawPacket []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
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | CNAME=1 | length | user and domain name ...
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
- if len(rawPacket) < (sdesTypeLen + sdesOctetCountLen) {
- return errPacketTooShort
- }
- s.Type = SDESType(rawPacket[sdesTypeOffset])
- octetCount := int(rawPacket[sdesOctetCountOffset])
- if sdesTextOffset+octetCount > len(rawPacket) {
- return errPacketTooShort
- }
- txtBytes := rawPacket[sdesTextOffset : sdesTextOffset+octetCount]
- s.Text = string(txtBytes)
- 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
- }
- func (s *SourceDescription) String() string {
- out := "Source Description:\n"
- for _, c := range s.Chunks {
- out += fmt.Sprintf("\t%x: %s\n", c.Source, c.Items)
- }
- return out
- }
|