streaming

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

commit 1fbee67ca51c80c4a1a167e577983480f0efdb03
parent 12b4cf5015a6017ee960c93072ddffdc51bc746c
Author: Oliver Lowe <o@olowe.co>
Date:   Sat,  1 Jun 2024 19:25:17 +1000

m3u8: separate printing and validation when writing playlist renditions

This will make it way easier to test how we turn one of our Rendition structs into text.

References: https://github.com/untangledco/streaming/issues/7

Diffstat:
Mm3u8/master.go | 37+++++++++++++++++++++++++++++++++++++
Mm3u8/write.go | 40+++-------------------------------------
2 files changed, 40 insertions(+), 37 deletions(-)

diff --git a/m3u8/master.go b/m3u8/master.go @@ -22,6 +22,43 @@ type Rendition struct { Channels []string } +func (r Rendition) String() string { + var attrs []string + attrs = append(attrs, fmt.Sprintf("NAME=%q", r.Name)) + attrs = append(attrs, fmt.Sprintf("TYPE=%s", r.Type)) + if r.URI != "" { + attrs = append(attrs, fmt.Sprintf("URI=%q", r.URI)) + } + attrs = append(attrs, fmt.Sprintf("GROUP-ID=%q", r.Group)) + if r.Language != "" { + attrs = append(attrs, fmt.Sprintf("LANGUAGE=%q", r.Language)) + } + if r.AssocLanguage != "" { + attrs = append(attrs, fmt.Sprintf("ASSOC-LANGUAGE=%q", r.AssocLanguage)) + } + if r.Default { + attrs = append(attrs, "DEFAULT=YES") + } + if r.AutoSelect { + attrs = append(attrs, "AUTOSELECT=YES") + } + if r.Forced { + attrs = append(attrs, "FORCED=YES") + } + if r.Type == MediaClosedCaptions && r.InstreamID != nil { + attrs = append(attrs, fmt.Sprintf("INSTREAM-ID=%q", r.InstreamID)) + } + if len(r.Characteristics) > 0 { + chars := strings.Join(r.Characteristics, ",") + attrs = append(attrs, fmt.Sprintf("CHARACTERISTICS=%q", chars)) + } + if len(r.Channels) > 0 { + channels := strings.Join(r.Channels, "/") + attrs = append(attrs, fmt.Sprintf("CHANNELS=%q", channels)) + } + return tagRendition + ":" + strings.Join(attrs, ",") +} + type MediaType uint8 const ( diff --git a/m3u8/write.go b/m3u8/write.go @@ -34,54 +34,20 @@ func Encode(w io.Writer, p *Playlist) error { if r.Name == "" { return fmt.Errorf("empty name") } - fmt.Fprintf(w, tagRendition+":") - // TODO(otl): use string slice, then strings.Join(s, ",") - // then we don't worry about errors from w.Write - fmt.Fprintf(w, "NAME=%q,", r.Name) rname := fmt.Sprintf("rendition %s", r.Name) if r.Type > MediaClosedCaptions { return fmt.Errorf("%s: unknown type %s", rname, r.Type) } - fmt.Fprintf(w, "TYPE=%s,", r.Type) - if r.URI != "" { - fmt.Fprintf(w, "URI=%q,", r.URI) - } if r.Group == "" { return fmt.Errorf("%s: empty group", rname) } - fmt.Fprintf(w, "GROUP-ID=%q,", r.Group) - if r.Language != "" { - fmt.Fprintf(w, "LANGUAGE=%q,", r.Language) - } - if r.AssocLanguage != "" { - fmt.Fprintf(w, "ASSOC-LANGUAGE=%q,", r.AssocLanguage) - } - if r.Default { - fmt.Fprint(w, "DEFAULT=YES,") - } - if r.AutoSelect { - fmt.Fprint(w, "AUTOSELECT=YES,") - } - if r.Forced { - fmt.Fprint(w, "FORCED=YES,") - } - if r.Type != MediaClosedCaptions && r.InstreamID != nil { return fmt.Errorf("%s: instream-id set but type is %s", rname, r.Type) } - if r.Type == MediaClosedCaptions { - if r.InstreamID == nil { - return fmt.Errorf("%s: nil instream-id", rname) - } - fmt.Fprintf(w, "INSTREAM-ID=%q,", r.InstreamID) - } - if len(r.Characteristics) > 0 { - fmt.Fprintf(w, "CHARACTERISTICS=%q,", strings.Join(r.Characteristics, ",")) - } - if len(r.Channels) > 0 { - fmt.Fprintf(w, "CHANNELS=%q,", strings.Join(r.Channels, "/")) + if r.Type == MediaClosedCaptions && r.InstreamID == nil { + return fmt.Errorf("%s: nil instream-id", rname) } - fmt.Fprintln(w) + fmt.Fprintln(w, r) } for i, v := range p.Variants {