Skip to content

Commit

Permalink
feat: DASH Ed 6 elements and new test content
Browse files Browse the repository at this point in the history
Added elements from current Ed 6 draft
Changed Duration to round to milliseconds unless time is less than millisecond
  • Loading branch information
tobbee committed Oct 22, 2024
1 parent 3c6a0a8 commit dc2cd4e
Show file tree
Hide file tree
Showing 9 changed files with 350 additions and 69 deletions.
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

0 comments on commit dc2cd4e

Please sign in to comment.