streaming

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

commit d1dd8e3025766be03521c488a2f65cdd1202e33b
parent b9fff5d7c191955ae61eb22983fdd23993d995f0
Author: Oliver Lowe <o@olowe.co>
Date:   Tue, 16 Jul 2024 15:51:59 +1000

sdp: parse time zone adjustments

That's all the fields in SDP handled!

Diffstat:
Msdp/parser.go | 15+++++++++++----
Msdp/sdp.go | 36++++++++++++++++++++++++++++++++----
Msdp/sdp_test.go | 45+++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 86 insertions(+), 10 deletions(-)

diff --git a/sdp/parser.go b/sdp/parser.go @@ -20,7 +20,7 @@ type parser struct { session Session } -var ftab = [...]string{"i", "u", "e", "p", "c", "b", "t", "r", "z", "a", "m", "a"} +var ftab = [...]string{"i", "u", "e", "p", "c", "b", "t", "r", "z", "a", "m"} var mtab = [...]string{"i", "c", "b", "a", "m"} @@ -141,10 +141,15 @@ func (p *parser) parseOptional() error { p.session.Repeat = &repeat p.next = ftab[8:] case "z": - return fmt.Errorf("parse time desc %s not yet implemented", p.value) + var err error + p.session.Adjustments, err = parseAdjustments(p.value) + if err != nil { + return fmt.Errorf("parse time adjustments: %w", err) + } + p.next = ftab[9:] case "a": p.session.Attributes = strings.Fields(p.value) - p.next = ftab[9:] + p.next = ftab[10:] case "m": m, err := parseMedia(p.value) if err != nil { @@ -153,6 +158,8 @@ func (p *parser) parseOptional() error { p.session.Media = append(p.session.Media, m) p.next = mtab[:] return p.parseMedia() + default: + return fmt.Errorf("unknown field key %s", p.key) } } return p.err @@ -237,7 +244,7 @@ func parseRepeat(s string) (Repeat, error) { // guard against negative durations, decimals. // these are valid for time.ParseDuration, but not for our Repeat. if i := strings.IndexAny(s, "-."); i > 0 { - return Repeat{}, fmt.Errorf("illegal character %c", s[i]) + return Repeat{}, fmt.Errorf("illegal character in duration: %c", s[i]) } fields := strings.Fields(s) if len(fields) < 3 { diff --git a/sdp/sdp.go b/sdp/sdp.go @@ -26,10 +26,11 @@ type Session struct { Bandwidth *Bandwidth // Time holds the start time and stop time of the Session, at // the first and second index respectively. - Time [2]time.Time - Repeat *Repeat - Attributes []string - Media []Media + Time [2]time.Time + Repeat *Repeat + Adjustments []TimeAdjustment + Attributes []string + Media []Media } type Origin struct { @@ -228,3 +229,30 @@ func parseConnInfo(s string) (ConnInfo, error) { } return conn, nil } + +type TimeAdjustment struct { + When time.Time + Offset time.Duration +} + +func parseAdjustments(line string) ([]TimeAdjustment, error) { + fields := strings.Fields(line) + if len(fields)%2 != 0 { + return nil, fmt.Errorf("odd field count %d", len(fields)) + } + var adjustments []TimeAdjustment + for i := 0; i < len(fields); i += 2 { + var adj TimeAdjustment + t, err := strconv.Atoi(fields[i]) + if err != nil { + return nil, fmt.Errorf("time %s: %w", fields[i], err) + } + adj.When = time.Unix(int64(t-sinceTimeZero), 0).UTC() + adj.Offset, err = parseDuration(fields[i+1]) + if err != nil { + return nil, fmt.Errorf("offset %s: %w", fields[i+1], err) + } + adjustments = append(adjustments, adj) + } + return adjustments, nil +} diff --git a/sdp/sdp_test.go b/sdp/sdp_test.go @@ -26,7 +26,10 @@ func TestReadSession(t *testing.T) { Host: "www.jdoe.example.com", Path: "/home.html", }, - Email: &mail.Address{"Jane Doe", "jane@jdoe.example.com"}, + Email: &mail.Address{ + Name: "Jane Doe", + Address: "jane@jdoe.example.com", + }, Phone: "+16175556011", Connection: &ConnInfo{ Type: "IP4", @@ -61,7 +64,10 @@ func TestReadSession(t *testing.T) { want: Session{ Origin: Origin{"jdoe", 3724394400, 3724394405, "IP4", "198.51.100.1"}, Name: "Call to John Smith", - Email: &mail.Address{"Jane Doe", "jane@jdoe.example.com"}, + Email: &mail.Address{ + Name: "Jane Doe", + Address: "jane@jdoe.example.com", + }, }, }, { @@ -229,3 +235,38 @@ func TestDuration(t *testing.T) { }) } } + +func TestParseAdjustments(t *testing.T) { + var cases = []struct { + name string + line string + want []TimeAdjustment + wantErr bool + }{ + {"uneven", "3730922900 -2h 123456789", nil, true}, + {"missing offset", "3730922900 ", nil, true}, + {"garbage", "hello world!", nil, true}, + { + "from rfc", + "3730928400 -1h 3749680800 0", + []TimeAdjustment{ + {time.Date(2018, time.March, 25, 1, 0, 0, 0, time.UTC), -time.Hour}, + {time.Date(2018, time.October, 28, 2, 0, 0, 0, time.UTC), 0}, + }, + false, + }, + } + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + got, err := parseAdjustments(tt.line) + if err != nil && !tt.wantErr { + t.Fatal(err) + } else if err == nil && tt.wantErr { + t.Error("unexpected nil error") + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("parseAdjustments(%q) = %v, want %v", tt.line, got, tt.want) + } + }) + } +}