Commit Diff


commit - 1146e3cfa554e40c95835cd5fd7c65eb7afb40aa
commit + a30e111bb1d9e62aac3810e51749241f488e046a
blob - 1944ff91c17ec4b247f369030d6be9974a145ba2
blob + b524a68ce5470a6e0137bb827ed7145edd7d5f99
--- m3u8/m3u8.go
+++ m3u8/m3u8.go
@@ -99,6 +99,7 @@ const (
 	EncryptMethodNone EncryptMethod = 0 + iota
 	EncryptMethodAES128
 	EncryptMethodSampleAES
+	encryptMethodInvalid EncryptMethod = 255
 )
 
 func (m EncryptMethod) String() string {
@@ -113,6 +114,18 @@ func (m EncryptMethod) String() string {
 	return "invalid"
 }
 
+func parseEncryptMethod(s string) EncryptMethod {
+	switch s {
+	case EncryptMethodNone.String():
+		return EncryptMethodNone
+	case EncryptMethodAES128.String():
+		return EncryptMethodAES128
+	case EncryptMethodSampleAES.String():
+		return EncryptMethodSampleAES
+	}
+	return encryptMethodInvalid
+}
+
 type Map struct {
 	URI       string
 	ByteRange ByteRange
blob - 22fc0b281c644f6e25183ceb6de969d198189b80
blob + d019ec22dc533455dad16f1fa9b75bc93bb9732f
--- m3u8/segment.go
+++ m3u8/segment.go
@@ -2,6 +2,7 @@ package m3u8
 
 import (
 	"bytes"
+	"encoding/hex"
 	"errors"
 	"fmt"
 	"io"
@@ -38,6 +39,12 @@ func parseSegment(items chan item, leading item) (*Seg
 				return nil, fmt.Errorf("parse segment duration: %w", err)
 			}
 			seg.Duration = dur
+		case tagKey:
+			key, err := parseKey(items)
+			if err != nil {
+				return nil, fmt.Errorf("parse key: %w", err)
+			}
+			seg.Key = &key
 		default:
 			return nil, fmt.Errorf("parse leading item %s: unsupported", leading)
 		}
@@ -75,6 +82,12 @@ func parseSegment(items chan item, leading item) (*Seg
 			seg.Range = r
 		case tagDiscontinuity:
 			seg.Discontinuity = true
+		case tagKey:
+			key, err := parseKey(items)
+			if err != nil {
+				return nil, fmt.Errorf("parse key: %w", err)
+			}
+			seg.Key = &key
 		default:
 			return nil, fmt.Errorf("parsing %s unsupported", it)
 		}
@@ -123,6 +136,61 @@ func parseSegmentDuration(it item) (time.Duration, err
 	return time.Duration(microseconds) * time.Microsecond, nil
 }
 
+func parseKey(items chan item) (Key, error) {
+	var key Key
+	for it := range items {
+		switch it.typ {
+		case itemError:
+			return key, errors.New(it.val)
+		case itemNewline:
+			return key, nil
+		case itemAttrName:
+			v := <-items
+			if v.typ != itemEquals {
+				return key, fmt.Errorf("expected %q after %s, got %s", "=", it.typ, v)
+			}
+			switch it.val {
+			case "METHOD":
+				v = <-items
+				key.Method = parseEncryptMethod(v.val)
+				if key.Method == encryptMethodInvalid {
+					return key, fmt.Errorf("bad encrypt method %q", v.val)
+				}
+			case "URI":
+				v = <-items
+				key.URI = strings.Trim(v.val, `"`)
+			case "IV":
+				v = <-items
+				b, err := hex.DecodeString(strings.TrimPrefix(v.val, "0x"))
+				if err != nil {
+					return key, fmt.Errorf("parse initialisation vector: %w", err)
+				}
+				if len(b) != len(key.IV) {
+					return key, fmt.Errorf("bad initialisation length %d, want %d", len(b), len(key.IV))
+				}
+				copy(key.IV[:], b)
+			case "KEYFORMAT":
+				v = <-items
+				key.Format = strings.Trim(v.val, `"`)
+			case "KEYFORMATVERSIONS":
+				v = <-items
+				ss := strings.Split(v.val, "/")
+				key.FormatVersions = make([]uint32, len(ss))
+				for i := range ss {
+					n, err := strconv.Atoi(ss[i])
+					if err != nil {
+						return key, fmt.Errorf("parse key format version: %w", err)
+					}
+					key.FormatVersions[i] = uint32(n)
+				}
+			default:
+				return key, fmt.Errorf("TODO %s", it.val)
+			}
+		}
+	}
+	return key, fmt.Errorf("TODO")
+}
+
 func writeSegments(w io.Writer, segments []Segment) (n int, err error) {
 	for i, seg := range segments {
 		b, err := seg.MarshalText()
blob - 40d13ffe7d9e39b9073e50c523d8d9341d1e6072
blob + c9c777d8c62b46c04c954a12fc84294e87fd250b
--- m3u8/segment_test.go
+++ m3u8/segment_test.go
@@ -113,7 +113,7 @@ func TestParseSegment(t *testing.T) {
 		Key: &Key{
 			Method: EncryptMethodAES128,
 			URI:    "key1.json?f=1041&s=0&p=1822767&m=1506045858",
-			IV:     [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1B, 0xD0, 0x2F},
+			IV:     [...]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1B, 0xD0, 0x2F},
 		},
 		URI: "1041_6_1822767.ts?m=1506045858",
 	}