commit 9d0a9c380ce962f56c141d6db6fdacd7a41bf831 from: Oliver Lowe date: Thu May 15 13:24:18 2025 UTC m3u8: support parsing EXT-X-MAP tag commit - a30e111bb1d9e62aac3810e51749241f488e046a commit + 9d0a9c380ce962f56c141d6db6fdacd7a41bf831 blob - b524a68ce5470a6e0137bb827ed7145edd7d5f99 blob + cad16cdf5b3e2c9aeaa5df7f69207b3151bc066a --- m3u8/m3u8.go +++ m3u8/m3u8.go @@ -126,6 +126,9 @@ func parseEncryptMethod(s string) EncryptMethod { return encryptMethodInvalid } +// Map represents the EXT-X-MAP tag. +// A Map informs of any byte sequences to initialise readers of +// some media formats. type Map struct { URI string ByteRange ByteRange blob - d019ec22dc533455dad16f1fa9b75bc93bb9732f blob + 0d706d6f50a48b0bc89983ee9fa72a8a9c18b968 --- m3u8/segment.go +++ m3u8/segment.go @@ -88,6 +88,12 @@ func parseSegment(items chan item, leading item) (*Seg return nil, fmt.Errorf("parse key: %w", err) } seg.Key = &key + case tagMap: + m, err := parseMap(items) + if err != nil { + return nil, fmt.Errorf("parse map: %w", err) + } + seg.Map = &m default: return nil, fmt.Errorf("parsing %s unsupported", it) } @@ -184,13 +190,45 @@ func parseKey(items chan item) (Key, error) { key.FormatVersions[i] = uint32(n) } default: - return key, fmt.Errorf("TODO %s", it.val) + return key, fmt.Errorf("unexpected attribute %q", it.val) } } } - return key, fmt.Errorf("TODO") + return key, fmt.Errorf("unexpected end of tag") } +func parseMap(items chan item) (Map, error) { + var mmap Map + for it := range items { + switch it.typ { + case itemError: + return mmap, errors.New(it.val) + case itemNewline: + return mmap, nil + case itemAttrName: + v := <-items + if v.typ != itemEquals { + return Map{}, fmt.Errorf("expected %q after %s, got %s", "=", it.typ, v) + } + switch it.val { + case "URI": + v = <-items + mmap.URI = strings.Trim(it.val, `"`) + case "BYTERANGE": + v = <-items + r, err := parseByteRange(v.val) + if err != nil { + return Map{}, fmt.Errorf("parse byte range: %w", err) + } + mmap.ByteRange = r + default: + return Map{}, fmt.Errorf("unexpected attribute %q", it.val) + } + } + } + return Map{}, fmt.Errorf("unexpected end of tag") +} + func writeSegments(w io.Writer, segments []Segment) (n int, err error) { for i, seg := range segments { b, err := seg.MarshalText()