Skip to content

Commit

Permalink
Merge pull request #10 from zencoder/DELIVER-4039-opt-a
Browse files Browse the repository at this point in the history
Add option to encode MAP tag before KEY tag
  • Loading branch information
mjneil committed Apr 29, 2024
2 parents 9467fd7 + f9ca25e commit 96e01f0
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 105 deletions.
2 changes: 1 addition & 1 deletion reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ func decodeLineOfMediaPlaylist(p *MediaPlaylist, wv *WV, state *decodingState, l
}
// If EXT-X-MAP appeared before reference to segment (EXTINF) then it linked to this segment
if state.tagMap {
p.Segments[p.last()].Map = &Map{state.xmap.URI, state.xmap.Limit, state.xmap.Offset}
p.Segments[p.last()].Map = &Map{state.xmap.URI, state.xmap.Limit, state.xmap.Offset, false}
// First EXT-X-MAP may appeared in the header of the playlist and linked to first segment
// but for convenient playlist generation it also linked as default playlist map
if p.Map == nil {
Expand Down
7 changes: 4 additions & 3 deletions structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,10 @@ type Key struct {
//
// Realizes EXT-MAP tag.
type Map struct {
URI string
Limit int64 // <n> is length in bytes for the file under URI
Offset int64 // [@o] is offset from the start of the file under URI
URI string
Limit int64 // <n> is length in bytes for the file under URI
Offset int64 // [@o] is offset from the start of the file under URI
BeforeKey bool // If true, the EXT-X-MAP tag will be written before the EXT-X-KEY tag
}

// WV structure represents metadata for Google Widevine playlists.
Expand Down
179 changes: 79 additions & 100 deletions writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,50 +440,18 @@ func (p *MediaPlaylist) Encode() *bytes.Buffer {
}
}

encodeDefaultMap := p.Map != nil
// default key (workaround for Widevine)
if p.Key != nil {
p.buf.WriteString("#EXT-X-KEY:")
p.buf.WriteString("METHOD=")
p.buf.WriteString(p.Key.Method)
if p.Key.Method != "NONE" {
p.buf.WriteString(",URI=\"")
p.buf.WriteString(p.Key.URI)
p.buf.WriteRune('"')
if p.Key.IV != "" {
p.buf.WriteString(",IV=")
p.buf.WriteString(p.Key.IV)
}
if p.Key.Keyformat != "" {
p.buf.WriteString(",KEYFORMAT=\"")
p.buf.WriteString(p.Key.Keyformat)
p.buf.WriteRune('"')
}
if p.Key.Keyformatversions != "" {
p.buf.WriteString(",KEYFORMATVERSIONS=\"")
p.buf.WriteString(p.Key.Keyformatversions)
p.buf.WriteRune('"')
}
if p.Key.KeyID != "" {
p.buf.WriteString(",KEYID=\"")
p.buf.WriteString(p.Key.KeyID)
p.buf.WriteRune('"')
}
if encodeDefaultMap && p.Map.BeforeKey {
p.encodeMap(p.Map)
// set to false so we don't encode it twice
encodeDefaultMap = false
}
p.buf.WriteRune('\n')
p.encodeKey(p.Key)
}
if p.Map != nil {
p.buf.WriteString("#EXT-X-MAP:")
p.buf.WriteString("URI=\"")
p.buf.WriteString(p.Map.URI)
p.buf.WriteRune('"')
if p.Map.Limit > 0 {
p.buf.WriteString(",BYTERANGE=\"")
p.buf.WriteString(strconv.FormatInt(p.Map.Limit, 10))
p.buf.WriteRune('@')
p.buf.WriteString(strconv.FormatInt(p.Map.Offset, 10))
p.buf.WriteRune('"')
}
p.buf.WriteRune('\n')
if encodeDefaultMap {
p.encodeMap(p.Map)
}
if p.MediaType > 0 {
p.buf.WriteString("#EXT-X-PLAYLIST-TYPE:")
Expand Down Expand Up @@ -664,70 +632,27 @@ func (p *MediaPlaylist) Encode() *bytes.Buffer {
}
}
}
// check for key change
if seg.Key != nil && p.Key != seg.Key {
p.buf.WriteString("#EXT-X-KEY:")
p.buf.WriteString("METHOD=")
p.buf.WriteString(seg.Key.Method)
if seg.Key.Method != "NONE" {
p.buf.WriteString(",URI=\"")
p.buf.WriteString(seg.Key.URI)
p.buf.WriteRune('"')
if seg.Key.IV != "" {
p.buf.WriteString(",IV=")
p.buf.WriteString(seg.Key.IV)
}
if seg.Key.Keyformat != "" {
p.buf.WriteString(",KEYFORMAT=\"")
p.buf.WriteString(seg.Key.Keyformat)
p.buf.WriteRune('"')
}
if seg.Key.Keyformatversions != "" {
p.buf.WriteString(",KEYFORMATVERSIONS=\"")
p.buf.WriteString(seg.Key.Keyformatversions)
p.buf.WriteRune('"')
}
if seg.Key.KeyID != "" {
p.buf.WriteString(",KEYID=\"")
p.buf.WriteString(seg.Key.KeyID)
p.buf.WriteRune('"')
}
}
p.buf.WriteRune('\n')
}
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('"')

// Encode segment map if non-nil and there is no default or this segment is a discontinuity
encodeSegMap := seg.Map != nil && (p.Map == nil || seg.Discontinuity)

// check for key change
if seg.Key != nil && p.Key != seg.Key {
if encodeSegMap && seg.Map.BeforeKey {
p.encodeMap(seg.Map)
// set to false so we don't encode it twice
encodeSegMap = false
}
p.buf.WriteRune('\n')
p.encodeKey(seg.Key)
}
// add if default map exists and playlist has discontinuities
if p.Map != nil && seg.Discontinuity && 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('"')
}
p.buf.WriteRune('\n')

if encodeSegMap {
p.encodeMap(seg.Map)
}

if !seg.ProgramDateTime.IsZero() {
p.buf.WriteString("#EXT-X-PROGRAM-DATE-TIME:")
p.buf.WriteString(seg.ProgramDateTime.Format(DATETIME))
Expand Down Expand Up @@ -793,6 +718,52 @@ func (p *MediaPlaylist) Encode() *bytes.Buffer {
return &p.buf
}

func (p *MediaPlaylist) encodeKey(k *Key) {
p.buf.WriteString("#EXT-X-KEY:")
p.buf.WriteString("METHOD=")
p.buf.WriteString(k.Method)
if k.Method != "NONE" {
p.buf.WriteString(",URI=\"")
p.buf.WriteString(k.URI)
p.buf.WriteRune('"')
if k.IV != "" {
p.buf.WriteString(",IV=")
p.buf.WriteString(k.IV)
}
if k.Keyformat != "" {
p.buf.WriteString(",KEYFORMAT=\"")
p.buf.WriteString(k.Keyformat)
p.buf.WriteRune('"')
}
if k.Keyformatversions != "" {
p.buf.WriteString(",KEYFORMATVERSIONS=\"")
p.buf.WriteString(k.Keyformatversions)
p.buf.WriteRune('"')
}
if k.KeyID != "" {
p.buf.WriteString(",KEYID=\"")
p.buf.WriteString(k.KeyID)
p.buf.WriteRune('"')
}
}
p.buf.WriteRune('\n')
}

func (p *MediaPlaylist) encodeMap(m *Map) {
p.buf.WriteString("#EXT-X-MAP:")
p.buf.WriteString("URI=\"")
p.buf.WriteString(m.URI)
p.buf.WriteRune('"')
if m.Limit > 0 {
p.buf.WriteString(",BYTERANGE=\"")
p.buf.WriteString(strconv.FormatInt(m.Limit, 10))
p.buf.WriteRune('@')
p.buf.WriteString(strconv.FormatInt(m.Offset, 10))
p.buf.WriteRune('"')
}
p.buf.WriteRune('\n')
}

// String here for compatibility with Stringer interface For example
// fmt.Printf("%s", sampleMediaList) will encode playist and print its
// string representation.
Expand Down Expand Up @@ -843,7 +814,15 @@ func (p *MediaPlaylist) SetDefaultKey(method, uri, iv, keyformat, keyformatversi
// whole playlist.
func (p *MediaPlaylist) SetDefaultMap(uri string, limit, offset int64) {
version(&p.ver, 5) // due section 4
p.Map = &Map{uri, limit, offset}
p.Map = &Map{uri, limit, offset, false}
}

// SetDefaultMapBeforeKey sets default Media Initialization Section values for
// playlist (pointer to MediaPlaylist.Map). Set EXT-X-MAP tag for the
// whole playlist and have it written before the playlist's default EXT-X-KEY tag.
func (p *MediaPlaylist) SetDefaultMapBeforeKey(uri string, limit, offset int64) {
version(&p.ver, 5) // due section 4
p.Map = &Map{uri, limit, offset, true}
}

// SetIframeOnly marks medialist as consists of only I-frames (Intra
Expand Down Expand Up @@ -878,7 +857,7 @@ func (p *MediaPlaylist) SetMap(uri string, limit, offset int64) error {
return errors.New("playlist is empty")
}
version(&p.ver, 5) // due section 4
p.Segments[p.last()].Map = &Map{uri, limit, offset}
p.Segments[p.last()].Map = &Map{uri, limit, offset, false}
return nil
}

Expand Down
23 changes: 22 additions & 1 deletion writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,29 @@ func TestSetDefaultMapForMediaPlaylist(t *testing.T) {
t.Fatalf("Create media playlist failed: %s", e)
}
p.SetDefaultMap("https://example.com", 1000*1024, 1024*1024)
p.SetDefaultKey("NONE", "", "", "", "", "")

expected := `EXT-X-MAP:URI="https://example.com",BYTERANGE="1024000@1048576"`
expected := `
#EXT-X-KEY:METHOD=NONE
#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())
}
}

func TestSetDefaultMapBeforeKeyForMediaPlaylist(t *testing.T) {
p, e := NewMediaPlaylist(3, 5)
if e != nil {
t.Fatalf("Create media playlist failed: %s", e)
}
p.SetDefaultMapBeforeKey("https://example.com", 1000*1024, 1024*1024)
p.SetDefaultKey("NONE", "", "", "", "", "")

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

0 comments on commit 96e01f0

Please sign in to comment.