mirror of
https://dev.narayana.im/narayana/telegabber.git
synced 2026-08-05 12:17:06 +00:00
141 lines
2.5 KiB
Go
141 lines
2.5 KiB
Go
package jingle
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const sdpLineDivider = "\r\n"
|
|
|
|
// one SDP "a=" attribute line
|
|
type kv struct {
|
|
k, v string
|
|
}
|
|
|
|
// one m= block + its connection line and ordered attrs
|
|
type mediaSection struct {
|
|
media string
|
|
port string
|
|
protocol string
|
|
formats []string
|
|
connection string
|
|
attrs []kv
|
|
}
|
|
|
|
type sdpDoc struct {
|
|
sessionAttrs []kv
|
|
media []mediaSection
|
|
}
|
|
|
|
// preserves attribute order (SDP repeats keys; order matters for
|
|
// rtpmap/candidate)
|
|
func parseSDP(s string) (*sdpDoc, error) {
|
|
doc := &sdpDoc{}
|
|
var cur *mediaSection
|
|
|
|
for _, raw := range strings.Split(s, "\n") {
|
|
line := strings.TrimRight(raw, "\r")
|
|
if len(line) < 2 || line[1] != '=' {
|
|
continue
|
|
}
|
|
key := line[0]
|
|
value := line[2:]
|
|
switch key {
|
|
case 'm':
|
|
if cur != nil {
|
|
doc.media = append(doc.media, *cur)
|
|
}
|
|
parts := strings.Split(value, " ")
|
|
if len(parts) < 3 {
|
|
return nil, fmt.Errorf("malformed m= line: %q", value)
|
|
}
|
|
ms := mediaSection{
|
|
media: parts[0],
|
|
port: parts[1],
|
|
protocol: parts[2],
|
|
}
|
|
if len(parts) > 3 {
|
|
ms.formats = parts[3:]
|
|
}
|
|
cur = &ms
|
|
case 'c':
|
|
if cur != nil {
|
|
cur.connection = value
|
|
}
|
|
case 'a':
|
|
k, v := splitAttr(value)
|
|
if cur == nil {
|
|
doc.sessionAttrs = append(doc.sessionAttrs, kv{k, v})
|
|
} else {
|
|
cur.attrs = append(cur.attrs, kv{k, v})
|
|
}
|
|
}
|
|
}
|
|
if cur != nil {
|
|
doc.media = append(doc.media, *cur)
|
|
}
|
|
return doc, nil
|
|
}
|
|
|
|
// value is everything after the first colon; later colons are preserved
|
|
func splitAttr(body string) (string, string) {
|
|
if i := strings.IndexByte(body, ':'); i >= 0 {
|
|
return body[:i], body[i+1:]
|
|
}
|
|
return body, ""
|
|
}
|
|
|
|
func mmGetAll(mm []kv, key string) []string {
|
|
var out []string
|
|
for _, p := range mm {
|
|
if p.k == key {
|
|
out = append(out, p.v)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func mmFirst(mm []kv, key, def string) string {
|
|
for _, p := range mm {
|
|
if p.k == key {
|
|
return p.v
|
|
}
|
|
}
|
|
return def
|
|
}
|
|
|
|
func mmHas(mm []kv, key string) bool {
|
|
for _, p := range mm {
|
|
if p.k == key {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
type sdpBuilder struct {
|
|
lines []string
|
|
}
|
|
|
|
func (b *sdpBuilder) line(s string) {
|
|
b.lines = append(b.lines, s)
|
|
}
|
|
|
|
// empty value emits "a=key" (no colon)
|
|
func (b *sdpBuilder) attr(k, v string) {
|
|
if v == "" {
|
|
b.lines = append(b.lines, "a="+k)
|
|
} else {
|
|
b.lines = append(b.lines, "a="+k+":"+v)
|
|
}
|
|
}
|
|
|
|
func (b *sdpBuilder) appendAttrs(mm []kv) {
|
|
for _, p := range mm {
|
|
b.attr(p.k, p.v)
|
|
}
|
|
}
|
|
|
|
func (b *sdpBuilder) String() string {
|
|
return strings.Join(b.lines, sdpLineDivider) + sdpLineDivider
|
|
}
|