diff --git a/mpd/content_steering_test.go b/mpd/content_steering_test.go index 891a739..9469541 100644 --- a/mpd/content_steering_test.go +++ b/mpd/content_steering_test.go @@ -108,6 +108,40 @@ func TestStringsToBaseURLsAndBack(t *testing.T) { require.EqualStringSlice(t, ss, BaseURLsToStrings(b)) } +// ISO/IEC 23009-1 Amd 1 (DASH-MPD.xsd) requires ContentSteering to be the last +// child of MPD. Strict XSD validators reject manifests that emit it earlier. +func TestContentSteeringEmittedAfterPeriodAndUTCTiming(t *testing.T) { + m := NewDynamicMPD(DASH_PROFILE_LIVE, "1970-01-01T00:00:00Z", "PT2S", + AttrMediaPresentationDuration("PT10S")) + m.BaseURL = []BaseURLValue{ + {ServiceLocation: "fastly", Value: "https://f.example/x/"}, + } + m.ContentSteering = &ContentSteering{ + DefaultServiceLocation: Strptr("fastly"), + URI: "https://steer.example/steer", + } + out, err := m.WriteToString() + require.NoError(t, err) + + baseURLIdx := strings.Index(out, "") + utcIdx := strings.Index(out, " periodCloseIdx) { + t.Fatalf("ContentSteering must come after : %s", out) + } + if !(csIdx > utcIdx) { + t.Fatalf("ContentSteering must come after UTCTiming: %s", out) + } +} + func TestReadFromStringWithOptionsAppliesSteeringOptionsForWrite(t *testing.T) { xml := ` diff --git a/mpd/mpd.go b/mpd/mpd.go index 2c7fc8c..bb95c5e 100644 --- a/mpd/mpd.go +++ b/mpd/mpd.go @@ -70,6 +70,9 @@ var ( ErrInbandEventStreamSchemeUriEmpty = errors.New("Inband Event Stream schemeIdUri Empty") ) +// MPD is marshaled with encoding/xml (e.g. xml.Encoder.Encode). The marshaller reflects +// on the concrete type and walks exported fields in struct source order, so reordering +// fields changes emitted XML (attributes and child element sequence). type MPD struct { XMLNs *string `xml:"xmlns,attr"` XMLNsDolby *XmlnsAttr `xml:"dolby,attr"` @@ -88,14 +91,14 @@ type MPD struct { PublishTime *string `xml:"publishTime,attr"` TimeShiftBufferDepth *string `xml:"timeShiftBufferDepth,attr"` SuggestedPresentationDelay *Duration `xml:"suggestedPresentationDelay,attr,omitempty"` - BaseURL []BaseURLValue `xml:"BaseURL,omitempty"` - ContentSteering *ContentSteering `xml:"ContentSteering,omitempty"` + BaseURL []BaseURLValue `xml:"BaseURL,omitempty"` Location string `xml:"Location,omitempty"` period *Period - Periods []*Period `xml:"Period,omitempty"` - UTCTiming *DescriptorType `xml:"UTCTiming,omitempty"` - ID string `xml:"id,attr,omitempty"` - Comment string `xml:"-"` + Periods []*Period `xml:"Period,omitempty"` + UTCTiming *DescriptorType `xml:"UTCTiming,omitempty"` + ContentSteering *ContentSteering `xml:"ContentSteering,omitempty"` + ID string `xml:"id,attr,omitempty"` + Comment string `xml:"-"` } type XmlnsAttr struct {