Skip to content
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

feat: DASH Ed 6 elements and new test content #32

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Duration is now printed with millisecond accuracy unless value less than one millisecond
- PatchLocationType according to Ed. 6
- Location according to Ed. 6

### Added

- Ed5Amd1 element `<SelectionInfo>` inside mixed XML type `<Event>`
- AlternativeMPD element according to Ed. 6
- ContentSteering according to Ed. 6
- ClientDataReporting according to Ed. 6
- SegmentSequenceProperties according to Ed. 6
- RunLengthType according to Ed. 6
- Pattern and PatternType according to Ed. 6
- SupVideoInfoType according to Ed. 6 xsd (what is it used for??)
- SapWithCadenceType according to Ed. 6
- Example content G.23 to G28.1, G28.2, and G.29

### Fixed

Expand Down
40 changes: 25 additions & 15 deletions mpd/duration.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mpd

import (
"math"
"regexp"
"strconv"
"strings"
Expand Down Expand Up @@ -64,33 +65,42 @@ func (d *Duration) UnmarshalXMLAttr(attr xml.Attr) error {
//
// It handles negative durations, although they should not occur.
// The highest output unit is hours (H).
// There is never more than 3 decimals to the seconds.
func (d *Duration) String() string {
// Largest time is 2540400h10m10.000000000s
// Largest time is 2540400h10m10.000s
var buf [32]byte
w := len(buf)

u := uint64(*d)
if u == 0 {
return "PT0S"
}
neg := *d < 0
if neg {
u = -u
}

if u < uint64(time.Second) {
var prec int
w--
buf[w] = 'S'
w--
if u == 0 {
return "PT0S"
}
w, u = fmtFrac(buf[:w], u, prec)
w = fmtInt(buf[:w], u)
} else {
w--
buf[w] = 'S'
s := u / uint64(time.Second)
ns := u - s*uint64(time.Second)
ms := uint64(math.Round(float64(ns) * 1.0e-6))

w, u = fmtFrac(buf[:w], u, 9)
w--
buf[w] = 'S' // End with Seconds

switch {
case s == 0 && ms == 0:
// Time smaller than ms, return higher precision
w, u = fmtFrac(buf[:w], u, 9)
w = fmtInt(buf[:w], u)
case s == 0:
// Time smaller than 1s, return ms
w, _ = fmtFrac(buf[:w], ms, 3)
w--
buf[w] = '0'
default:
// Time larger than 1s, return s and potentially ms
u = 1000*s + ms
w, u = fmtFrac(buf[:w], u, 3)
// u is now integer seconds
w = fmtInt(buf[:w], u%60)
u /= 60
Expand Down
20 changes: 20 additions & 0 deletions mpd/duration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,23 @@ func TestParseBadDurations(t *testing.T) {
require.EqualError(t, err, msg, fmt.Sprintf("Expected an error for: %s", ins))
}
}

func TestUnMarshalReMarshalDuration(t *testing.T) {
cases := []string{
"PT0.0002S",
"PT0.334S",
"PT2.002S",
"PT2S",
"PT1M",
"PT0S",
}

for _, dur := range cases {
timeDur, err := ParseDuration(dur)
require.NoError(t, err)

tDur := Duration(timeDur)
outDur := tDur.String()
require.Equal(t, dur, outDur)
}
}
183 changes: 131 additions & 52 deletions mpd/mpd.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ type MPD struct {
MaxSubsegmentDuration *Duration `xml:"maxSubsegmentDuration,attr"`
ProgramInformation []*ProgramInformationType `xml:"ProgramInformation"`
BaseURL []*BaseURLType `xml:"BaseURL"`
Location []AnyURI `xml:"Location"`
Location []*LocationType `xml:"Location"`
PatchLocation []*PatchLocationType `xml:"PatchLocation"`
ServiceDescription []*ServiceDescriptionType `xml:"ServiceDescription"`
InitializationSet []*InitializationSetType `xml:"InitializationSet"`
Expand Down Expand Up @@ -89,9 +89,16 @@ func Clone(mpd *MPD) *MPD {

// PatchLocationType is Patch Location Type.
type PatchLocationType struct {
XMLName xml.Name `xml:"PatchLocation"`
Ttl float64 `xml:"ttl,attr,omitempty"`
Value AnyURI `xml:",chardata"`
XMLName xml.Name `xml:"PatchLocation"`
ServiceLocation string `xml:"serviceLocation,attr,omitempty"`
Ttl float64 `xml:"ttl,attr,omitempty"`
Value AnyURI `xml:",chardata"`
}

// LocationType is Location Type (extension with ServiceLocation in Ed 6)
type LocationType struct {
ServiceLocation string `xml:"serviceLocation,attr,omitempty"`
Value string `xml:",chardata"`
}

type Period struct {
Expand Down Expand Up @@ -146,14 +153,15 @@ type EventStreamType struct {
// EventType is Event. This has settings mixed="true" in the schema, so it can either
// have charData or child elements or both.
type EventType struct {
XMLName xml.Name `xml:"Event"`
PresentationTime uint64 `xml:"presentationTime,attr,omitempty"` // default is 0
Duration uint64 `xml:"duration,attr,omitempty"`
Id uint32 `xml:"id,attr"`
ContentEncoding ContentEncodingType `xml:"contentEncoding,attr,omitempty"`
MessageData string `xml:"messageData,attr,omitempty"`
Value string `xml:",chardata"`
SelectionInfo *SelectionInfoType `xml:"SelectionInfo"`
XMLName xml.Name `xml:"Event"`
PresentationTime uint64 `xml:"presentationTime,attr,omitempty"` // default is 0
Duration uint64 `xml:"duration,attr,omitempty"`
Id uint32 `xml:"id,attr"`
ContentEncoding ContentEncodingType `xml:"contentEncoding,attr,omitempty"`
MessageData string `xml:"messageData,attr,omitempty"`
Value string `xml:",chardata"`
SelectionInfo *SelectionInfoType `xml:"SelectionInfo"`
AlternativeMPD *AlternativeMPDEventType `xml:"AlternativeMPD"`
}

// SelectionInfoType is SelectionInfo
Expand All @@ -170,6 +178,13 @@ type SelectionType struct {
Data string `xml:"data,attr,omitempty"`
}

// AlternativeMPDEventType is Alternative MPD event
type AlternativeMPDEventType struct {
Uri string `xml:"uri,attr"`
Mode string `xml:"mode,attr"`
EarliestResolutionTimeOffset float64 `xml:"earliestResolutionTimeOffset,attr"`
}

// InitializationSetType is Initialization Set.
type InitializationSetType struct {
XMLName xml.Name `xml:"InitializationSet"`
Expand All @@ -193,13 +208,15 @@ type InitializationSetType struct {

// ServiceDescriptionType is Service Description.
type ServiceDescriptionType struct {
XMLName xml.Name `xml:"ServiceDescription"`
Id uint32 `xml:"id,attr"`
Scopes []*DescriptorType `xml:"Scope"`
Latencies []*LatencyType `xml:"Latency"`
PlaybackRates []*PlaybackRateType `xml:"PlaybackRate"`
OperatingQualities []*OperatingQualityType `xml:"OperatingQuality"`
OperatingBandwidths []*OperatingBandwidthType `xml:"OperatingBandwidth"`
XMLName xml.Name `xml:"ServiceDescription"`
Id uint32 `xml:"id,attr"`
Scopes []*DescriptorType `xml:"Scope"`
Latencies []*LatencyType `xml:"Latency"`
PlaybackRates []*PlaybackRateType `xml:"PlaybackRate"`
OperatingQualities []*OperatingQualityType `xml:"OperatingQuality"`
OperatingBandwidths []*OperatingBandwidthType `xml:"OperatingBandwidth"`
ContentSteering []*ContentSteeringType `xml:"ContentSteering"`
ClientDataReporting []*ClientDataReportingType `xml:"ClientDataReporting"`
}

// LatencyType is Service Description Latency (Annex K.4.2.2).
Expand Down Expand Up @@ -423,37 +440,38 @@ func (s *SubRepresentationType) Parent() *RepresentationType {

// RepresentationBaseType is Representation base (common attributes and elements).
type RepresentationBaseType struct {
Profiles ListOfProfilesType `xml:"profiles,attr,omitempty"`
Width uint32 `xml:"width,attr,omitempty"`
Height uint32 `xml:"height,attr,omitempty"`
Sar RatioType `xml:"sar,attr,omitempty"`
FrameRate FrameRateType `xml:"frameRate,attr,omitempty"`
AudioSamplingRate *UIntVectorType `xml:"audioSamplingRate,attr,omitempty"`
MimeType string `xml:"mimeType,attr,omitempty"`
SegmentProfiles *ListOf4CCType `xml:"segmentProfiles,attr,omitempty"`
Codecs string `xml:"codecs,attr,omitempty"`
ContainerProfiles *ListOf4CCType `xml:"containerProfiles,attr,omitempty"`
MaximumSAPPeriod float64 `xml:"maximumSAPPeriod,attr,omitempty"`
StartWithSAP uint32 `xml:"startWithSAP,attr,omitempty"`
MaxPlayoutRate float64 `xml:"maxPlayoutRate,attr,omitempty"`
CodingDependency *bool `xml:"codingDependency,attr,omitempty"`
ScanType VideoScanType `xml:"scanType,attr,omitempty"`
SelectionPriority *uint32 `xml:"selectionPriority,attr"` // default = 1
Tag string `xml:"tag,attr,omitempty"`
FramePackings []*DescriptorType `xml:"FramePacking"`
AudioChannelConfigurations []*DescriptorType `xml:"AudioChannelConfiguration"`
ContentProtections []*ContentProtectionType `xml:"ContentProtection"`
OutputProtection *DescriptorType `xml:"OutputProtection"`
EssentialProperties []*DescriptorType `xml:"EssentialProperty"`
SupplementalProperties []*DescriptorType `xml:"SupplementalProperty"`
InbandEventStreams []*EventStreamType `xml:"InbandEventStream"`
Switchings []*SwitchingType `xml:"Switching"`
RandomAccesses []*RandomAccessType `xml:"RandomAccess"`
GroupLabels []*LabelType `xml:"GroupLabel"`
Labels []*LabelType `xml:"Label"`
ProducerReferenceTimes []*ProducerReferenceTimeType `xml:"ProducerReferenceTime"`
ContentPopularityRates []*ContentPopularityRateType `xml:"ContentPopularityRate"`
Resyncs []*ResyncType `xml:"Resync"`
Profiles ListOfProfilesType `xml:"profiles,attr,omitempty"`
Width uint32 `xml:"width,attr,omitempty"`
Height uint32 `xml:"height,attr,omitempty"`
Sar RatioType `xml:"sar,attr,omitempty"`
FrameRate FrameRateType `xml:"frameRate,attr,omitempty"`
AudioSamplingRate *UIntVectorType `xml:"audioSamplingRate,attr,omitempty"`
MimeType string `xml:"mimeType,attr,omitempty"`
SegmentProfiles *ListOf4CCType `xml:"segmentProfiles,attr,omitempty"`
Codecs string `xml:"codecs,attr,omitempty"`
ContainerProfiles *ListOf4CCType `xml:"containerProfiles,attr,omitempty"`
MaximumSAPPeriod float64 `xml:"maximumSAPPeriod,attr,omitempty"`
StartWithSAP uint32 `xml:"startWithSAP,attr,omitempty"`
MaxPlayoutRate float64 `xml:"maxPlayoutRate,attr,omitempty"`
CodingDependency *bool `xml:"codingDependency,attr,omitempty"`
ScanType VideoScanType `xml:"scanType,attr,omitempty"`
SelectionPriority *uint32 `xml:"selectionPriority,attr"` // default = 1
Tag string `xml:"tag,attr,omitempty"`
FramePackings []*DescriptorType `xml:"FramePacking"`
AudioChannelConfigurations []*DescriptorType `xml:"AudioChannelConfiguration"`
ContentProtections []*ContentProtectionType `xml:"ContentProtection"`
OutputProtection *DescriptorType `xml:"OutputProtection"`
EssentialProperties []*DescriptorType `xml:"EssentialProperty"`
SupplementalProperties []*DescriptorType `xml:"SupplementalProperty"`
InbandEventStreams []*EventStreamType `xml:"InbandEventStream"`
Switchings []*SwitchingType `xml:"Switching"`
RandomAccesses []*RandomAccessType `xml:"RandomAccess"`
GroupLabels []*LabelType `xml:"GroupLabel"`
Labels []*LabelType `xml:"Label"`
ProducerReferenceTimes []*ProducerReferenceTimeType `xml:"ProducerReferenceTime"`
ContentPopularityRates []*ContentPopularityRateType `xml:"ContentPopularityRate"`
Resyncs []*ResyncType `xml:"Resync"`
SegmentSequenceProperties *SegmentSequencePropertiesType `xml:"SegmentSequenceProperties"`
}

func (r *RepresentationType) GetSegmentTemplate() *SegmentTemplateType {
Expand Down Expand Up @@ -756,6 +774,20 @@ type SegmentTemplateType struct {
MultipleSegmentBaseType
}

// RunLengthType is Run-length coded sequence of segments or segment
// sequences
type RunLengthType struct {
D uint64 `xml:"d,attr"`
R uint64 `xml:"r,attr,omitempty"`
K uint64 `xml:"k,attr,omitempty"`
}

// PatternType is Duration pattern
type PatternType struct {
Id uint64 `xml:"id,attr"`
P []*RunLengthType `xml:"P"`
}

// S is the S element of SegmentTimeline. All time units in media timescale.
// Defined in ISO/IEC 23009-1 Section 5.3.9.6
type S struct {
Expand All @@ -768,12 +800,15 @@ type S struct {
// R is repeat count (how many times to repeat. -1 is unlimited)
R int `xml:"r,attr,omitempty"` // default = 0
// K is the number of Segments that are included in a Segment Sequence.
K *uint64 `xml:"k,attr"` // default = 1
K *uint64 `xml:"k,attr"` // default = 1
P uint64 `xml:"p,attr,omitempty"` // Ed6
PE uint64 `xml:"pE,attr,omitempty"` // Ed6
}

// SegmentTimelineType is Segment Timeline.
type SegmentTimelineType struct {
S []*S `xml:"S"`
Pattern []*PatternType `xml:"Pattern"` // Ed6
S []*S `xml:"S"`
}

// BaseURLType is Base URL.
Expand Down Expand Up @@ -843,6 +878,50 @@ type LeapSecondInformationType struct {
NextLeapChangeTime DateTime `xml:"nextLeapChangeTime,attr,omitempty"`
}

// StringNoWhitespaceVectorType is Whitespace-separated list of no-whitespace strings
type StringNoWhitespaceVectorType []string

// ContentSteeringType ...
type ContentSteeringType struct {
DefaultServiceLocation string `xml:"defaultServiceLocation,attr,omitempty"`
QueryBeforeStart bool `xml:"queryBeforeStart,attr,omitempty"`
ClientRequirement bool `xml:"clientRequirement,attr,omitempty"`
Value string `xml:",chardata"`
}

// ClientDataReportingType is Client Data Reporting
type ClientDataReportingType struct {
ServiceLocations *StringVectorType `xml:"serviceLocations,attr,omitempty"`
AdaptationSets *UIntVectorType `xml:"adaptationSets,attr,omitempty"`
ReportingSystem []*DescriptorType `xml:"ReportingSystem"`
}

// CMCDParameterType is CMCD Parameters
type CMCDParameterType struct {
Version uint32 `xml:"version,attr,omitempty"`
Mode string `xml:"mode,attr,omitempty"`
IncludeInRequests *StringNoWhitespaceVectorType `xml:"includeInRequests,attr,omitempty"`
Keys *StringNoWhitespaceVectorType `xml:"keys,attr,omitempty"`
ContentID string `xml:"contentID,attr,omitempty"`
SessionID string `xml:"sessionID,attr,omitempty"`
}

// SupVideoInfoType is Picture-in-picture information
type SupVideoInfoType struct {
ProcessingInfo string `xml:"processingInfo,attr,omitempty"`
}

// SegmentSequencePropertiesType is Segment Sequence properties
type SegmentSequencePropertiesType struct {
SAP []*SapWithCadenceType `xml:"SAP"`
}

// SapWithCadenceType is Segment Sequence SAP properties
type SapWithCadenceType struct {
Type uint32 `xml:"type,attr"`
Cadence uint32 `xml:"cadence,attr,omitempty"`
}

// ListOfProfilesType is comma-separated list of profiles.
type ListOfProfilesType string

Expand Down
2 changes: 1 addition & 1 deletion mpd/testdata/go-dash-fixtures/truncate.mpd
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
</Representation>
</AdaptationSet>
</Period>
<Period id="1" start="PT31.421333333S">
<Period id="1" start="PT31.421S">
<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">
Expand Down
2 changes: 1 addition & 1 deletion mpd/testdata/go-dash-fixtures/truncate_short.mpd
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?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="PT2M">
<Period id="1" start="PT31.421333333S">
<Period id="1" start="PT31.421S">
<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">
Expand Down
Loading
Loading