streaming

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

commit e5a2561859aaf24e6368af6a4e82fc8112bc3d08
parent 3083e3dd1cb493efa7557138726614b1d3758e81
Author: Oliver Lowe <o@olowe.co>
Date:   Sun,  4 May 2025 14:00:30 +1000

m3u8: ignore trailing commas, support quoted string attributes

Playlist from
https://test-streams.mux.dev/dai-discontinuity-deltatre/manifest.m3u8
containing discontinuities exposed the bugs.

Diffstat:
Mm3u8/lex.go | 13++++++++++---
Am3u8/testdata/discontinuities.m3u8 | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 89 insertions(+), 3 deletions(-)

diff --git a/m3u8/lex.go b/m3u8/lex.go @@ -127,6 +127,7 @@ func newLexer(r io.Reader) *lexer { return &lexer{ sc: bufio.NewScanner(r), items: make(chan item), + debug: false, } } @@ -135,7 +136,10 @@ func lexStart(l *lexer) stateFn { if l.sc.Text() == "" { continue // ignore blank lines } - l.input = l.sc.Text() + "\n" + text := l.sc.Text() + // ignore valid but meaningless trailing commas. + text = strings.TrimSuffix(text, ",") + l.input = text + "\n" l.pos = 0 l.start = 0 if strings.HasPrefix(l.input, tagStart) { @@ -145,7 +149,7 @@ func lexStart(l *lexer) stateFn { } // not a tag, so must be a URL. // emit the URL, then the newline we appended ourselves. - l.pos = len(l.sc.Text()) + l.pos = len(text) l.emit(itemURL) l.emit(itemNewline) } @@ -179,7 +183,7 @@ func lexTagName(l *lexer) stateFn { case ':': l.emit(itemTag) l.next() - l.ignore() + l.ignore() // ignore ':' after tag name return lexAttrs(l) } return l.errorf("illegal tag character %q", r) @@ -225,6 +229,9 @@ func lexAttrs(l *lexer) stateFn { return lexAttrValue(l) case r == '@': return lexAttrValue(l) + case r == '"': + l.next() + return lexQString(l) default: return l.errorf("illegal character %q in attribute name", r) } diff --git a/m3u8/testdata/discontinuities.m3u8 b/m3u8/testdata/discontinuities.m3u8 @@ -0,0 +1,78 @@ +#EXTM3U +#EXT-X-YOSPACE-ANALYTICS-URL:"https://csm-e-cebteurxaws416-2hjvnjz85og7.tls1.yospace.com/csm/analytics;jsessionid=E97CB20FD3BCE93F89ADDB12E8C82895.csm-e-cebteurxaws416-2hjvnjz85og7.tls1.yospace.com" +#EXT-X-TARGETDURATION:10 +#EXT-X-VERSION:3 +#EXT-X-PLAYLIST-TYPE:EVENT +#EXT-X-KEY:METHOD=AES-128,URI="key1.json?f=1041&s=0&p=1822767&m=1506045858",IV=0x000000000000000000000000001BD02F +#EXTINF:10, +1041_6_1822767.ts?m=1506045858 +#EXT-X-KEY:METHOD=AES-128,URI="key2.json?f=1041&s=0&p=1822768&m=1506045858",IV=0x000000000000000000000000001BD030 +#EXTINF:10, +1041_6_1822768.ts?m=1506045858 +#EXT-X-KEY:METHOD=AES-128,URI="key3.json?f=1041&s=0&p=1822769&m=1506045858",IV=0x000000000000000000000000001BD031 +#EXTINF:10, +1041_6_1822769.ts?m=1506045858 +#EXT-X-KEY:METHOD=AES-128,URI="key4.json?f=1041&s=0&p=1822770&m=1506045858",IV=0x000000000000000000000000001BD032 +#EXTINF:6.28, +1041_6_1822770.ts?m=1506045858 +#EXT-X-DISCONTINUITY +#EXT-X-KEY:METHOD=NONE +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-1.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-2.ts +#EXTINF:7.2, +u-6400-m-720x408-1628-a-96-1-3.ts +#EXTINF:2.8, +u-6400-m-720x408-1628-a-96-1-4.ts +#EXT-X-DISCONTINUITY +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-1-1.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-2-1.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-3-1.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-4-1.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-5.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-6.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-7.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-8.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-9.ts +#EXTINF:7.56, +u-6400-m-720x408-1628-a-96-1-10.ts +#EXTINF:2.44, +u-6400-m-720x408-1628-a-96-1-11.ts +#EXT-X-DISCONTINUITY +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-1-2.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-2.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-3-2.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-4-2.ts +#EXTINF:10, +u-6400-m-720x408-1628-a-96-1-5-1.ts +#EXT-X-DISCONTINUITY +#EXT-X-KEY:METHOD=AES-128,URI="key5.json?f=1041&s=0&p=1822791&m=1506045858",IV=0x000000000000000000000000001BD047 +#EXTINF:9.72, +1041_6_1822791.ts?m=1506045858 +#EXT-X-KEY:METHOD=AES-128,URI="key6.json?f=1041&s=0&p=1822792&m=1506045858",IV=0x000000000000000000000000001BD048 +#EXTINF:10, +1041_6_1822792.ts?m=1506045858 +#EXT-X-KEY:METHOD=AES-128,URI="key7.json?f=1041&s=0&p=1822793&m=1506045858",IV=0x000000000000000000000000001BD049 +#EXTINF:10, +1041_6_1822793.ts?m=1506045858 +#EXT-X-KEY:METHOD=AES-128,URI="key8.json?f=1041&s=0&p=1822794&m=1506045858",IV=0x000000000000000000000000001BD04A +#EXTINF:10, +1041_6_1822794.ts?m=1506045858 +#EXT-X-KEY:METHOD=AES-128,URI="key9.json?f=1041&s=0&p=1822795&m=1506045858",IV=0x000000000000000000000000001BD04B +#EXTINF:10, +1041_6_1822795.ts?m=1506045858 +#EXT-X-ENDLIST +\ No newline at end of file