Skip to content

support multiple baseurl elements #92

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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