Skip to content

Commit

Permalink
Encode Segment.Map attribute (grafov#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
andyborn authored and bradleyfalzon committed Jun 28, 2017
1 parent d08b372 commit e911eb7
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 8 deletions.
21 changes: 17 additions & 4 deletions writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,20 @@ func (p *MediaPlaylist) Encode() *bytes.Buffer {
if seg.Discontinuity {
p.buf.WriteString("#EXT-X-DISCONTINUITY\n")
}
// ignore segment Map if default playlist Map is present
if p.Map == nil && seg.Map != nil {
p.buf.WriteString("#EXT-X-MAP:")
p.buf.WriteString("URI=\"")
p.buf.WriteString(seg.Map.URI)
p.buf.WriteRune('"')
if seg.Map.Limit > 0 {
p.buf.WriteString(",BYTERANGE=")
p.buf.WriteString(strconv.FormatInt(seg.Map.Limit, 10))
p.buf.WriteRune('@')
p.buf.WriteString(strconv.FormatInt(seg.Map.Offset, 10))
}
p.buf.WriteRune('\n')
}
if !seg.ProgramDateTime.IsZero() {
p.buf.WriteString("#EXT-X-PROGRAM-DATE-TIME:")
p.buf.WriteString(seg.ProgramDateTime.Format(DATETIME))
Expand Down Expand Up @@ -631,9 +645,8 @@ func (p *MediaPlaylist) SetDefaultKey(method, uri, iv, keyformat, keyformatversi
return nil
}

// Set map appeared once in header of the playlist (pointer to MediaPlaylist.Key).
// It useful when map not changed during playback.
// Set tag for the whole list.
// Set default Media Initialization Section values for playlist (pointer to MediaPlaylist.Map).
// Set EXT-X-MAP tag for the whole playlist.
func (p *MediaPlaylist) SetDefaultMap(uri string, limit, offset int64) {
version(&p.ver, 5) // due section 4
p.Map = &Map{uri, limit, offset}
Expand Down Expand Up @@ -663,7 +676,7 @@ func (p *MediaPlaylist) SetKey(method, uri, iv, keyformat, keyformatversions str
return nil
}

// Set encryption key for the current segment of media playlist (pointer to Segment.Key)
// Set map for the current segment of media playlist (pointer to Segment.Map)
func (p *MediaPlaylist) SetMap(uri string, limit, offset int64) error {
if p.count == 0 {
return errors.New("playlist is empty")
Expand Down
65 changes: 61 additions & 4 deletions writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,24 @@ func TestSetDefaultKeyForMediaPlaylist(t *testing.T) {
}
}

// Create new media playlist
// Set default map
func TestSetDefaultMapForMediaPlaylist(t *testing.T) {
p, e := NewMediaPlaylist(3, 5)
if e != nil {
t.Fatalf("Create media playlist failed: %s", e)
}
p.SetDefaultMap("https://example.com", 1000*1024, 1024*1024)

expected := `EXT-X-MAP:URI="https://example.com",BYTERANGE=1024000@1048576`
if !strings.Contains(p.String(), expected) {
t.Fatalf("Media playlist did not contain: %s\nMedia Playlist:\n%v", expected, p.String())
}
}

// Create new media playlist
// Add segment to media playlist
// Set map
// Set map on segment
func TestSetMapForMediaPlaylist(t *testing.T) {
p, e := NewMediaPlaylist(3, 5)
if e != nil {
Expand All @@ -313,6 +328,45 @@ func TestSetMapForMediaPlaylist(t *testing.T) {
if e != nil {
t.Errorf("Set map to a media playlist failed: %s", e)
}

expected := `EXT-X-MAP:URI="https://example.com",BYTERANGE=1024000@1048576
#EXTINF:5.000,
test01.ts`
if !strings.Contains(p.String(), expected) {
t.Fatalf("Media playlist did not contain: %s\nMedia Playlist:\n%v", expected, p.String())
}
}

// Create new media playlist
// Set default map
// Add segment to media playlist
// Set map on segment (should be ignored when encoding)
func TestEncodeMediaPlaylistWithDefaultMap(t *testing.T) {
p, e := NewMediaPlaylist(3, 5)
if e != nil {
t.Fatalf("Create media playlist failed: %s", e)
}
p.SetDefaultMap("https://example.com", 1000*1024, 1024*1024)

e = p.Append("test01.ts", 5.0, "")
if e != nil {
t.Errorf("Add 1st segment to a media playlist failed: %s", e)
}
e = p.SetMap("https://notencoded.com", 1000*1024, 1024*1024)
if e != nil {
t.Errorf("Set map to segment failed: %s", e)
}

encoded := p.String()
expected := `EXT-X-MAP:URI="https://example.com",BYTERANGE=1024000@1048576`
if !strings.Contains(encoded, expected) {
t.Fatalf("Media playlist did not contain: %s\nMedia Playlist:\n%v", expected, encoded)
}

ignored := `EXT-X-MAP:URI="https://notencoded.com"`
if strings.Contains(encoded, ignored) {
t.Fatalf("Media playlist contains non default map: %s\nMedia Playlist:\n%v", ignored, encoded)
}
}

// Create new media playlist
Expand Down Expand Up @@ -566,7 +620,10 @@ func TestNewMasterPlaylistWithAlternatives(t *testing.T) {
if m.ver != 4 {
t.Fatalf("Expected version 4, actual, %d", m.ver)
}
fmt.Printf("%v\n", m)
expected := `#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio",NAME="main",DEFAULT=YES,AUTOSELECT=YES,LANGUAGE="english",URI="800/rendition.m3u8"`
if !strings.Contains(m.String(), expected) {
t.Fatalf("Master playlist did not contain: %s\nMaster Playlist:\n%v", expected, m.String())
}
}

// Create new master playlist supporting CLOSED-CAPTIONS=NONE
Expand Down Expand Up @@ -636,7 +693,7 @@ func TestEncodeMasterPlaylistWithExistingQuery(t *testing.T) {
}
m.Append("chunklist1.m3u8?k1=v1&k2=v2", p, VariantParams{ProgramId: 123, Bandwidth: 1500000, Resolution: "576x480"})
m.Args = "k3=v3"
if !strings.Contains(m.String(), "chunklist1.m3u8?k1=v1&k2=v2&k3=v3\n") {
if !strings.Contains(m.String(), `chunklist1.m3u8?k1=v1&k2=v2&k3=v3`) {
t.Errorf("Encode master with existing args failed")
}
}
Expand Down Expand Up @@ -678,7 +735,7 @@ func TestEncodeMasterPlaylistWithStreamInfName(t *testing.T) {
if m.Variants[0].Name != "HD 960p" {
t.Fatalf("Create master with Name in EXT-X-STREAM-INF failed")
}
if !strings.Contains(m.String(), "NAME=\"HD 960p\"") {
if !strings.Contains(m.String(), `NAME="HD 960p"`) {
t.Fatalf("Encode master with Name in EXT-X-STREAM-INF failed")
}
}
Expand Down

0 comments on commit e911eb7

Please sign in to comment.