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:
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