Skip to content

Commit

Permalink
Merge pull request #23 from Eyevinn/feat/drm-namespaces
Browse files Browse the repository at this point in the history
Support for Marlin and DASH-IF ClearKey content protection
  • Loading branch information
tobbee authored Jan 4, 2024
2 parents b2fbd62 + 0b01f75 commit 84fa8d7
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 67 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- GetContentProtections, GetMimeType, GetCodecs, and GetSegmentTemplate methods for AdaptationSet and Representation
- ContentProtection elements and name spaces for Marlin DRM and DASH-IF ClearKey

### Fixed

- ContentProtection and other parts of RepresentationBaseType moved before other elements in AdaptationSet and Representation

## [0.10.0] - 2023-05-26

Expand Down
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ needed. The main modifications made were:
value `false` or be absent
* Add type comments and document enum values for certain types
* Change names to plural for all subelement slices, e.g. Periods instead of Period
* Add `cenc:pssh` and `mspr:pro` DRM elements
* Add ContentProtection elements and corresponding name spaces

## XML handling

Expand Down Expand Up @@ -74,13 +74,16 @@ and how the output looks:
comes from the XML schema. The order is therefore often different from the input document.
4. Mapping to float numbers may not preserve the exact value of the input.
5. Addition of extra name spaces, such as specific DRM systems must be done explicitly.
6. Durations are mapped to nanoseconds and back. This may change the duration slightly. All trailing zeros
6. In the output, the name-spaces are added to the level where they are used and not at the top MPD level.
The output names are also fixed, and may differ from the input names.
7. Durations are mapped to nanoseconds and back. This may change the duration slightly. All trailing zeros
are also removed, as are the minutes and seconds counts if they are zero and a bigger unit is present.

## Tests

