mirror of https://github.com/EasyDarwin/EasyDarwin
478 lines
8.9 KiB
Go
478 lines
8.9 KiB
Go
package sdp
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Parse reads session description from the buffer.
|
|
func Parse(b []byte) (*Session, error) {
|
|
return ParseString(string(b))
|
|
}
|
|
|
|
// ParseString reads session description from the string.
|
|
func ParseString(s string) (*Session, error) {
|
|
return NewDecoderString(s).Decode()
|
|
}
|
|
|
|
// A Decoder reads a session description from a stream.
|
|
type Decoder struct {
|
|
r lineReader
|
|
p []string
|
|
}
|
|
|
|
// NewDecoder returns new decoder that reads from r.
|
|
func NewDecoder(r io.Reader) *Decoder {
|
|
return &Decoder{r: &reader{b: bufio.NewReaderSize(r, maxLineSize)}}
|
|
}
|
|
|
|
// NewDecoderString returns new decoder that reads from s.
|
|
func NewDecoderString(s string) *Decoder {
|
|
return &Decoder{r: &stringReader{s: s}}
|
|
}
|
|
|
|
// Decode encodes the session description.
|
|
func (d *Decoder) Decode() (*Session, error) {
|
|
line := 0
|
|
sess := new(Session)
|
|
var media *Media
|
|
|
|
for {
|
|
line++
|
|
s, err := d.r.ReadLine()
|
|
if err != nil {
|
|
if err == io.EOF && sess.Origin != nil {
|
|
break
|
|
}
|
|
return nil, err
|
|
}
|
|
if len(s) == 0 && sess.Origin != nil {
|
|
break
|
|
}
|
|
if len(s) < 2 || s[1] != '=' {
|
|
return nil, &errDecode{errFormat, line, s}
|
|
}
|
|
f, v := s[0], s[2:]
|
|
if f == 'm' {
|
|
media = new(Media)
|
|
err = d.media(media, f, v)
|
|
if err == nil {
|
|
sess.Media = append(sess.Media, media)
|
|
}
|
|
} else if media == nil {
|
|
err = d.session(sess, f, v)
|
|
} else {
|
|
err = d.media(media, f, v)
|
|
}
|
|
if err != nil {
|
|
return nil, &errDecode{err, line, s}
|
|
}
|
|
}
|
|
return sess, nil
|
|
}
|
|
|
|
func (d *Decoder) session(s *Session, f byte, v string) error {
|
|
var err error
|
|
switch f {
|
|
case 'v':
|
|
s.Version, err = strconv.Atoi(v)
|
|
case 'o':
|
|
if s.Origin != nil {
|
|
return errUnexpectedField
|
|
}
|
|
s.Origin, err = d.origin(v)
|
|
case 's':
|
|
s.Name = v
|
|
case 'i':
|
|
s.Information = v
|
|
case 'u':
|
|
s.URI = v
|
|
case 'e':
|
|
s.Email = append(s.Email, v)
|
|
case 'p':
|
|
s.Phone = append(s.Phone, v)
|
|
case 'c':
|
|
if s.Connection != nil {
|
|
return errUnexpectedField
|
|
}
|
|
s.Connection, err = d.connection(v)
|
|
case 'b':
|
|
if s.Bandwidth == nil {
|
|
s.Bandwidth = make(Bandwidth)
|
|
}
|
|
err = d.bandwidth(s.Bandwidth, v)
|
|
case 'z':
|
|
s.TimeZone, err = d.timezone(v)
|
|
case 'k':
|
|
s.Key = append(s.Key, d.key(v))
|
|
case 'a':
|
|
a := d.attr(v)
|
|
switch a.Name {
|
|
case ModeInactive, ModeRecvOnly, ModeSendOnly, ModeSendRecv:
|
|
s.Mode = a.Name
|
|
default:
|
|
s.Attributes = append(s.Attributes, a)
|
|
}
|
|
case 't':
|
|
s.Timing, err = d.timing(v)
|
|
case 'r':
|
|
r, err := d.repeat(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.Repeat = append(s.Repeat, r)
|
|
default:
|
|
return errUnexpectedField
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (d *Decoder) media(m *Media, f byte, v string) error {
|
|
var err error
|
|
switch f {
|
|
case 'm':
|
|
err = d.proto(m, v)
|
|
case 'i':
|
|
m.Information = v
|
|
case 'c':
|
|
conn, err := d.connection(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.Connection = append(m.Connection, conn)
|
|
case 'b':
|
|
if m.Bandwidth == nil {
|
|
m.Bandwidth = make(Bandwidth)
|
|
}
|
|
err = d.bandwidth(m.Bandwidth, v)
|
|
case 'k':
|
|
m.Key = append(m.Key, d.key(v))
|
|
case 'a':
|
|
a := d.attr(v)
|
|
switch a.Name {
|
|
case ModeInactive, ModeRecvOnly, ModeSendOnly, ModeSendRecv:
|
|
m.Mode = a.Name
|
|
case "rtpmap", "rtcp-fb", "fmtp":
|
|
err = d.format(m, a)
|
|
default:
|
|
m.Attributes = append(m.Attributes, a)
|
|
}
|
|
default:
|
|
return errUnexpectedField
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (d *Decoder) format(m *Media, a *Attr) error {
|
|
p, ok := d.fields(a.Value, 2)
|
|
if !ok {
|
|
return errFormat
|
|
}
|
|
pt, err := strconv.Atoi(p[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f, v := m.Format(pt), p[1]
|
|
if f == nil {
|
|
return nil
|
|
}
|
|
switch a.Name {
|
|
case "rtpmap":
|
|
err = d.rtpmap(f, v)
|
|
case "rtcp-fb":
|
|
f.Feedback = append(f.Feedback, v)
|
|
case "fmtp":
|
|
f.Params = append(f.Params, v)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (d *Decoder) rtpmap(f *Format, v string) error {
|
|
p, ok := d.split(v, '/', 3)
|
|
if len(p) < 2 {
|
|
return errFormat
|
|
}
|
|
f.Name = p[0]
|
|
var err error
|
|
if ok {
|
|
if f.Channels, err = strconv.Atoi(strings.TrimSpace(p[2])); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if f.ClockRate, err = strconv.Atoi(strings.TrimSpace(p[1])); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Decoder) proto(m *Media, v string) error {
|
|
p, ok := d.fields(v, 4)
|
|
if !ok {
|
|
return errFormat
|
|
}
|
|
formats := p[3]
|
|
m.Type, m.Proto = p[0], p[2]
|
|
p, ok = d.split(p[1], '/', 2)
|
|
var err error
|
|
if ok {
|
|
if m.PortNum, err = strconv.Atoi(p[1]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if m.Port, err = strconv.Atoi(p[0]); err != nil {
|
|
return err
|
|
}
|
|
p, _ = d.fields(formats, maxLineSize)
|
|
for _, it := range p {
|
|
if it == "*" {
|
|
continue
|
|
}
|
|
pt, err := strconv.Atoi(it)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.Formats = append(m.Formats, &Format{Payload: pt})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Decoder) origin(v string) (*Origin, error) {
|
|
p, ok := d.fields(v, 6)
|
|
if !ok {
|
|
return nil, errFormat
|
|
}
|
|
o := new(Origin)
|
|
o.Username, o.SessionID, o.Network, o.Type, o.Address = p[0], p[1], p[3], p[4], p[5]
|
|
var err error
|
|
if o.SessionVersion, err = d.int(p[2]); err != nil {
|
|
return nil, err
|
|
}
|
|
return o, nil
|
|
}
|
|
|
|
func (d *Decoder) connection(v string) (*Connection, error) {
|
|
p, ok := d.fields(v, 3)
|
|
if !ok {
|
|
return nil, errFormat
|
|
}
|
|
c := new(Connection)
|
|
c.Network, c.Type, c.Address = p[0], p[1], p[2]
|
|
p, ok = d.split(c.Address, '/', 3)
|
|
if ok {
|
|
ttl, err := d.int(p[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
c.TTL = int(ttl)
|
|
p = p[1:]
|
|
}
|
|
if len(p) > 1 {
|
|
num, err := d.int(p[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
c.Address, c.AddressNum = p[0], int(num)
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func (d *Decoder) bandwidth(b Bandwidth, v string) error {
|
|
p, ok := d.split(v, ':', 2)
|
|
if !ok {
|
|
return errFormat
|
|
}
|
|
val, err := d.int(p[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b[p[0]] = int(val)
|
|
return nil
|
|
}
|
|
|
|
func (d *Decoder) timezone(v string) ([]*TimeZone, error) {
|
|
p, _ := d.fields(v, 40)
|
|
zone := make([]*TimeZone, 0, 1)
|
|
var err error
|
|
for len(p) > 1 {
|
|
it := new(TimeZone)
|
|
if it.Time, err = d.time(p[0]); err != nil {
|
|
return nil, err
|
|
}
|
|
if it.Offset, err = d.duration(p[1]); err != nil {
|
|
return nil, err
|
|
}
|
|
zone = append(zone, it)
|
|
p = p[2:]
|
|
}
|
|
return zone, nil
|
|
}
|
|
|
|
func (d *Decoder) key(v string) *Key {
|
|
if p, ok := d.split(v, ':', 2); ok {
|
|
return &Key{p[0], p[1]}
|
|
}
|
|
return &Key{v, ""}
|
|
}
|
|
|
|
func (d *Decoder) attr(v string) *Attr {
|
|
if p, ok := d.split(v, ':', 2); ok {
|
|
return &Attr{p[0], p[1]}
|
|
}
|
|
return &Attr{v, ""}
|
|
}
|
|
|
|
func (d *Decoder) timing(v string) (*Timing, error) {
|
|
p, ok := d.fields(v, 2)
|
|
if !ok {
|
|
return nil, errFormat
|
|
}
|
|
start, err := d.time(p[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
stop, err := d.time(p[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Timing{start, stop}, nil
|
|
}
|
|
|
|
func (d *Decoder) repeat(v string) (*Repeat, error) {
|
|
p, _ := d.fields(v, maxLineSize)
|
|
if len(p) < 2 {
|
|
return nil, errFormat
|
|
}
|
|
r := new(Repeat)
|
|
var err error
|
|
if r.Interval, err = d.duration(p[0]); err != nil {
|
|
return nil, err
|
|
}
|
|
if r.Duration, err = d.duration(p[1]); err != nil {
|
|
return nil, err
|
|
}
|
|
for _, it := range p[2:] {
|
|
off, err := d.duration(it)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
r.Offsets = append(r.Offsets, off)
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
func (d *Decoder) time(v string) (time.Time, error) {
|
|
sec, err := d.int(v)
|
|
if err != nil || sec == 0 {
|
|
return time.Time{}, err
|
|
}
|
|
return epoch.Add(time.Second * time.Duration(sec)), nil
|
|
}
|
|
|
|
func (d *Decoder) duration(v string) (time.Duration, error) {
|
|
m := int64(1)
|
|
if n := len(v) - 1; n >= 0 {
|
|
switch v[n] {
|
|
case 'd':
|
|
m, v = 86400, v[:n]
|
|
case 'h':
|
|
m, v = 3600, v[:n]
|
|
case 'm':
|
|
m, v = 60, v[:n]
|
|
case 's':
|
|
v = v[:n]
|
|
}
|
|
}
|
|
sec, err := d.int(v)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return time.Duration(sec*m) * time.Second, nil
|
|
}
|
|
|
|
func (d *Decoder) int(v string) (int64, error) {
|
|
return strconv.ParseInt(v, 10, 64)
|
|
}
|
|
|
|
func (d *Decoder) fields(s string, n int) ([]string, bool) {
|
|
return d.split(s, ' ', n)
|
|
}
|
|
|
|
func (d *Decoder) split(s string, sep rune, n int) ([]string, bool) {
|
|
p, pos := d.p[:0], 0
|
|
for i, c := range s {
|
|
if c != sep {
|
|
continue
|
|
}
|
|
p = append(p, s[pos:i])
|
|
pos = i + 1
|
|
if len(p) >= n-1 {
|
|
break
|
|
}
|
|
}
|
|
p = append(p, s[pos:])
|
|
d.p = p[:0]
|
|
return p, len(p) == n
|
|
}
|
|
|
|
const maxLineSize = 1024
|
|
|
|
type lineReader interface {
|
|
ReadLine() (string, error)
|
|
}
|
|
|
|
type stringReader struct {
|
|
s string
|
|
}
|
|
|
|
func (r *stringReader) ReadLine() (string, error) {
|
|
s, n := r.s, len(r.s)
|
|
if n == 0 {
|
|
return "", io.EOF
|
|
}
|
|
for i, ch := range s {
|
|
if ch == '\n' {
|
|
r.s = s[i+1:]
|
|
for i > 0 && s[i-1] == '\r' {
|
|
i--
|
|
}
|
|
return s[:i], nil
|
|
}
|
|
}
|
|
r.s = ""
|
|
return s, nil
|
|
}
|
|
|
|
type reader struct {
|
|
b *bufio.Reader
|
|
}
|
|
|
|
func (r *reader) ReadLine() (string, error) {
|
|
b, prefix, err := r.b.ReadLine()
|
|
if prefix && err == nil {
|
|
err = errLineTooLong
|
|
}
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(b), nil
|
|
}
|
|
|
|
var errLineTooLong = errors.New("sdp: line is too long")
|
|
var errUnexpectedField = errors.New("unexpected field")
|
|
var errFormat = errors.New("format error")
|
|
|
|
type errDecode struct {
|
|
err error
|
|
line int
|
|
text string
|
|
}
|
|
|
|
func (e *errDecode) Error() string {
|
|
return fmt.Sprintf("sdp: %s on line %d '%s'", e.err.Error(), e.line, e.text)
|
|
}
|