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 }