streaming

Media streaming and broadcast systems in Go
Log | Files | Refs | README | LICENSE

parser.go (4486B)


      1 package sdp
      2 
      3 import (
      4 	"bufio"
      5 	"fmt"
      6 	"net/url"
      7 	"strconv"
      8 	"strings"
      9 )
     10 
     11 type parser struct {
     12 	*bufio.Scanner
     13 	err error
     14 	// Field name and value from the current line.
     15 	key, value string
     16 	next       []string // expected next field names
     17 
     18 	session Session
     19 }
     20 
     21 var ftab = [...]string{"i", "u", "e", "p", "c", "b", "t", "r", "z", "a", "m"}
     22 
     23 var mtab = [...]string{"i", "c", "b", "a", "m"}
     24 
     25 func (p *parser) scan() bool {
     26 	if !p.Scan() {
     27 		p.err = p.Err()
     28 		return false
     29 	}
     30 	line := strings.TrimSpace(p.Text())
     31 	if line == "" {
     32 		p.err = fmt.Errorf("illegal empty line")
     33 		return false
     34 	}
     35 	k, v, found := strings.Cut(line, "=")
     36 	if !found {
     37 		p.err = fmt.Errorf("parse field %q, missing %q", k, "=")
     38 		return false
     39 	}
     40 	p.key = k
     41 	p.value = v
     42 
     43 	if p.next != nil {
     44 		for i := range p.next {
     45 			if p.next[i] == p.key {
     46 				return true
     47 			}
     48 		}
     49 		p.err = fmt.Errorf("unexpected field %q: expected one of %q", k, p.next)
     50 		return false
     51 	}
     52 	return true
     53 }
     54 
     55 func (p *parser) parse() error {
     56 	next := "v"
     57 	for p.scan() {
     58 		if p.key != next {
     59 			return fmt.Errorf("expected key %q, found %q", next, p.key)
     60 		}
     61 		switch p.key {
     62 		case "v":
     63 			i, err := strconv.Atoi(p.value)
     64 			if err != nil {
     65 				return fmt.Errorf("parse version: %w", err)
     66 			}
     67 			if i != 0 {
     68 				return fmt.Errorf("unsupported version %d", i)
     69 			}
     70 			next = "o"
     71 		case "o":
     72 			o, err := parseOrigin(p.value)
     73 			if err != nil {
     74 				return fmt.Errorf("parse origin: %w", err)
     75 			}
     76 			p.session.Origin = o
     77 			next = "s"
     78 		case "s":
     79 			if p.value == "" {
     80 				return fmt.Errorf("empty name")
     81 			}
     82 			p.session.Name = p.value
     83 			return p.parseOptional()
     84 		}
     85 	}
     86 	return p.err
     87 }
     88 
     89 func (p *parser) parseOptional() error {
     90 	p.next = ftab[:]
     91 	for p.scan() {
     92 		switch p.key {
     93 		case "i":
     94 			p.session.Info = p.value
     95 			p.next = ftab[1:]
     96 		case "u":
     97 			u, err := url.Parse(p.value)
     98 			if err != nil {
     99 				return fmt.Errorf("parse uri: %w", err)
    100 			}
    101 			p.session.URI = u
    102 			p.next = ftab[2:]
    103 		case "e":
    104 			addr, err := parseEmail(p.value)
    105 			if err != nil {
    106 				return fmt.Errorf("parse email: %w", err)
    107 			}
    108 			p.session.Email = addr
    109 			p.next = ftab[3:]
    110 		case "p":
    111 			p.session.Phone = cleanPhone(p.value)
    112 			p.next = ftab[4:]
    113 		case "c":
    114 			conn, err := parseConnInfo(p.value)
    115 			if err != nil {
    116 				return fmt.Errorf("parse connection info: %w", err)
    117 			}
    118 			p.session.Connection = &conn
    119 			p.next = ftab[5:]
    120 		case "b":
    121 			bw, err := parseBandwidth(p.value)
    122 			if err != nil {
    123 				return fmt.Errorf("parse bandwidth line %q: %w", p.value, err)
    124 			}
    125 			p.session.Bandwidth = &bw
    126 			p.next = ftab[6:]
    127 		case "t":
    128 			when, err := parseTimes(p.value)
    129 			if err != nil {
    130 				return fmt.Errorf("parse time description: %w", err)
    131 			}
    132 			p.session.Time = when
    133 			p.next = ftab[7:]
    134 		case "r":
    135 			repeat, err := parseRepeat(p.value)
    136 			if err != nil {
    137 				return fmt.Errorf("parse repeat: %w", err)
    138 			}
    139 			p.session.Repeat = &repeat
    140 			p.next = ftab[8:]
    141 		case "z":
    142 			var err error
    143 			p.session.Adjustments, err = parseAdjustments(p.value)
    144 			if err != nil {
    145 				return fmt.Errorf("parse time adjustments: %w", err)
    146 			}
    147 			p.next = ftab[9:]
    148 		case "a":
    149 			p.session.Attributes = strings.Fields(p.value)
    150 			p.next = ftab[10:]
    151 		case "m":
    152 			m, err := parseMedia(p.value)
    153 			if err != nil {
    154 				return fmt.Errorf("parse media info from %q: %w", p.value, err)
    155 			}
    156 			p.session.Media = append(p.session.Media, m)
    157 			p.next = mtab[:]
    158 			return p.parseMedia()
    159 		default:
    160 			return fmt.Errorf("unknown field key %s", p.key)
    161 		}
    162 	}
    163 	return p.err
    164 }
    165 
    166 func (p *parser) parseMedia() error {
    167 	var media *Media
    168 	if len(p.session.Media) > 0 {
    169 		media = &p.session.Media[len(p.session.Media)-1]
    170 	}
    171 	for p.scan() {
    172 		switch p.key {
    173 		case "i":
    174 			media.Title = p.value
    175 			p.next = mtab[1:]
    176 		case "c":
    177 			conn, err := parseConnInfo(p.value)
    178 			if err != nil {
    179 				return fmt.Errorf("parse connection info: %w", err)
    180 			}
    181 			media.Connection = &conn
    182 			p.next = mtab[2:]
    183 		case "b":
    184 			bw, err := parseBandwidth(p.value)
    185 			if err != nil {
    186 				return fmt.Errorf("parse bandwidth: %w", err)
    187 			}
    188 			media.Bandwidth = &bw
    189 			p.next = mtab[3:]
    190 		case "a":
    191 			media.Attributes = strings.Fields(p.value)
    192 			p.next = mtab[4:]
    193 		case "m":
    194 			m, err := parseMedia(p.value)
    195 			if err != nil {
    196 				return fmt.Errorf("parse media description: %w", err)
    197 			}
    198 			p.session.Media = append(p.session.Media, m)
    199 			media = &p.session.Media[len(p.session.Media)-1]
    200 			p.next = mtab[:]
    201 		default:
    202 			return fmt.Errorf("unsupported field char %s", p.key)
    203 		}
    204 	}
    205 	return p.err
    206 }