streaming

Media streaming and broadcast systems in Go
Log | Files | Refs | README | LICENSE

commit baaea663292b3165b7b67c663676c2f8798c7ce5
parent b113263c7e5e7cb104c5fff7a9c61d0c10db31f4
Author: Oliver Lowe <o@olowe.co>
Date:   Tue, 28 May 2024 14:30:14 +1000

m3u8: write all tags, attributes of playlist segments

There were still some that we weren't writing out in m3u8 text format.

Diffstat:
Mm3u8/m3u8.go | 22++++++++++++++++++++++
Mm3u8/write.go | 19+++++++++++++++++++
2 files changed, 41 insertions(+), 0 deletions(-)

diff --git a/m3u8/m3u8.go b/m3u8/m3u8.go @@ -3,8 +3,10 @@ package m3u8 import ( + "encoding/hex" "fmt" "strconv" + "strings" "time" "github.com/untangledco/streaming/scte35" @@ -12,6 +14,8 @@ import ( const MimeType string = "application/vnd.apple.mpegurl" +const RFC3339Milli string = "2006-01-02T15:04:05.999Z07:00" + type Playlist struct { Version int Segments []Segment @@ -70,6 +74,24 @@ type Key struct { IV [16]byte } +func (k Key) String() string { + var attrs []string + attrs = append(attrs, fmt.Sprintf("METHOD=%s", k.Method)) + attrs = append(attrs, fmt.Sprintf("URI=%q", k.URI)) + attrs = append(attrs, fmt.Sprintf("IV=0x%s", hex.EncodeToString(k.IV[:]))) + if k.Format != "" { + attrs = append(attrs, fmt.Sprintf("KEYFORMAT=%q", k.Format)) + } + if k.FormatVersions != nil { + ss := make([]string, len(k.FormatVersions)) + for i := range k.FormatVersions { + ss[i] = strconv.Itoa(int(k.FormatVersions[i])) + } + attrs = append(attrs, fmt.Sprintf("KEYFORMATVERSIONS=%q", strings.Join(ss, "/"))) + } + return strings.Join(attrs, ",") +} + type EncryptMethod uint8 const ( diff --git a/m3u8/write.go b/m3u8/write.go @@ -34,6 +34,18 @@ func Encode(w io.Writer, p *Playlist) error { return fmt.Errorf("write date range: %w", err) } } + if seg.Range != [2]int{0, 0} { + fmt.Fprintf(w, "%s:%s\n", tagByteRange, seg.Range) + } + if seg.Key != nil { + fmt.Fprintf(w, "%s:%s\n", tagKey, seg.Key) + } + if seg.Map != nil { + writeMap(w, *seg.Map) + } + if !seg.DateTime.IsZero() { + fmt.Fprintf(w, "%s:%s\n", tagDateTime, seg.DateTime.Format(RFC3339Milli)) + } us := seg.Duration / time.Microsecond // we do .03f for the same precision as test-streams.mux.dev. fmt.Fprintf(w, "%s:%.03f\n", tagSegmentDuration, float32(us)/1e6) @@ -176,3 +188,10 @@ func writeDateRange(w io.Writer, dr *DateRange) error { } return nil } + +func writeMap(w io.Writer, m Map) (n int, err error) { + if m.ByteRange != [2]int{0, 0} { + return fmt.Fprintf(w, "%s:URI=%q,BYTERANGE=%s\n", tagMap, m.URI, m.ByteRange) + } + return fmt.Fprintf(w, "%s:URI=%q\n", tagMap, m.URI) +}