Skip to content

Commit

Permalink
add EXT-X-KEY tag support to playlist parser
Browse files Browse the repository at this point in the history
  • Loading branch information
Lysander66 committed Dec 14, 2024
1 parent 047a1d4 commit 6ebab44
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 0 deletions.
19 changes: 19 additions & 0 deletions pkg/playlist/media.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ func (m *Media) Unmarshal(buf []byte) error {
return err
}

var curKey *MediaKey

curSegment := &MediaSegment{}

for {
Expand Down Expand Up @@ -224,6 +226,15 @@ func (m *Media) Unmarshal(buf []byte) error {
return err
}

case strings.HasPrefix(line, "#EXT-X-KEY:"):
line = line[len("#EXT-X-KEY:"):]

curKey = &MediaKey{}
err = curKey.unmarshal(line)
if err != nil {
return err
}

case strings.HasPrefix(line, "#EXT-X-SKIP:"):
line = line[len("#EXT-X-SKIP:"):]

Expand Down Expand Up @@ -278,6 +289,8 @@ func (m *Media) Unmarshal(buf []byte) error {
curSegment.Duration = du
curSegment.Title = strings.TrimSpace(parts[1])

curSegment.Key = curKey

case strings.HasPrefix(line, "#EXT-X-BYTERANGE:"):
line = line[len("#EXT-X-BYTERANGE:"):]

Expand Down Expand Up @@ -387,7 +400,13 @@ func (m Media) Marshal() ([]byte, error) {
ret += m.Skip.marshal()
}

var prevKey *MediaKey
for _, seg := range m.Segments {
if seg.Key != nil && (prevKey == nil || !seg.Key.Equal(prevKey)) {
ret += seg.Key.marshal()
prevKey = seg.Key
}

ret += seg.marshal()
}

Expand Down
117 changes: 117 additions & 0 deletions pkg/playlist/media_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package playlist

import (
"fmt"

"github.com/bluenviron/gohlslib/v2/pkg/playlist/primitives"
)

type MediaKeyMethod string

Check warning on line 9 in pkg/playlist/media_key.go

View workflow job for this annotation

GitHub Actions / golangci-lint

exported: exported type MediaKeyMethod should have comment or be unexported (revive)

const (
MediaKeyMethodNone = "NONE"

Check warning on line 12 in pkg/playlist/media_key.go

View workflow job for this annotation

GitHub Actions / golangci-lint

exported: exported const MediaKeyMethodNone should have comment (or a comment on this block) or be unexported (revive)
MediaKeyMethodAES128 = "AES-128"
MediaKeyMethodSampleAes = "SAMPLE-AES"
)

// MediaKey is a EXT-X-KEY tag.
type MediaKey struct {
// METHOD
// required
Method MediaKeyMethod

// URI is required unless METHOD is NONE
URI string

// IV
IV string

// KEYFORMAT
KeyFormat string

// KEYFORMATVERSIONS
KeyFormatVersions string
}

func (t *MediaKey) unmarshal(v string) error {
attrs, err := primitives.AttributesUnmarshal(v)
if err != nil {
return err
}

for key, val := range attrs {
switch key {
case "METHOD":
km := MediaKeyMethod(val)
if km != MediaKeyMethodNone &&
km != MediaKeyMethodAES128 &&
km != MediaKeyMethodSampleAes {
return fmt.Errorf("invalid method: %s", val)
}
t.Method = km

case "URI":
t.URI = val

case "IV":
t.IV = val

case "KEYFORMAT":
t.KeyFormat = val

case "KEYFORMATVERSIONS":
t.KeyFormatVersions = val
}
}

switch t.Method {
case MediaKeyMethodAES128, MediaKeyMethodSampleAes:
if t.URI == "" {
return fmt.Errorf("URI is required for method %s", t.Method)
}
default:
}

return nil
}

func (t MediaKey) marshal() string {
ret := "#EXT-X-KEY:METHOD=" + string(t.Method)

// If the encryption method is NONE, other attributes MUST NOT be present.
if t.Method != MediaKeyMethodNone {
ret += ",URI=\"" + t.URI + "\""

if t.IV != "" {
ret += ",IV=" + t.IV
}

if t.KeyFormat != "" {
ret += ",KEYFORMAT=\"" + t.KeyFormat + "\""
}

if t.KeyFormatVersions != "" {
ret += ",KEYFORMATVERSIONS=\"" + t.KeyFormatVersions + "\""
}
}

ret += "\n"

return ret
}

func (t *MediaKey) Equal(key *MediaKey) bool {

Check warning on line 103 in pkg/playlist/media_key.go

View workflow job for this annotation

GitHub Actions / golangci-lint

exported: exported method MediaKey.Equal should have comment or be unexported (revive)
if t == key {
return true
}

if key == nil {
return false
}

return t.Method == key.Method &&
t.URI == key.URI &&
t.IV == key.IV &&
t.KeyFormat == key.KeyFormat &&
t.KeyFormatVersions == key.KeyFormatVersions
}
3 changes: 3 additions & 0 deletions pkg/playlist/media_segment.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ type MediaSegment struct {
// EXT-X-BITRATE
Bitrate *int

// EXT-X-KEY
Key *MediaKey

// EXT-X-BYTERANGE
ByteRangeLength *uint64
ByteRangeStart *uint64
Expand Down

0 comments on commit 6ebab44

Please sign in to comment.