The MPD marshaling/unmarshaling is tested by by using many MPDs in the `testdata/schema-mpds` and
`testdata/go-dash-fixtures` directories. See the README.md files in these directorie for their
The MPD marshaling/unmarshaling is tested by by using many MPDs in the `testdata/schema-mpds`,
`testdata/go-dash-fixtures`, and `testdata/livesim` directories.
See the README.md files in these directories for their
origins and the small tweaks needed to make durations and some other values consistent after a
unmarshaling/marshaling process.

Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ go 1.19
require (
aqwari.net/xml v0.0.0-20210331023308-d9421b293817
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df
github.com/google/go-cmp v0.5.9
github.com/stretchr/testify v1.8.2
github.com/google/go-cmp v0.6.0
github.com/stretchr/testify v1.8.4
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
Expand All @@ -21,6 +23,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
Expand All @@ -32,6 +36,8 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand All @@ -40,6 +46,8 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200821192610-3366bbee4705/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
145 changes: 90 additions & 55 deletions mpd/mpd.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ const (
MIME_TYPE_TTML = "application/ttml+xml"
)

const (
DRM_CLEAR_KEY_DASHIF = "urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e"
DRM_PLAYREADY = "urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95"
DRM_WIDEVINE = "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"
DRM_MARLIN = "urn:uuid:5e629af5-38dA-4063-8977-97ffbd9902d4"
)

// MPD is MPEG-DASH Media Presentation Description (MPD) as defined in ISO/IEC 23009-1 5'th edition.
//
// The tree of structs is generated from the corresponding XML Schema at https://github.com/MPEGGroup/DASHSchema
Expand Down Expand Up @@ -234,41 +241,41 @@ type UIntVWithIDType struct {
// AdaptationSetType is AdaptationSet or EmptyAdaptationSet.
// Note that XMLName is not set, since the same structure is used also for EmptyAdaptationSet.
type AdaptationSetType struct {
XlinkHref string `xml:"xlink:href,attr,omitempty"`
XlinkActuate string `xml:"xlink:actuate,attr,omitempty"` // default is "onRequest"
XlinkType string `xml:"xlink:type,attr,omitempty"` // fixed "simple"
XlinkShow string `xml:"xlink:show,attr,omitempty"` // fixed "embed"
Id *uint32 `xml:"id,attr"`
Group uint32 `xml:"group,attr,omitempty"`
Lang string `xml:"lang,attr,omitempty"`
ContentType RFC6838ContentTypeType `xml:"contentType,attr,omitempty"`
Par RatioType `xml:"par,attr,omitempty"`
MinBandwidth uint32 `xml:"minBandwidth,attr,omitempty"`
MaxBandwidth uint32 `xml:"maxBandwidth,attr,omitempty"`
MinWidth uint32 `xml:"minWidth,attr,omitempty"`
MaxWidth uint32 `xml:"maxWidth,attr,omitempty"`
MinHeight uint32 `xml:"minHeight,attr,omitempty"`
MaxHeight uint32 `xml:"maxHeight,attr,omitempty"`
MinFrameRate string `xml:"minFrameRate,attr,omitempty"`
MaxFrameRate string `xml:"maxFrameRate,attr,omitempty"`
SegmentAlignment bool `xml:"segmentAlignment,attr,omitempty"` // default = false
SubsegmentAlignment bool `xml:"subsegmentAlignment,attr,omitempty"` // default = false
SubsegmentStartsWithSAP uint32 `xml:"subsegmentStartsWithSAP,attr,omitempty"` // default = 0
BitstreamSwitching *bool `xml:"bitstreamSwitching,attr"`
InitializationSetRef *UIntVectorType `xml:"initializationSetRef,attr,omitempty"`
InitializationPrincipal AnyURI `xml:"initializationPrincipal,attr,omitempty"`
Accessibilities []*DescriptorType `xml:"Accessibility"`
Roles []*DescriptorType `xml:"Role"`
Ratings []*DescriptorType `xml:"Rating"`
Viewpoints []*DescriptorType `xml:"Viewpoint"`
ContentComponents []*ContentComponentType `xml:"ContentComponent"`
BaseURLs []*BaseURLType `xml:"BaseURL"`
SegmentBase *SegmentBaseType `xml:"SegmentBase"`
SegmentList *SegmentListType `xml:"SegmentList"`
SegmentTemplate *SegmentTemplateType `xml:"SegmentTemplate"`
Representations []*RepresentationType `xml:"Representation"`
parent *Period `xml:"-"`
XlinkHref string `xml:"xlink:href,attr,omitempty"`
XlinkActuate string `xml:"xlink:actuate,attr,omitempty"` // default is "onRequest"
XlinkType string `xml:"xlink:type,attr,omitempty"` // fixed "simple"
XlinkShow string `xml:"xlink:show,attr,omitempty"` // fixed "embed"
Id *uint32 `xml:"id,attr"`
Group uint32 `xml:"group,attr,omitempty"`
Lang string `xml:"lang,attr,omitempty"`
ContentType RFC6838ContentTypeType `xml:"contentType,attr,omitempty"`
Par RatioType `xml:"par,attr,omitempty"`
MinBandwidth uint32 `xml:"minBandwidth,attr,omitempty"`
MaxBandwidth uint32 `xml:"maxBandwidth,attr,omitempty"`
MinWidth uint32 `xml:"minWidth,attr,omitempty"`
MaxWidth uint32 `xml:"maxWidth,attr,omitempty"`
MinHeight uint32 `xml:"minHeight,attr,omitempty"`
MaxHeight uint32 `xml:"maxHeight,attr,omitempty"`
MinFrameRate string `xml:"minFrameRate,attr,omitempty"`
MaxFrameRate string `xml:"maxFrameRate,attr,omitempty"`
SegmentAlignment bool `xml:"segmentAlignment,attr,omitempty"` // default = false
SubsegmentAlignment bool `xml:"subsegmentAlignment,attr,omitempty"` // default = false
SubsegmentStartsWithSAP uint32 `xml:"subsegmentStartsWithSAP,attr,omitempty"` // default = 0
BitstreamSwitching *bool `xml:"bitstreamSwitching,attr"`
InitializationSetRef *UIntVectorType `xml:"initializationSetRef,attr,omitempty"`
InitializationPrincipal AnyURI `xml:"initializationPrincipal,attr,omitempty"`
RepresentationBaseType
Accessibilities []*DescriptorType `xml:"Accessibility"`
Roles []*DescriptorType `xml:"Role"`
Ratings []*DescriptorType `xml:"Rating"`
Viewpoints []*DescriptorType `xml:"Viewpoint"`
ContentComponents []*ContentComponentType `xml:"ContentComponent"`
BaseURLs []*BaseURLType `xml:"BaseURL"`
SegmentBase *SegmentBaseType `xml:"SegmentBase"`
SegmentList *SegmentListType `xml:"SegmentList"`
SegmentTemplate *SegmentTemplateType `xml:"SegmentTemplate"`
Representations []*RepresentationType `xml:"Representation"`
parent *Period `xml:"-"`
}

func (a *AdaptationSetType) SetParent(p *Period) {
Expand Down Expand Up @@ -322,22 +329,23 @@ type ContentComponentType struct {

// RepresentationType is Representation.
type RepresentationType struct {
XMLName xml.Name `xml:"Representation"`
Id string `xml:"id,attr"`
Bandwidth uint32 `xml:"bandwidth,attr"`
QualityRanking *uint32 `xml:"qualityRanking,attr,omitempty"`
DependencyId *StringVectorType `xml:"dependencyId,attr,omitempty"`
AssociationId *StringVectorType `xml:"associationId,attr,omitempty"`
AssociationType *ListOf4CCType `xml:"associationType,attr,omitempty"`
MediaStreamStructureId *StringVectorType `xml:"mediaStreamStructureId,attr,omitempty"`
BaseURLs []*BaseURLType `xml:"BaseURL"`
ExtendedBandwidths []*ExtendedBandwidthType `xml:"ExtendedBandwidth"`
SubRepresentations []*SubRepresentationType `xml:"SubRepresentation"`
SegmentBase *SegmentBaseType `xml:"SegmentBase"`
SegmentList *SegmentListType `xml:"SegmentList"`
SegmentTemplate *SegmentTemplateType `xml:"SegmentTemplate"`
parent *AdaptationSetType `xml:"-"` // adaptation set
XMLName xml.Name `xml:"Representation"`
Id string `xml:"id,attr"`
Bandwidth uint32 `xml:"bandwidth,attr"`
QualityRanking *uint32 `xml:"qualityRanking,attr,omitempty"`
DependencyId *StringVectorType `xml:"dependencyId,attr,omitempty"`
AssociationId *StringVectorType `xml:"associationId,attr,omitempty"`
AssociationType *ListOf4CCType `xml:"associationType,attr,omitempty"`
MediaStreamStructureId *StringVectorType `xml:"mediaStreamStructureId,attr,omitempty"`
RepresentationBaseType
BaseURLs []*BaseURLType `xml:"BaseURL"`
ExtendedBandwidths []*ExtendedBandwidthType `xml:"ExtendedBandwidth"`
SubRepresentations []*SubRepresentationType `xml:"SubRepresentation"`
SegmentBase *SegmentBaseType `xml:"SegmentBase"`
SegmentList *SegmentListType `xml:"SegmentList"`
SegmentTemplate *SegmentTemplateType `xml:"SegmentTemplate"`
parent *AdaptationSetType `xml:"-"` // adaptation set

}

func (r *RepresentationType) SetParent(p *AdaptationSetType) {
Expand Down Expand Up @@ -379,13 +387,13 @@ type ModelPairType struct {

// SubRepresentationType is SubRepresentation
type SubRepresentationType struct {
XMLName xml.Name `xml:"SubRepresentation"`
Level *uint32 `xml:"level,attr,omitempty"`
DependencyLevel *UIntVectorType `xml:"dependencyLevel,attr,omitempty"`
Bandwidth uint32 `xml:"bandwidth,attr,omitempty"`
ContentComponent *StringVectorType `xml:"contentComponent,attr,omitempty"`
parent *RepresentationType `xml:"-"`
XMLName xml.Name `xml:"SubRepresentation"`
Level *uint32 `xml:"level,attr,omitempty"`
DependencyLevel *UIntVectorType `xml:"dependencyLevel,attr,omitempty"`
Bandwidth uint32 `xml:"bandwidth,attr,omitempty"`
ContentComponent *StringVectorType `xml:"contentComponent,attr,omitempty"`
RepresentationBaseType
parent *RepresentationType `xml:"-"`
}

func (s *SubRepresentationType) SetParent(p *RepresentationType) {
Expand Down Expand Up @@ -510,6 +518,12 @@ type ContentProtectionType struct {
Pssh *PsshType `xml:"urn:mpeg:cenc:2013 cenc:pssh,omitempty"`
// MSPro is Microsoft PlayReady provisioning data with namespace "urn:microsoft:playready and "prefix "mspr".
MSPro *MSProType `xml:"urn:microsoft:playready mspr:pro,omitempty"`
// ClearKey is DASH-IF clear key
ClearKey *ClearKeyType `xml:"http://dashif.org/guidelines/clearKey ck:Laurl,omitempty"`
// LaURL is DASH-IF License Acquisition URL.
LaURL *LaURL `xml:"https://dashif.org/ dashif:laurl,omitempty"`
// MarlinContentIds is Marlin Content Ids containing one or more MarlineContentId elements.
MarlinContentIds *MarlinContentIds `xml:"urn:marlin:mas:1-0:services:schemas:mpd mas:MarlinContentIds,omitempty"`
DescriptorType
}

Expand All @@ -523,6 +537,27 @@ type MSProType struct {
Value string `xml:",chardata"`
}

// ClearKeyType is DASH-IF clear key type
type ClearKeyType struct {
LicType string `xml:"Lic_type,attr"`
Value string `xml:",chardata"`
}

// LaURL is License Acquisition URL.
type LaURL struct {
Value string `xml:",chardata"`
}

// MarlinContentIds is Marlin Content Ids containing one or more MarlineContentId elements.
type MarlinContentIds struct {
Cids []*MarlinContentId `xml:"urn:marlin:mas:1-0:services:schemas:mpd mas:MarlinContentId"`
}

// MarlinContentId is Marlin Content Id.
type MarlinContentId struct {
Value string `xml:",chardata"`
}

// ResyncType is Resynchronization Point.
type ResyncType struct {
XMLName xml.Name `xml:"Resync"`
Expand Down
14 changes: 10 additions & 4 deletions mpd/mpd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import (
"github.com/stretchr/testify/require"
)

const (
printProcessMPDFile = "" // Used to see the output of the process even if there is no essential diff
)

func TestDecodeEncodeMPDs(t *testing.T) {
testDirs := []string{"testdata/go-dash-fixtures", "testdata/schema-mpds", "testdata/livesim"}
for _, testDir := range testDirs {
Expand All @@ -36,6 +40,10 @@ func TestDecodeEncodeMPDs(t *testing.T) {
inTree, err := xmltree.Parse(td)
require.NoError(t, err)
outTree, err := xmltree.Parse(out)
if fName == printProcessMPDFile {
err := os.WriteFile(fName, out, 0644)
require.NoError(t, err)
}
require.NoError(t, err)
if !xmltree.Equal(inTree, outTree) {
inBuf := bytes.Buffer{}
Expand All @@ -47,11 +55,9 @@ func TestDecodeEncodeMPDs(t *testing.T) {
d := cmp.Diff(inBuf.String(), outBuf.String())
// Note. There is no canonicalization and there may
// be comments in the input, so the diff is not minimal.
t.Errorf("non-minimal diff for mpd %s:\n%s\n", fName, d[:400])
ofh, err := os.Create(fName)
t.Errorf("non-minimal diff for mpd %s:\n%s. Writing file %s\n", fName, d[:400], fName)
err = os.WriteFile(fName, out, 0644)
require.NoError(t, err)
_, _ = ofh.Write(out)
ofh.Close()
}
}
}
Expand Down
Loading

0 comments on commit 84fa8d7

Please sign in to comment.