Skip to content

Commit 6153c9f

Browse files
authored
support multiple baseurl elements (#92)
* support multiple baseurl elements * fix test Co-authored-by: Matthew Neil <[email protected]>
1 parent 01b2380 commit 6153c9f

File tree

7 files changed

+109
-12
lines changed

7 files changed

+109
-12
lines changed

helpers/require/require.go

+14
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,20 @@ func EqualString(t *testing.T, expected, actual string, msgs ...string) {
104104
}
105105
}
106106

107+
func EqualStringSlice(t *testing.T, expected, actual []string, msgs ...string) {
108+
if len(expected) != len(actual) {
109+
t.Errorf("Expected %v but got %v", expected, actual)
110+
for _, msg := range msgs {
111+
t.Errorf("\n" + msg)
112+
}
113+
t.FailNow()
114+
}
115+
for i, e := range expected {
116+
a := actual[i]
117+
EqualString(t, e, a, msgs...)
118+
}
119+
}
120+
107121
func EqualUInt32(t *testing.T, expected, actual uint32, msgs ...string) {
108122
if expected != actual {
109123
t.Errorf("Expected %d but got %d", expected, actual)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="static" mediaPresentationDuration="PT6M16S" minBufferTime="PT1.97S" availabilityStartTime="1970-01-01T00:00:00Z">
3+
<BaseURL>./</BaseURL>
4+
<BaseURL>../a/</BaseURL>
5+
<BaseURL>../b/</BaseURL>
6+
<Period>
7+
<AdaptationSet mimeType="audio/mp4" startWithSAP="1" id="7357" segmentAlignment="true" lang="en">
8+
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" xmlns:cenc="urn:mpeg:cenc:2013" cenc:default_KID="08e36702-8f33-436c-a5dd-60ffe5571e60" value="cenc"></ContentProtection>
9+
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" xmlns:cenc="urn:mpeg:cenc:2013">
10+
<cenc:pssh>AAAAYXBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAEEIARIQWr3VL1VKTyq40GH3YUJRVRoIY2FzdGxhYnMiGFdyM1ZMMVZLVHlxNDBHSDNZVUpSVlE9PTIHZGVmYXVsdA==</cenc:pssh>
11+
</ContentProtection>
12+
<ContentProtection schemeIdUri="urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95" xmlns:cenc="urn:mpeg:cenc:2013" xmlns:mspr="urn:microsoft:playready">
13+
<mspr:pro>BgIAAAEAAQD8ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4ATAA5AFcAOQBXAGsAcABWAEsAawArADQAMABHAEgAMwBZAFUASgBSAFYAUQA9AD0APAAvAEsASQBEAD4APABDAEgARQBDAEsAUwBVAE0APgBJAEsAegBZADIASABaAEwAQQBsAEkAPQA8AC8AQwBIAEUAQwBLAFMAVQBNAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA=</mspr:pro>
14+
<cenc:pssh>AAACJnBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAgYGAgAAAQABAPwBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBMADkAVwA5AFcAawBwAFYASwBrACsANAAwAEcASAAzAFkAVQBKAFIAVgBRAD0APQA8AC8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+AEkASwB6AFkAMgBIAFoATABBAGwASQA9ADwALwBDAEgARQBDAEsAUwBVAE0APgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA==</cenc:pssh>
15+
</ContentProtection>
16+
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role>
17+
<SegmentTemplate duration="1968" initialization="$RepresentationID$/audio/en/init.mp4" media="$RepresentationID$/audio/en/seg-$Number$.m4f" startNumber="0" timescale="1000"></SegmentTemplate>
18+
<Representation audioSamplingRate="44100" bandwidth="67095" codecs="mp4a.40.2" id="800"></Representation>
19+
<Accessibility schemeIdUri="urn:tva:metadata:cs:AudioPurposeCS:2007" value="1"></Accessibility>
20+
</AdaptationSet>
21+
<AdaptationSet mimeType="video/mp4" startWithSAP="1" scanType="progressive" id="7357" segmentAlignment="true">
22+
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" xmlns:cenc="urn:mpeg:cenc:2013" cenc:default_KID="08e36702-8f33-436c-a5dd-60ffe5571e60" value="cenc"></ContentProtection>
23+
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" xmlns:cenc="urn:mpeg:cenc:2013">
24+
<cenc:pssh>AAAAYXBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAEEIARIQWr3VL1VKTyq40GH3YUJRVRoIY2FzdGxhYnMiGFdyM1ZMMVZLVHlxNDBHSDNZVUpSVlE9PTIHZGVmYXVsdA==</cenc:pssh>
25+
</ContentProtection>
26+
<ContentProtection schemeIdUri="urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95" xmlns:cenc="urn:mpeg:cenc:2013" xmlns:mspr="urn:microsoft:playready">
27+
<mspr:pro>BgIAAAEAAQD8ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4ATAA5AFcAOQBXAGsAcABWAEsAawArADQAMABHAEgAMwBZAFUASgBSAFYAUQA9AD0APAAvAEsASQBEAD4APABDAEgARQBDAEsAUwBVAE0APgBJAEsAegBZADIASABaAEwAQQBsAEkAPQA8AC8AQwBIAEUAQwBLAFMAVQBNAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA=</mspr:pro>
28+
<cenc:pssh>AAACJnBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAgYGAgAAAQABAPwBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBMADkAVwA5AFcAawBwAFYASwBrACsANAAwAEcASAAzAFkAVQBKAFIAVgBRAD0APQA8AC8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+AEkASwB6AFkAMgBIAFoATABBAGwASQA9ADwALwBDAEgARQBDAEsAUwBVAE0APgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA==</cenc:pssh>
29+
</ContentProtection>
30+
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role>
31+
<SegmentTemplate duration="1968" initialization="$RepresentationID$/video/1/init.mp4" media="$RepresentationID$/video/1/seg-$Number$.m4f" startNumber="0" timescale="1000"></SegmentTemplate>
32+
<Representation bandwidth="1518664" codecs="avc1.4d401f" frameRate="30000/1001" height="540" id="800" width="960"></Representation>
33+
<Representation bandwidth="1911775" codecs="avc1.4d401f" frameRate="30000/1001" height="576" id="1000" width="1024"></Representation>
34+
<Representation bandwidth="2295158" codecs="avc1.4d401f" frameRate="30000/1001" height="576" id="1200" width="1024"></Representation>
35+
<Representation bandwidth="2780732" codecs="avc1.4d401f" frameRate="30000/1001" height="720" id="1500" width="1280"></Representation>
36+
</AdaptationSet>
37+
<AdaptationSet mimeType="text/vtt" id="7357" lang="en">
38+
<Representation bandwidth="256" id="subtitle_en">
39+
<BaseURL>http://example.com/content/sintel/subtitles/subtitles_en.vtt</BaseURL>
40+
</Representation>
41+
</AdaptationSet>
42+
</Period>
43+
</MPD>

mpd/mpd.go

+15-4
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ type MPD struct {
8080
PublishTime *string `xml:"publishTime,attr"`
8181
TimeShiftBufferDepth *string `xml:"timeShiftBufferDepth,attr"`
8282
SuggestedPresentationDelay *Duration `xml:"suggestedPresentationDelay,attr,omitempty"`
83-
BaseURL string `xml:"BaseURL,omitempty"`
83+
BaseURL []string `xml:"BaseURL,omitempty"`
8484
Location string `xml:"Location,omitempty"`
8585
period *Period
8686
Periods []*Period `xml:"Period,omitempty"`
@@ -91,7 +91,7 @@ type Period struct {
9191
ID string `xml:"id,attr,omitempty"`
9292
Duration Duration `xml:"duration,attr,omitempty"`
9393
Start *Duration `xml:"start,attr,omitempty"`
94-
BaseURL string `xml:"BaseURL,omitempty"`
94+
BaseURL []string `xml:"BaseURL,omitempty"`
9595
SegmentBase *SegmentBase `xml:"SegmentBase,omitempty"`
9696
SegmentList *SegmentList `xml:"SegmentList,omitempty"`
9797
SegmentTemplate *SegmentTemplate `xml:"SegmentTemplate,omitempty"`
@@ -373,7 +373,7 @@ type Representation struct {
373373
Height *int64 `xml:"height,attr"` // Video
374374
ID *string `xml:"id,attr"` // Audio + Video
375375
Width *int64 `xml:"width,attr"` // Video
376-
BaseURL *string `xml:"BaseURL,omitempty"` // On-Demand Profile
376+
BaseURL []string `xml:"BaseURL,omitempty"` // On-Demand Profile
377377
SegmentBase *SegmentBase `xml:"SegmentBase,omitempty"` // On-Demand Profile
378378
SegmentList *SegmentList `xml:"SegmentList,omitempty"`
379379
SegmentTemplate *SegmentTemplate `xml:"SegmentTemplate,omitempty"`
@@ -1123,7 +1123,18 @@ func (r *Representation) SetNewBaseURL(baseURL string) error {
11231123
if baseURL == "" {
11241124
return ErrBaseURLEmpty
11251125
}
1126-
r.BaseURL = Strptr(baseURL)
1126+
// overwrite for backwards compatability
1127+
r.BaseURL = []string{baseURL}
1128+
return nil
1129+
}
1130+
1131+
// Sets the BaseURL for a Representation.
1132+
// baseURL - Base URL as a string (i.e. 800k/output-audio-und.mp4)
1133+
func (r *Representation) AddNewBaseURL(baseURL string) error {
1134+
if baseURL == "" {
1135+
return ErrBaseURLEmpty
1136+
}
1137+
r.BaseURL = append(r.BaseURL, baseURL)
11271138
return nil
11281139
}
11291140

mpd/mpd_read_write_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,17 @@ func TestFullLiveProfileWriteToString(t *testing.T) {
281281
testfixtures.CompareFixture(t, "fixtures/live_profile.mpd", xmlStr)
282282
}
283283

284+
func TestFullLiveProfileMultiBaseURLWriteToString(t *testing.T) {
285+
m := LiveProfile()
286+
require.NotNil(t, m)
287+
288+
m.BaseURL = []string{"./", "../a/", "../b/"}
289+
290+
xmlStr, err := m.WriteToString()
291+
require.NoError(t, err)
292+
testfixtures.CompareFixture(t, "fixtures/live_profile_multi_base_url.mpd", xmlStr)
293+
}
294+
284295
func TestFullLiveProfileWriteToFile(t *testing.T) {
285296
m := LiveProfile()
286297
require.NotNil(t, m)

mpd/mpd_test.go

+22-4
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func TestWidevineContentProtection_ImplementsInterface(t *testing.T) {
154154

155155
func TestNewMPDLiveWithBaseURLInMPD(t *testing.T) {
156156
m := NewMPD(DASH_PROFILE_LIVE, VALID_MEDIA_PRESENTATION_DURATION, VALID_MIN_BUFFER_TIME)
157-
m.BaseURL = VALID_BASE_URL_VIDEO
157+
m.BaseURL = []string{VALID_BASE_URL_VIDEO}
158158
require.NotNil(t, m)
159159
expectedMPD := &MPD{
160160
XMLNs: Strptr("urn:mpeg:dash:schema:mpd:2011"),
@@ -164,7 +164,7 @@ func TestNewMPDLiveWithBaseURLInMPD(t *testing.T) {
164164
MinBufferTime: Strptr(VALID_MIN_BUFFER_TIME),
165165
period: &Period{},
166166
Periods: []*Period{{}},
167-
BaseURL: VALID_BASE_URL_VIDEO,
167+
BaseURL: []string{VALID_BASE_URL_VIDEO},
168168
}
169169

170170
expectedString, err := expectedMPD.WriteToString()
@@ -177,10 +177,10 @@ func TestNewMPDLiveWithBaseURLInMPD(t *testing.T) {
177177

178178
func TestNewMPDLiveWithBaseURLInPeriod(t *testing.T) {
179179
m := NewMPD(DASH_PROFILE_LIVE, VALID_MEDIA_PRESENTATION_DURATION, VALID_MIN_BUFFER_TIME)
180-
m.period.BaseURL = VALID_BASE_URL_VIDEO
180+
m.period.BaseURL = []string{VALID_BASE_URL_VIDEO}
181181
require.NotNil(t, m)
182182
period := &Period{
183-
BaseURL: VALID_BASE_URL_VIDEO,
183+
BaseURL: []string{VALID_BASE_URL_VIDEO},
184184
}
185185
expectedMPD := &MPD{
186186
XMLNs: Strptr("urn:mpeg:dash:schema:mpd:2011"),
@@ -402,6 +402,24 @@ func TestSetNewBaseURLVideo(t *testing.T) {
402402
require.NoError(t, err)
403403
}
404404

405+
func TestAddNewBaseURLVideo(t *testing.T) {
406+
m := NewMPD(DASH_PROFILE_ONDEMAND, VALID_MEDIA_PRESENTATION_DURATION, VALID_MIN_BUFFER_TIME)
407+
videoAS, _ := m.AddNewAdaptationSetVideoWithID("7357", DASH_MIME_TYPE_VIDEO_MP4, VALID_SCAN_TYPE, VALID_SEGMENT_ALIGNMENT, VALID_START_WITH_SAP)
408+
409+
r, _ := videoAS.AddNewRepresentationVideo(VALID_VIDEO_BITRATE, VALID_VIDEO_CODEC, VALID_VIDEO_ID, VALID_VIDEO_FRAMERATE, VALID_VIDEO_WIDTH, VALID_VIDEO_HEIGHT)
410+
411+
err := r.AddNewBaseURL("./")
412+
require.NoError(t, err)
413+
414+
err = r.AddNewBaseURL("../a/")
415+
require.NoError(t, err)
416+
417+
err = r.AddNewBaseURL("../b/")
418+
require.NoError(t, err)
419+
420+
require.EqualStringSlice(t, []string{"./", "../a/", "../b/"}, r.BaseURL)
421+
}
422+
405423
func TestSetNewBaseURLSubtitle(t *testing.T) {
406424
m := NewMPD(DASH_PROFILE_ONDEMAND, VALID_MEDIA_PRESENTATION_DURATION, VALID_MIN_BUFFER_TIME)
407425
subtitleAS, _ := m.AddNewAdaptationSetSubtitleWithID("7357", DASH_MIME_TYPE_SUBTITLE_VTT, VALID_LANG)

mpd/segment_list_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func TestSegmentListDeserialization(t *testing.T) {
2323
if err == nil {
2424
expected := getSegmentListMPD()
2525

26-
require.EqualString(t, expected.Periods[0].BaseURL, m.Periods[0].BaseURL)
26+
require.EqualStringSlice(t, expected.Periods[0].BaseURL, m.Periods[0].BaseURL)
2727

2828
expectedAudioSegList := expected.Periods[0].AdaptationSets[0].Representations[0].SegmentList
2929
audioSegList := m.Periods[0].AdaptationSets[0].Representations[0].SegmentList
@@ -59,7 +59,7 @@ func TestSegmentListDeserialization(t *testing.T) {
5959

6060
func getSegmentListMPD() *MPD {
6161
m := NewMPD(DASH_PROFILE_LIVE, "PT30.016S", "PT2.000S")
62-
m.period.BaseURL = "http://localhost:8002/dash/"
62+
m.period.BaseURL = []string{"http://localhost:8002/dash/"}
6363

6464
aas, _ := m.AddNewAdaptationSetAudioWithID("1", "audio/mp4", true, 1, "English")
6565
ra, _ := aas.AddNewRepresentationAudio(48000, 255000, "mp4a.40.2", "audio_1")

mpd/segment_timeline_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func TestSegmentTimelineDeserialization(t *testing.T) {
3232
m, err := ReadFromString(xml)
3333
require.NoError(t, err)
3434
expected := getSegmentTimelineMPD()
35-
require.EqualString(t, expected.Periods[0].BaseURL, m.Periods[0].BaseURL)
35+
require.EqualStringSlice(t, expected.Periods[0].BaseURL, m.Periods[0].BaseURL)
3636

3737
expectedAudioSegTimeline := expected.Periods[0].AdaptationSets[0].Representations[0].SegmentTemplate.SegmentTimeline
3838
audioSegTimeline := m.Periods[0].AdaptationSets[0].Representations[0].SegmentTemplate.SegmentTimeline
@@ -102,7 +102,7 @@ func getMultiPeriodSegmentTimelineMPD() *MPD {
102102

103103
func getSegmentTimelineMPD() *MPD {
104104
m := NewMPD(DASH_PROFILE_LIVE, "PT65.063S", "PT2.000S")
105-
m.period.BaseURL = "http://localhost:8002/public/"
105+
m.period.BaseURL = []string{"http://localhost:8002/public/"}
106106

107107
aas, _ := m.AddNewAdaptationSetAudioWithID("1", "audio/mp4", true, 1, "English")
108108
ra, _ := aas.AddNewRepresentationAudio(48000, 255000, "mp4a.40.2", "audio_1")

0 commit comments

Comments
 (0)