Skip to content

Commit

Permalink
support multiple baseurl elements (#92)
Browse files Browse the repository at this point in the history
* support multiple baseurl elements

* fix test

Co-authored-by: Matthew Neil <[email protected]>
  • Loading branch information
mjneil and mjneil authored Apr 28, 2022
1 parent 01b2380 commit 6153c9f
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 12 deletions.
14 changes: 14 additions & 0 deletions helpers/require/require.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,20 @@ func EqualString(t *testing.T, expected, actual string, msgs ...string) {
}
}

func EqualStringSlice(t *testing.T, expected, actual []string, msgs ...string) {
if len(expected) != len(actual) {
t.Errorf("Expected %v but got %v", expected, actual)
for _, msg := range msgs {
t.Errorf("\n" + msg)
}
t.FailNow()
}
for i, e := range expected {
a := actual[i]
EqualString(t, e, a, msgs...)
}
}

func EqualUInt32(t *testing.T, expected, actual uint32, msgs ...string) {
if expected != actual {
t.Errorf("Expected %d but got %d", expected, actual)
Expand Down
43 changes: 43 additions & 0 deletions mpd/fixtures/live_profile_multi_base_url.mpd
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<BaseURL>./</BaseURL>
<BaseURL>../a/</BaseURL>
<BaseURL>../b/</BaseURL>
<Period>
<AdaptationSet mimeType="audio/mp4" startWithSAP="1" id="7357" segmentAlignment="true" lang="en">
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" xmlns:cenc="urn:mpeg:cenc:2013" cenc:default_KID="08e36702-8f33-436c-a5dd-60ffe5571e60" value="cenc"></ContentProtection>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" xmlns:cenc="urn:mpeg:cenc:2013">
<cenc:pssh>AAAAYXBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAEEIARIQWr3VL1VKTyq40GH3YUJRVRoIY2FzdGxhYnMiGFdyM1ZMMVZLVHlxNDBHSDNZVUpSVlE9PTIHZGVmYXVsdA==</cenc:pssh>
</ContentProtection>
<ContentProtection schemeIdUri="urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95" xmlns:cenc="urn:mpeg:cenc:2013" xmlns:mspr="urn:microsoft:playready">
<mspr:pro>BgIAAAEAAQD8ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4ATAA5AFcAOQBXAGsAcABWAEsAawArADQAMABHAEgAMwBZAFUASgBSAFYAUQA9AD0APAAvAEsASQBEAD4APABDAEgARQBDAEsAUwBVAE0APgBJAEsAegBZADIASABaAEwAQQBsAEkAPQA8AC8AQwBIAEUAQwBLAFMAVQBNAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA=</mspr:pro>
<cenc:pssh>AAACJnBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAgYGAgAAAQABAPwBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBMADkAVwA5AFcAawBwAFYASwBrACsANAAwAEcASAAzAFkAVQBKAFIAVgBRAD0APQA8AC8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+AEkASwB6AFkAMgBIAFoATABBAGwASQA9ADwALwBDAEgARQBDAEsAUwBVAE0APgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA==</cenc:pssh>
</ContentProtection>
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role>
<SegmentTemplate duration="1968" initialization="$RepresentationID$/audio/en/init.mp4" media="$RepresentationID$/audio/en/seg-$Number$.m4f" startNumber="0" timescale="1000"></SegmentTemplate>
<Representation audioSamplingRate="44100" bandwidth="67095" codecs="mp4a.40.2" id="800"></Representation>
<Accessibility schemeIdUri="urn:tva:metadata:cs:AudioPurposeCS:2007" value="1"></Accessibility>
</AdaptationSet>
<AdaptationSet mimeType="video/mp4" startWithSAP="1" scanType="progressive" id="7357" segmentAlignment="true">
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" xmlns:cenc="urn:mpeg:cenc:2013" cenc:default_KID="08e36702-8f33-436c-a5dd-60ffe5571e60" value="cenc"></ContentProtection>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" xmlns:cenc="urn:mpeg:cenc:2013">
<cenc:pssh>AAAAYXBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAEEIARIQWr3VL1VKTyq40GH3YUJRVRoIY2FzdGxhYnMiGFdyM1ZMMVZLVHlxNDBHSDNZVUpSVlE9PTIHZGVmYXVsdA==</cenc:pssh>
</ContentProtection>
<ContentProtection schemeIdUri="urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95" xmlns:cenc="urn:mpeg:cenc:2013" xmlns:mspr="urn:microsoft:playready">
<mspr:pro>BgIAAAEAAQD8ATwAVwBSAE0ASABFAEEARABFAFIAIAB4AG0AbABuAHMAPQAiAGgAdAB0AHAAOgAvAC8AcwBjAGgAZQBtAGEAcwAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ALwBEAFIATQAvADIAMAAwADcALwAwADMALwBQAGwAYQB5AFIAZQBhAGQAeQBIAGUAYQBkAGUAcgAiACAAdgBlAHIAcwBpAG8AbgA9ACIANAAuADAALgAwAC4AMAAiAD4APABEAEEAVABBAD4APABQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsARQBZAEwARQBOAD4AMQA2ADwALwBLAEUAWQBMAEUATgA+ADwAQQBMAEcASQBEAD4AQQBFAFMAQwBUAFIAPAAvAEEATABHAEkARAA+ADwALwBQAFIATwBUAEUAQwBUAEkATgBGAE8APgA8AEsASQBEAD4ATAA5AFcAOQBXAGsAcABWAEsAawArADQAMABHAEgAMwBZAFUASgBSAFYAUQA9AD0APAAvAEsASQBEAD4APABDAEgARQBDAEsAUwBVAE0APgBJAEsAegBZADIASABaAEwAQQBsAEkAPQA8AC8AQwBIAEUAQwBLAFMAVQBNAD4APAAvAEQAQQBUAEEAPgA8AC8AVwBSAE0ASABFAEEARABFAFIAPgA=</mspr:pro>
<cenc:pssh>AAACJnBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAAgYGAgAAAQABAPwBPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgBMADkAVwA5AFcAawBwAFYASwBrACsANAAwAEcASAAzAFkAVQBKAFIAVgBRAD0APQA8AC8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+AEkASwB6AFkAMgBIAFoATABBAGwASQA9ADwALwBDAEgARQBDAEsAUwBVAE0APgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA==</cenc:pssh>
</ContentProtection>
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role>
<SegmentTemplate duration="1968" initialization="$RepresentationID$/video/1/init.mp4" media="$RepresentationID$/video/1/seg-$Number$.m4f" startNumber="0" timescale="1000"></SegmentTemplate>
<Representation bandwidth="1518664" codecs="avc1.4d401f" frameRate="30000/1001" height="540" id="800" width="960"></Representation>
<Representation bandwidth="1911775" codecs="avc1.4d401f" frameRate="30000/1001" height="576" id="1000" width="1024"></Representation>
<Representation bandwidth="2295158" codecs="avc1.4d401f" frameRate="30000/1001" height="576" id="1200" width="1024"></Representation>
<Representation bandwidth="2780732" codecs="avc1.4d401f" frameRate="30000/1001" height="720" id="1500" width="1280"></Representation>
</AdaptationSet>
<AdaptationSet mimeType="text/vtt" id="7357" lang="en">
<Representation bandwidth="256" id="subtitle_en">
<BaseURL>http://example.com/content/sintel/subtitles/subtitles_en.vtt</BaseURL>
</Representation>
</AdaptationSet>
</Period>
</MPD>
19 changes: 15 additions & 4 deletions mpd/mpd.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ type MPD struct {
PublishTime *string `xml:"publishTime,attr"`
TimeShiftBufferDepth *string `xml:"timeShiftBufferDepth,attr"`
SuggestedPresentationDelay *Duration `xml:"suggestedPresentationDelay,attr,omitempty"`
BaseURL string `xml:"BaseURL,omitempty"`
BaseURL []string `xml:"BaseURL,omitempty"`
Location string `xml:"Location,omitempty"`
period *Period
Periods []*Period `xml:"Period,omitempty"`
Expand All @@ -91,7 +91,7 @@ type Period struct {
ID string `xml:"id,attr,omitempty"`
Duration Duration `xml:"duration,attr,omitempty"`
Start *Duration `xml:"start,attr,omitempty"`
BaseURL string `xml:"BaseURL,omitempty"`
BaseURL []string `xml:"BaseURL,omitempty"`
SegmentBase *SegmentBase `xml:"SegmentBase,omitempty"`
SegmentList *SegmentList `xml:"SegmentList,omitempty"`
SegmentTemplate *SegmentTemplate `xml:"SegmentTemplate,omitempty"`
Expand Down Expand Up @@ -373,7 +373,7 @@ type Representation struct {
Height *int64 `xml:"height,attr"` // Video
ID *string `xml:"id,attr"` // Audio + Video
Width *int64 `xml:"width,attr"` // Video
BaseURL *string `xml:"BaseURL,omitempty"` // On-Demand Profile
BaseURL []string `xml:"BaseURL,omitempty"` // On-Demand Profile
SegmentBase *SegmentBase `xml:"SegmentBase,omitempty"` // On-Demand Profile
SegmentList *SegmentList `xml:"SegmentList,omitempty"`
SegmentTemplate *SegmentTemplate `xml:"SegmentTemplate,omitempty"`
Expand Down Expand Up @@ -1123,7 +1123,18 @@ func (r *Representation) SetNewBaseURL(baseURL string) error {
if baseURL == "" {
return ErrBaseURLEmpty
}
r.BaseURL = Strptr(baseURL)
// overwrite for backwards compatability
r.BaseURL = []string{baseURL}
return nil
}

// Sets the BaseURL for a Representation.
// baseURL - Base URL as a string (i.e. 800k/output-audio-und.mp4)
func (r *Representation) AddNewBaseURL(baseURL string) error {
if baseURL == "" {
return ErrBaseURLEmpty
}
r.BaseURL = append(r.BaseURL, baseURL)
return nil
}

Expand Down
11 changes: 11 additions & 0 deletions mpd/mpd_read_write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,17 @@ func TestFullLiveProfileWriteToString(t *testing.T) {
testfixtures.CompareFixture(t, "fixtures/live_profile.mpd", xmlStr)
}

func TestFullLiveProfileMultiBaseURLWriteToString(t *testing.T) {
m := LiveProfile()
require.NotNil(t, m)

m.BaseURL = []string{"./", "../a/", "../b/"}

xmlStr, err := m.WriteToString()
require.NoError(t, err)
testfixtures.CompareFixture(t, "fixtures/live_profile_multi_base_url.mpd", xmlStr)
}

func TestFullLiveProfileWriteToFile(t *testing.T) {
m := LiveProfile()
require.NotNil(t, m)
Expand Down
26 changes: 22 additions & 4 deletions mpd/mpd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func TestWidevineContentProtection_ImplementsInterface(t *testing.T) {

func TestNewMPDLiveWithBaseURLInMPD(t *testing.T) {
m := NewMPD(DASH_PROFILE_LIVE, VALID_MEDIA_PRESENTATION_DURATION, VALID_MIN_BUFFER_TIME)
m.BaseURL = VALID_BASE_URL_VIDEO
m.BaseURL = []string{VALID_BASE_URL_VIDEO}
require.NotNil(t, m)
expectedMPD := &MPD{
XMLNs: Strptr("urn:mpeg:dash:schema:mpd:2011"),
Expand All @@ -164,7 +164,7 @@ func TestNewMPDLiveWithBaseURLInMPD(t *testing.T) {
MinBufferTime: Strptr(VALID_MIN_BUFFER_TIME),
period: &Period{},
Periods: []*Period{{}},
BaseURL: VALID_BASE_URL_VIDEO,
BaseURL: []string{VALID_BASE_URL_VIDEO},
}

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

func TestNewMPDLiveWithBaseURLInPeriod(t *testing.T) {
m := NewMPD(DASH_PROFILE_LIVE, VALID_MEDIA_PRESENTATION_DURATION, VALID_MIN_BUFFER_TIME)
m.period.BaseURL = VALID_BASE_URL_VIDEO
m.period.BaseURL = []string{VALID_BASE_URL_VIDEO}
require.NotNil(t, m)
period := &Period{
BaseURL: VALID_BASE_URL_VIDEO,
BaseURL: []string{VALID_BASE_URL_VIDEO},
}
expectedMPD := &MPD{
XMLNs: Strptr("urn:mpeg:dash:schema:mpd:2011"),
Expand Down Expand Up @@ -402,6 +402,24 @@ func TestSetNewBaseURLVideo(t *testing.T) {
require.NoError(t, err)
}

func TestAddNewBaseURLVideo(t *testing.T) {
m := NewMPD(DASH_PROFILE_ONDEMAND, VALID_MEDIA_PRESENTATION_DURATION, VALID_MIN_BUFFER_TIME)
videoAS, _ := m.AddNewAdaptationSetVideoWithID("7357", DASH_MIME_TYPE_VIDEO_MP4, VALID_SCAN_TYPE, VALID_SEGMENT_ALIGNMENT, VALID_START_WITH_SAP)

r, _ := videoAS.AddNewRepresentationVideo(VALID_VIDEO_BITRATE, VALID_VIDEO_CODEC, VALID_VIDEO_ID, VALID_VIDEO_FRAMERATE, VALID_VIDEO_WIDTH, VALID_VIDEO_HEIGHT)

err := r.AddNewBaseURL("./")
require.NoError(t, err)

err = r.AddNewBaseURL("../a/")
require.NoError(t, err)

err = r.AddNewBaseURL("../b/")
require.NoError(t, err)

require.EqualStringSlice(t, []string{"./", "../a/", "../b/"}, r.BaseURL)
}

func TestSetNewBaseURLSubtitle(t *testing.T) {
m := NewMPD(DASH_PROFILE_ONDEMAND, VALID_MEDIA_PRESENTATION_DURATION, VALID_MIN_BUFFER_TIME)
subtitleAS, _ := m.AddNewAdaptationSetSubtitleWithID("7357", DASH_MIME_TYPE_SUBTITLE_VTT, VALID_LANG)
Expand Down
4 changes: 2 additions & 2 deletions mpd/segment_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func TestSegmentListDeserialization(t *testing.T) {
if err == nil {
expected := getSegmentListMPD()

require.EqualString(t, expected.Periods[0].BaseURL, m.Periods[0].BaseURL)
require.EqualStringSlice(t, expected.Periods[0].BaseURL, m.Periods[0].BaseURL)

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

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

aas, _ := m.AddNewAdaptationSetAudioWithID("1", "audio/mp4", true, 1, "English")
ra, _ := aas.AddNewRepresentationAudio(48000, 255000, "mp4a.40.2", "audio_1")
Expand Down
4 changes: 2 additions & 2 deletions mpd/segment_timeline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestSegmentTimelineDeserialization(t *testing.T) {
m, err := ReadFromString(xml)
require.NoError(t, err)
expected := getSegmentTimelineMPD()
require.EqualString(t, expected.Periods[0].BaseURL, m.Periods[0].BaseURL)
require.EqualStringSlice(t, expected.Periods[0].BaseURL, m.Periods[0].BaseURL)

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

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

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

0 comments on commit 6153c9f

Please sign in to comment.