1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package sdp
import (
"strconv"
)
// MediaDescription represents a media type.
// https://tools.ietf.org/html/rfc4566#section-5.14
type MediaDescription struct {
// m=<media> <port>/<number of ports> <proto> <fmt> ...
// https://tools.ietf.org/html/rfc4566#section-5.14
MediaName MediaName
// i=<session description>
// https://tools.ietf.org/html/rfc4566#section-5.4
MediaTitle *Information
// c=<nettype> <addrtype> <connection-address>
// https://tools.ietf.org/html/rfc4566#section-5.7
ConnectionInformation *ConnectionInformation
// b=<bwtype>:<bandwidth>
// https://tools.ietf.org/html/rfc4566#section-5.8
Bandwidth []Bandwidth
// k=<method>
// k=<method>:<encryption key>
// https://tools.ietf.org/html/rfc4566#section-5.12
EncryptionKey *EncryptionKey
// a=<attribute>
// a=<attribute>:<value>
// https://tools.ietf.org/html/rfc4566#section-5.13
Attributes []Attribute
}
// Attribute returns the value of an attribute and if it exists.
func (d *MediaDescription) Attribute(key string) (string, bool) {
for _, a := range d.Attributes {
if a.Key == key {
return a.Value, true
}
}
return "", false
}
// RangedPort supports special format for the media field "m=" port value. If
// it may be necessary to specify multiple transport ports, the protocol allows
// to write it as: <port>/<number of ports> where number of ports is a an
// offsetting range.
type RangedPort struct {
Value int
Range *int
}
func (p *RangedPort) String() string {
output := strconv.Itoa(p.Value)
if p.Range != nil {
output += "/" + strconv.Itoa(*p.Range)
}
return output
}
func (p RangedPort) marshalInto(b []byte) []byte {
b = strconv.AppendInt(b, int64(p.Value), 10)
if p.Range != nil {
b = append(b, '/')
b = strconv.AppendInt(b, int64(*p.Range), 10)
}
return b
}
func (p RangedPort) marshalSize() (size int) {
size = lenInt(int64(p.Value))
if p.Range != nil {
size += 1 + lenInt(int64(*p.Range))
}
return
}
// MediaName describes the "m=" field storage structure.
type MediaName struct {
Media string
Port RangedPort
Protos []string
Formats []string
}
func (m MediaName) String() string {
return stringFromMarshal(m.marshalInto, m.marshalSize)
}
func (m MediaName) marshalInto(b []byte) []byte {
appendList := func(list []string, sep byte) {
for i, p := range list {
if i != 0 && i != len(list) {
b = append(b, sep)
}
b = append(b, p...)
}
}
b = append(append(b, m.Media...), ' ')
b = append(m.Port.marshalInto(b), ' ')
appendList(m.Protos, '/')
b = append(b, ' ')
appendList(m.Formats, ' ')
return b
}
func (m MediaName) marshalSize() (size int) {
listSize := func(list []string) {
for _, p := range list {
size += 1 + len(p)
}
}
size = len(m.Media)
size += 1 + m.Port.marshalSize()
listSize(m.Protos)
listSize(m.Formats)
return size
}
|