diff --git a/reader.go b/reader.go index 2d12ef3e..14c1f899 100644 --- a/reader.go +++ b/reader.go @@ -226,6 +226,8 @@ func decodeLineOfMasterPlaylist(p *MasterPlaylist, state *decodingState, line st if strict && err != nil { return err } + case line == "#EXT-X-INDEPENDENT-SEGMENTS": + p.SetIndependentSegments(true) case strings.HasPrefix(line, "#EXT-X-MEDIA:"): var alt Alternative state.listType = MASTER diff --git a/reader_test.go b/reader_test.go index 44ed8c66..4b491635 100644 --- a/reader_test.go +++ b/reader_test.go @@ -218,6 +218,21 @@ func TestDecodeMasterPlaylistWithStreamInfFrameRate(t *testing.T) { } } +func TestDecodeMasterPlaylistWithIndependentSegments(t *testing.T) { + f, err := os.Open("sample-playlists/master-with-independent-segments.m3u8") + if err != nil { + t.Fatal(err) + } + p := NewMasterPlaylist() + err = p.DecodeFrom(bufio.NewReader(f), false) + if err != nil { + t.Fatal(err) + } + if !p.IndependentSegments() { + t.Error("Expected independent segments to be true") + } +} + /**************************** * Begin Test MediaPlaylist * ****************************/ diff --git a/sample-playlists/master-with-independent-segments.m3u8 b/sample-playlists/master-with-independent-segments.m3u8 new file mode 100644 index 00000000..c14ea6a6 --- /dev/null +++ b/sample-playlists/master-with-independent-segments.m3u8 @@ -0,0 +1,5 @@ +#EXTM3U +#EXT-X-VERSION:3 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1828000,NAME="3 high",RESOLUTION=896x504 +chunklist_b1828000_t64NCBoaWdo.m3u8 diff --git a/structure.go b/structure.go index ab02ecc2..06e77408 100644 --- a/structure.go +++ b/structure.go @@ -140,11 +140,12 @@ type MediaPlaylist struct { http://example.com/audio-only.m3u8 */ type MasterPlaylist struct { - Variants []*Variant - Args string // optional arguments placed after URI (URI?Args) - CypherVersion string // non-standard tag for Widevine (see also WV struct) - buf bytes.Buffer - ver uint8 + Variants []*Variant + Args string // optional arguments placed after URI (URI?Args) + CypherVersion string // non-standard tag for Widevine (see also WV struct) + buf bytes.Buffer + ver uint8 + independentSegments bool } // This structure represents variants for master playlist. diff --git a/writer.go b/writer.go index cca955a5..c27c0d25 100644 --- a/writer.go +++ b/writer.go @@ -79,6 +79,10 @@ func (p *MasterPlaylist) Encode() *bytes.Buffer { p.buf.WriteString(strver(p.ver)) p.buf.WriteRune('\n') + if p.IndependentSegments() { + p.buf.WriteString("#EXT-X-INDEPENDENT-SEGMENTS\n") + } + var altsWritten map[string]bool = make(map[string]bool) for _, pl := range p.Variants { @@ -249,6 +253,18 @@ func (p *MasterPlaylist) SetVersion(ver uint8) { p.ver = ver } +// IndependentSegments returns true if all media samples in a segment can be +// decoded without information from other segments. +func (p *MasterPlaylist) IndependentSegments() bool { + return p.independentSegments +} + +// SetIndependentSegments sets whether all media samples in a segment can be +// decoded without information from other segments. +func (p *MasterPlaylist) SetIndependentSegments(b bool) { + p.independentSegments = b +} + // For compatibility with Stringer interface // For example fmt.Printf("%s", sampleMediaList) will encode // playist and print its string representation. diff --git a/writer_test.go b/writer_test.go index a642043f..a77c67a1 100644 --- a/writer_test.go +++ b/writer_test.go @@ -600,6 +600,20 @@ func TestMediaSetWinSize(t *testing.T) { } } +func TestIndependentSegments(t *testing.T) { + m := NewMasterPlaylist() + if m.IndependentSegments() != false { + t.Errorf("Expected independent segments to be false by default") + } + m.SetIndependentSegments(true) + if m.IndependentSegments() != true { + t.Errorf("Expected independent segments to be true") + } + if !strings.Contains(m.Encode().String(), "#EXT-X-INDEPENDENT-SEGMENTS") { + t.Error("Expected playlist to contain EXT-X-INDEPENDENT-SEGMENTS tag") + } +} + func TestMediaPlaylist_Slide(t *testing.T) { m, e := NewMediaPlaylist(3, 4) if e != nil {