commit 14875bf92d627b37f9d33e762f897f9f611d34ec
parent 65ddb6704d4b8a8a8c1dc40ff84556f3c56d1dd9
Author: Oliver Lowe <o@olowe.co>
Date: Wed, 5 Jun 2024 14:51:01 +1000
m3u8: more validation when writing DateRange tags
In particular: we always need to write ID and Start attributes, and
here are some special rules when EndOnNext is set. This serves as prep for work on
References: https://github.com/untangledco/streaming/issues/19
Diffstat:
2 files changed, 20 insertions(+), 13 deletions(-)
diff --git a/m3u8/m3u8.go b/m3u8/m3u8.go
@@ -57,6 +57,7 @@ type Segment struct {
DateRange *DateRange
}
+// Key represents the EXT-X-KEY tag specified in RFC 8216 seciton 4.3.2.3.
// A Key specifies how to decrypt encrypted playlist segments.
type Key struct {
Method EncryptMethod
diff --git a/m3u8/write.go b/m3u8/write.go
@@ -79,20 +79,20 @@ func writeVariant(w io.Writer, v *Variant) (n int, err error) {
}
func writeDateRange(w io.Writer, dr *DateRange) error {
- fmt.Fprint(w, tagDateRange+":")
+ if dr.ID == "" {
+ return fmt.Errorf("empty ID")
+ } else if dr.Start.IsZero() {
+ return fmt.Errorf("zero start time")
+ }
var attrs []string
- if dr.ID != "" {
- attrs = append(attrs, fmt.Sprintf("ID=%q", dr.ID))
+ attrs = append(attrs, fmt.Sprintf("ID=%q", dr.ID))
+ attrs = append(attrs, fmt.Sprintf("START-DATE=%q", dr.Start.Format(time.RFC3339)))
+ if !dr.End.IsZero() {
+ attrs = append(attrs, fmt.Sprintf("END-DATE=%q", dr.End.Format(time.RFC3339)))
}
if dr.Class != "" {
attrs = append(attrs, fmt.Sprintf("CLASS=%q", dr.Class))
}
- if !dr.Start.IsZero() {
- attrs = append(attrs, fmt.Sprintf("START-DATE=%q", dr.Start.Format(time.RFC3339)))
- }
- if !dr.End.IsZero() {
- attrs = append(attrs, fmt.Sprintf("END-DATE=%q", dr.End.Format(time.RFC3339)))
- }
// TODO(otl): dr.Duration, dr.Planned. Differentiate zero value and user-set zero.
// TODO(otl): dr.Custom.
// TODO(otl): dr.CueCommand, when to write this versuse cuein, cueout.
@@ -111,12 +111,18 @@ func writeDateRange(w io.Writer, dr *DateRange) error {
attrs = append(attrs, fmt.Sprintf("SCTE35-OUT=0x%s", hex.EncodeToString(b)))
}
if dr.EndOnNext {
+ if dr.Class == "" {
+ return fmt.Errorf("empty class with end-on-next set")
+ } else if !dr.End.IsZero() {
+ return fmt.Errorf("non-zero end time with end-on-next set")
+ } else if dr.Duration > 0 {
+ return fmt.Errorf("non-zero duration %s with end-on-next set", dr.Duration)
+ }
attrs = append(attrs, "END-ON-NEXT:YES")
}
- if _, err := fmt.Fprintln(w, strings.Join(attrs, ",")); err != nil {
- return err
- }
- return nil
+ tag := tagDateRange + ":" + strings.Join(attrs, ",")
+ _, err := fmt.Fprintln(w, tag)
+ return err
}
func writeMap(w io.Writer, m Map) (n int, err error) {