Skip to content
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
8 changes: 7 additions & 1 deletion examples/live.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/zencoder/go-dash/mpd"
)

func exampleLive() {
func main() {
m := mpd.NewMPD(mpd.DASH_PROFILE_LIVE, "PT6M16S", "PT1.97S")

audioAS, _ := m.AddNewAdaptationSetAudio(mpd.DASH_MIME_TYPE_AUDIO_MP4, true, 1, "und")
Expand All @@ -29,6 +29,12 @@ func exampleLive() {
subtitleAS, _ := m.AddNewAdaptationSetSubtitle(mpd.DASH_MIME_TYPE_SUBTITLE_VTT, "en")
subtitleRep, _ := subtitleAS.AddNewRepresentationSubtitle(256, "subtitle_en")
_ = subtitleRep.SetNewBaseURL("http://example.com/content/sintel/subtitles/subtitles_en.vtt")
schemeIDURI := "urn:mpeg:dash:utc:direct:2014"
value := "2019-10-23T15:56:29Z"
m.UTCTiming = &mpd.DescriptorType{
SchemeIDURI: &schemeIDURI,
Value: &value,
}

mpdStr, _ := m.WriteToString()
fmt.Println(mpdStr)
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
module github.com/zencoder/go-dash

go 1.13
4 changes: 2 additions & 2 deletions mpd/duration.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (d Duration) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
}

func (d *Duration) UnmarshalXMLAttr(attr xml.Attr) error {
dur, err := parseDuration(attr.Value)
dur, err := ParseDuration(attr.Value)
if err != nil {
return err
}
Expand Down Expand Up @@ -153,7 +153,7 @@ func fmtInt(buf []byte, v uint64) int {
return w
}

func parseDuration(str string) (time.Duration, error) {
func ParseDuration(str string) (time.Duration, error) {
if len(str) < 3 {
return 0, errors.New("At least one number and designator are required")
}
Expand Down
4 changes: 2 additions & 2 deletions mpd/duration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestParseDuration(t *testing.T) {
"PT1004199059S": (1004199059 * time.Second).Seconds(),
}
for ins, ex := range in {
act, err := parseDuration(ins)
act, err := ParseDuration(ins)
require.NoError(t, err, ins)
require.EqualFloat64(t, ex, act.Seconds(), ins)
}
Expand All @@ -54,7 +54,7 @@ func TestParseBadDurations(t *testing.T) {
"-P20H": `Duration cannot be negative`, // Negative duration doesn't make sense
}
for ins, msg := range in {
_, err := parseDuration(ins)
_, err := ParseDuration(ins)
require.EqualError(t, err, msg, fmt.Sprintf("Expected an error for: %s", ins))
}
}
1 change: 1 addition & 0 deletions mpd/fixtures/live_profile_dynamic.mpd
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@
</Representation>
</AdaptationSet>
</Period>
<UTCTiming></UTCTiming>
</MPD>
80 changes: 80 additions & 0 deletions mpd/fixtures/truncate.mpd
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" minBufferTime="PT2S" availabilityStartTime="2019-12-03T20:57:14Z" minimumUpdatePeriod="PT5S" publishTime="2019-12-03T21:05:05Z" timeShiftBufferDepth="PT120S">
<Period id="0">
<AdaptationSet frameRate="90000/3000" id="0" segmentAlignment="true" maxWidth="720" contentType="video">
<Representation sar="1:1" mimeType="video/mp4" bandwidth="306235" codecs="avc1.42c01e" height="480" id="0" width="720">
<SegmentTemplate initialization="video_0/init.mp4" media="video_0/media_$Number$.m4s" startNumber="1" timescale="90000">
<SegmentTimeline>
<S t="127920" d="540000" r="4"></S>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation sar="1:1" mimeType="video/mp4" bandwidth="201655" codecs="avc1.42c01e" height="360" id="2" width="640">
<SegmentTemplate initialization="video_1/init.mp4" media="video_1/media_$Number$.m4s" startNumber="1" timescale="90000">
<SegmentTimeline>
<S t="127920" d="540000" r="4"></S>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" segmentAlignment="true" contentType="audio">
<Representation mimeType="audio/mp4" audioSamplingRate="48000" bandwidth="97994" codecs="mp4a.40.2" id="1">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"></AudioChannelConfiguration>
<SegmentTemplate initialization="audio_0/init.mp4" media="audio_0/media_$Number$.m4s" startNumber="1" timescale="90000">
<SegmentTimeline>
<S t="126000" d="414720"></S>
<S t="540720" d="539520" r="3"></S>
<S t="2698800" d="128640"></S>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation mimeType="audio/mp4" audioSamplingRate="48000" bandwidth="97994" codecs="mp4a.40.2" id="3">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"></AudioChannelConfiguration>
<SegmentTemplate initialization="audio_1/init.mp4" media="audio_1/media_$Number$.m4s" startNumber="1" timescale="90000">
<SegmentTimeline>
<S t="126000" d="414720"></S>
<S t="540720" d="539520" r="3"></S>
<S t="2698800" d="128640"></S>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
</Period>
<Period id="1" start="PT31.421333333S">
<AdaptationSet frameRate="90000/3000" id="0" segmentAlignment="true" maxWidth="720" contentType="video">
<Representation sar="1:1" mimeType="video/mp4" bandwidth="311792" codecs="avc1.42c01e" height="480" id="0" width="720">
<SegmentTemplate initialization="video_0/init.mp4" media="video_0/media_$Number$.m4s" startNumber="6" timescale="90000">
<SegmentTimeline>
<S t="2827920" d="540000" r="9"></S>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation sar="1:1" mimeType="video/mp4" bandwidth="209067" codecs="avc1.42c01e" height="360" id="2" width="640">
<SegmentTemplate initialization="video_1/init.mp4" media="video_1/media_$Number$.m4s" startNumber="6" timescale="90000">
<SegmentTimeline>
<S t="2827920" d="540000" r="9"></S>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" segmentAlignment="true" contentType="audio">
<Representation mimeType="audio/mp4" audioSamplingRate="48000" bandwidth="97888" codecs="mp4a.40.2" id="1">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"></AudioChannelConfiguration>
<SegmentTemplate initialization="audio_0/init.mp4" media="audio_0/media_$Number$.m4s" startNumber="7" timescale="90000">
<SegmentTimeline>
<S t="2827440" d="540000" r="9"></S>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation mimeType="audio/mp4" audioSamplingRate="48000" bandwidth="97888" codecs="mp4a.40.2" id="3">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"></AudioChannelConfiguration>
<SegmentTemplate initialization="audio_1/init.mp4" media="audio_1/media_$Number$.m4s" startNumber="7" timescale="90000">
<SegmentTimeline>
<S t="2827440" d="540000" r="9"></S>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
</Period>
<UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-iso:2014" value="https://time.akamai.com/?iso"></UTCTiming>
</MPD>
40 changes: 40 additions & 0 deletions mpd/fixtures/truncate_short.mpd
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" minBufferTime="PT2S" availabilityStartTime="2019-12-03T20:57:14Z" minimumUpdatePeriod="PT5S" publishTime="2019-12-03T21:05:05Z" timeShiftBufferDepth="PT120S">
<Period id="1" start="PT31.421333333S">
<AdaptationSet frameRate="90000/3000" id="0" segmentAlignment="true" maxWidth="720" contentType="video">
<Representation sar="1:1" mimeType="video/mp4" bandwidth="311792" codecs="avc1.42c01e" height="480" id="0" width="720">
<SegmentTemplate initialization="video_0/init.mp4" media="video_0/media_$Number$.m4s" startNumber="6" timescale="90000">
<SegmentTimeline>
<S t="2827920" d="540000" r="9"></S>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation sar="1:1" mimeType="video/mp4" bandwidth="209067" codecs="avc1.42c01e" height="360" id="2" width="640">
<SegmentTemplate initialization="video_1/init.mp4" media="video_1/media_$Number$.m4s" startNumber="6" timescale="90000">
<SegmentTimeline>
<S t="2827920" d="540000" r="9"></S>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" segmentAlignment="true" contentType="audio">
<Representation mimeType="audio/mp4" audioSamplingRate="48000" bandwidth="97888" codecs="mp4a.40.2" id="1">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"></AudioChannelConfiguration>
<SegmentTemplate initialization="audio_0/init.mp4" media="audio_0/media_$Number$.m4s" startNumber="7" timescale="90000">
<SegmentTimeline>
<S t="2827440" d="540000" r="9"></S>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation mimeType="audio/mp4" audioSamplingRate="48000" bandwidth="97888" codecs="mp4a.40.2" id="3">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"></AudioChannelConfiguration>
<SegmentTemplate initialization="audio_1/init.mp4" media="audio_1/media_$Number$.m4s" startNumber="7" timescale="90000">
<SegmentTimeline>
<S t="2827440" d="540000" r="9"></S>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
</Period>
<UTCTiming schemeIdUri="urn:mpeg:dash:utc:http-iso:2014" value="https://time.akamai.com/?iso"></UTCTiming>
</MPD>
12 changes: 9 additions & 3 deletions mpd/mpd.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,18 @@ type MPD struct {
MinBufferTime *string `xml:"minBufferTime,attr"`
AvailabilityStartTime *string `xml:"availabilityStartTime,attr,omitempty"`
MinimumUpdatePeriod *string `xml:"minimumUpdatePeriod,attr"`
PublishTime *string `xml:"publishTime,attr"`
TimeShiftBufferDepth *string `xml:"timeShiftBufferDepth,attr"`
BaseURL string `xml:"BaseURL,omitempty"`
period *Period
Periods []*Period `xml:"Period,omitempty"`
Periods []*Period `xml:"Period,omitempty"`
UTCTiming *DescriptorType `xml:"UTCTiming,omitempty"`
}

type Period struct {
ID string `xml:"id,attr,omitempty"`
Duration Duration `xml:"duration,attr,omitempty"`
Start Duration `xml:"start,attr,omitempty"`
Start *Duration `xml:"start,attr,omitempty"`
BaseURL string `xml:"BaseURL,omitempty"`
SegmentBase *SegmentBase `xml:"SegmentBase,omitempty"`
SegmentList *SegmentList `xml:"SegmentList,omitempty"`
Expand All @@ -84,7 +87,7 @@ type Period struct {
}

type DescriptorType struct {
SchemeIDURI *string `xml:"schemeIDURI,attr"`
SchemeIDURI *string `xml:"schemeIdUri,attr"`
Value *string `xml:"value,attr"`
ID *string `xml:"id,attr"`
}
Expand Down Expand Up @@ -124,6 +127,7 @@ type AdaptationSet struct {
MaxBandwidth *string `xml:"maxBandwidth,attr"`
MinWidth *string `xml:"minWidth,attr"`
MaxWidth *string `xml:"maxWidth,attr"`
ContentType *string `xml:"contentType,attr"`
ContentProtection []ContentProtectioner `xml:"ContentProtection,omitempty"` // Common attribute, can be deprecated here
Roles []*Role `xml:"Role,omitempty"`
SegmentBase *SegmentBase `xml:"SegmentBase,omitempty"`
Expand All @@ -146,6 +150,7 @@ func (as *AdaptationSet) UnmarshalXML(d *xml.Decoder, start xml.StartElement) er
MaxBandwidth *string `xml:"maxBandwidth,attr"`
MinWidth *string `xml:"minWidth,attr"`
MaxWidth *string `xml:"maxWidth,attr"`
ContentType *string `xml:"contentType,attr"`
ContentProtection []ContentProtectioner `xml:"ContentProtection,omitempty"` // Common attribute, can be deprecated here
Roles []*Role `xml:"Role,omitempty"`
SegmentBase *SegmentBase `xml:"SegmentBase,omitempty"`
Expand Down Expand Up @@ -478,6 +483,7 @@ func NewDynamicMPD(profile DashProfile, availabilityStartTime, minBufferTime str
MinBufferTime: Strptr(minBufferTime),
period: period,
Periods: []*Period{period},
UTCTiming: &DescriptorType{},
}

for i := range attributes {
Expand Down
2 changes: 1 addition & 1 deletion mpd/mpd_read_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func Read(r io.Reader) (*MPD, error) {
// path - Output path to write the manifest to.
func (m *MPD) WriteToFile(path string) error {
// Open the file to write the XML to
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666)
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
Expand Down
46 changes: 46 additions & 0 deletions mpd/mpd_read_write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,28 @@ func TestNewDynamicMPDLiveWriteToString(t *testing.T) {
expectedXML := `<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" mediaPresentationDuration="PT6M16S" minBufferTime="PT1.97S" availabilityStartTime="1970-01-01T00:00:00Z" minimumUpdatePeriod="PT5S">
<Period></Period>
<UTCTiming></UTCTiming>
</MPD>
`
require.EqualString(t, expectedXML, xmlStr)
}

func TestNewDynamicMPDLiveWithPeriodStartWriteToString(t *testing.T) {
m := NewDynamicMPD(DASH_PROFILE_LIVE, VALID_AVAILABILITY_START_TIME, VALID_MIN_BUFFER_TIME,
AttrMediaPresentationDuration(VALID_MEDIA_PRESENTATION_DURATION),
AttrMinimumUpdatePeriod(VALID_MINIMUM_UPDATE_PERIOD))

// Set first period start time to PT0S
p := m.GetCurrentPeriod()
start := Duration(time.Duration(0))
p.Start = &start

xmlStr, err := m.WriteToString()
require.NoError(t, err)
expectedXML := `<?xml version="1.0" encoding="UTF-8"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="dynamic" mediaPresentationDuration="PT6M16S" minBufferTime="PT1.97S" availabilityStartTime="1970-01-01T00:00:00Z" minimumUpdatePeriod="PT5S">
<Period start="PT0S"></Period>
<UTCTiming></UTCTiming>
</MPD>
`
require.EqualString(t, expectedXML, xmlStr)
Expand Down Expand Up @@ -381,3 +403,27 @@ func TestWriteToFileInvalidFilePath(t *testing.T) {
err := m.WriteToFile("")
require.NotNil(t, err)
}

func TestWriteToFileTruncate(t *testing.T) {
out := "test-truncate.mpd"

m, err := ReadFromFile("fixtures/truncate.mpd")
require.NoError(t, err)

err = m.WriteToFile(out)
require.NoError(t, err)

defer os.Remove(out)

xmlStr := testfixtures.LoadFixture(out)
testfixtures.CompareFixture(t, "fixtures/truncate.mpd", xmlStr)

m, err = ReadFromFile("fixtures/truncate_short.mpd")
require.NoError(t, err)

err = m.WriteToFile(out)
require.NoError(t, err)

xmlStr = testfixtures.LoadFixture(out)
testfixtures.CompareFixture(t, "fixtures/truncate_short.mpd", xmlStr)
}
1 change: 1 addition & 0 deletions mpd/mpd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func TestNewDynamicMPDLive(t *testing.T) {
MinimumUpdatePeriod: Strptr(VALID_MINIMUM_UPDATE_PERIOD),
period: &Period{},
Periods: []*Period{{}},
UTCTiming: &DescriptorType{},
}

expectedString, err := expectedMPD.WriteToString()
Expand Down