streaming

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

commit aeced255c350bab7d38431993ca2485d4cfa2370
parent 598fdad5e83b6942731dd84ef231c3f8e605e5fe
Author: Oliver Lowe <o@olowe.co>
Date:   Wed, 22 May 2024 14:18:22 +1000

m3u8: test parsing quoted string lists

Diffstat:
Mm3u8/parse.go | 4+++-
Mm3u8/parse_test.go | 22++++++++++++++++++++++
Am3u8/testdata/closed_captions.m3u8 | 6++++++
Dm3u8/testdata/master-with-alternatives.m3u8 | 18------------------
Mm3u8/testdata/master.m3u8 | 28+++++++++++++++++-----------
Am3u8/testdata/media.m3u8 | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Am3u8/testdata/resolution.m3u8 | 3+++
Am3u8/testdata/tos.m3u8 | 14++++++++++++++
8 files changed, 173 insertions(+), 30 deletions(-)

diff --git a/m3u8/parse.go b/m3u8/parse.go @@ -97,6 +97,8 @@ func parseVariant(items chan item) (*Variant, error) { return nil, fmt.Errorf("missing equals after %s", attr) } switch attr.val { + case "PROGRAM-ID": + return nil, fmt.Errorf("parsing PROGRAM-ID attribute unsupported; removed in HLS version 6") case "BANDWIDTH", "AVERAGE-BANDWIDTH": it = <-items if it.typ != itemNumber { @@ -165,7 +167,7 @@ func parseVariant(items chan item) (*Variant, error) { if it.typ != itemString { return nil, fmt.Errorf("parse closed-captions: unexpcted %s", it) } - v.ClosedCaptions = strings.Split(it.val, ",") + v.ClosedCaptions = strings.Split(strings.Trim(it.val, `"`), ",") default: return nil, fmt.Errorf("unknown attribute %s", attr.val) } diff --git a/m3u8/parse_test.go b/m3u8/parse_test.go @@ -4,6 +4,7 @@ import ( "os" "path" "path/filepath" + "reflect" "testing" "time" ) @@ -64,3 +65,24 @@ func TestParseByteRange(t *testing.T) { }) } } + +func TestParseClosedCaptions(t *testing.T) { + f, err := os.Open("testdata/closed_captions.m3u8") + if err != nil { + t.Fatal(err) + } + defer f.Close() + p, err := ParsePlaylist(f) + if err != nil { + t.Fatal(err) + } + cc := p.Variants[0].ClosedCaptions[0] + if cc != "NONE" { + t.Errorf("want closed captions %q, got %s", "NONE", cc) + } + cc2 := p.Variants[1].ClosedCaptions + want := []string{"something", "another"} + if !reflect.DeepEqual(want, cc2) { + t.Errorf("want closed captions %s, got %s", want, cc2) + } +} diff --git a/m3u8/testdata/closed_captions.m3u8 b/m3u8/testdata/closed_captions.m3u8 @@ -0,0 +1,6 @@ +#EXTM3U +#EXT-X-STREAM-INF:CLOSED-CAPTIONS=NONE +tos_1080p/index.m3u8 +## can also have quoted strings, comma-delimeted +#EXT-X-STREAM-INF:CLOSED-CAPTIONS="something,another" +playlist.m3u8 diff --git a/m3u8/testdata/master-with-alternatives.m3u8 b/m3u8/testdata/master-with-alternatives.m3u8 @@ -1,18 +0,0 @@ -#EXTM3U -#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Main",DEFAULT=YES,URI="low/main/audio-video.m3u8" -#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Centerfield",DEFAULT=NO,URI="low/centerfield/audio-video.m3u8" -#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Dugout",DEFAULT=NO,URI="low/dugout/audio-video.m3u8" -#EXT-X-STREAM-INF:BANDWIDTH=1280000,VIDEO="low" -low/main/audio-video.m3u8 -#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Main",DEFAULT=YES,URI="mid/main/audio-video.m3u8" -#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Centerfield",DEFAULT=NO,URI="mid/centerfield/audio-video.m3u8" -#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Dugout",DEFAULT=NO,URI="mid/dugout/audio-video.m3u8" -#EXT-X-STREAM-INF:BANDWIDTH=2560000,VIDEO="mid" -mid/main/audio-video.m3u8 -#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Main",DEFAULT=YES,URI="hi/main/audio-video.m3u8" -#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Centerfield",DEFAULT=NO,URI="hi/centerfield/audio-video.m3u8" -#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Dugout",DEFAULT=NO,URI="hi/dugout/audio-video.m3u8" -#EXT-X-STREAM-INF:BANDWIDTH=7680000,VIDEO="hi" -hi/main/audio-video.m3u8 -#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5" -main/audio-only.m3u8 diff --git a/m3u8/testdata/master.m3u8 b/m3u8/testdata/master.m3u8 @@ -1,12 +1,18 @@ #EXTM3U -#EXT-X-VERSION:3 -#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=300000 -chunklist-b300000.m3u8 -#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=600000 -chunklist-b600000.m3u8 -#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=850000 -chunklist-b850000.m3u8 -#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1000000 -chunklist-b1000000.m3u8 -#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1500000 -chunklist-b1500000.m3u8 +#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Main",DEFAULT=YES,URI="low/main/audio-video.m3u8" +#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Centerfield",DEFAULT=NO,URI="low/centerfield/audio-video.m3u8" +#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Dugout",DEFAULT=NO,URI="low/dugout/audio-video.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=1280000,VIDEO="low" +low/main/audio-video.m3u8 +#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Main",DEFAULT=YES,URI="mid/main/audio-video.m3u8" +#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Centerfield",DEFAULT=NO,URI="mid/centerfield/audio-video.m3u8" +#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Dugout",DEFAULT=NO,URI="mid/dugout/audio-video.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=2560000,VIDEO="mid" +mid/main/audio-video.m3u8 +#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Main",DEFAULT=YES,URI="hi/main/audio-video.m3u8" +#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Centerfield",DEFAULT=NO,URI="hi/centerfield/audio-video.m3u8" +#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Dugout",DEFAULT=NO,URI="hi/dugout/audio-video.m3u8" +#EXT-X-STREAM-INF:BANDWIDTH=7680000,VIDEO="hi" +hi/main/audio-video.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5" +main/audio-only.m3u8 diff --git a/m3u8/testdata/media.m3u8 b/m3u8/testdata/media.m3u8 @@ -0,0 +1,108 @@ +#EXTM3U +#EXT-X-VERSION:3 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-ALLOW-CACHE:YES +#EXT-X-TARGETDURATION:10 +#EXTINF:10.000000, +stream_110k_48k_416x234_000.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_001.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_002.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_003.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_004.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_005.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_006.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_007.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_008.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_009.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_010.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_011.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_012.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_013.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_014.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_015.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_016.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_017.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_018.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_019.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_020.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_021.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_022.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_023.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_024.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_025.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_026.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_027.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_028.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_029.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_030.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_031.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_032.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_033.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_034.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_035.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_036.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_037.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_038.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_039.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_040.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_041.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_042.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_043.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_044.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_045.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_046.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_047.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_048.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_049.ts +#EXTINF:10.000000, +stream_110k_48k_416x234_050.ts +#EXT-X-ENDLIST diff --git a/m3u8/testdata/resolution.m3u8 b/m3u8/testdata/resolution.m3u8 @@ -0,0 +1,3 @@ +#EXTM3U +#EXT-X-STREAM-INF:BANDWIDTH=6725464,RESOLUTION=1920x800 +somefile.mp4 diff --git a/m3u8/testdata/tos.m3u8 b/m3u8/testdata/tos.m3u8 @@ -0,0 +1,14 @@ +#EXTM3U +## Generated with https://github.com/google/shaka-packager version v2.6.1-634af65-release + +#EXT-X-INDEPENDENT-SEGMENTS + +#EXT-X-MEDIA:TYPE=AUDIO,URI="tos_audio/index.m3u8",GROUP-ID="audio",LANGUAGE="en",NAME="ENGLISH",AUTOSELECT=YES,CHANNELS="2" + +## This TTML playlist was generated by shaka-packager when converting a webvtt subtitles into bare bones TTML +#EXT-X-MEDIA:TYPE=SUBTITLES,URI="tos_subs/index.m3u8",GROUP-ID="default-text-group",NAME="from webvtt",AUTOSELECT=YES +## This TTML playlist is copied from Unified Streaming's DASH with segmented TTML playlist +#EXT-X-MEDIA:TYPE=SUBTITLES,URI="tos_ttml/index.m3u8",GROUP-ID="default-text-group",NAME="ttml",AUTOSELECT=YES + +#EXT-X-STREAM-INF:BANDWIDTH=6725464,AVERAGE-BANDWIDTH=6296707,CODECS="avc1.4d4028,mp4a.40.2,stpp.ttml.im1t",RESOLUTION=1920x800,FRAME-RATE=24.000,AUDIO="audio",SUBTITLES="default-text-group",CLOSED-CAPTIONS=NONE +tos_1080p/index.m3u8