Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 2358130

Browse files
zsiecflavioribeiro
authored andcommitted
mpd: support for Accessibility element in AdaptationSet (#69)
* return element name unrecognized in adaptation set * extend elements in adaptation set to cover accessibility elements * testing: add tests for the Accessibility element * testing: assert accessibility value in test Co-authored-by: Flavio Ribeiro <email@flavioribeiro.com>
1 parent 34ac3a6 commit 2358130

File tree

7 files changed

+148
-35
lines changed

7 files changed

+148
-35
lines changed

‎mpd/fixtures/hbbtv_profile.mpd‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<Representation audioSamplingRate="44100" bandwidth="67095" codecs="mp4a.40.2" id="800">
1616
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"></AudioChannelConfiguration>
1717
</Representation>
18+
<Accessibility schemeIdUri="urn:tva:metadata:cs:AudioPurposeCS:2007" value="1"></Accessibility>
1819
</AdaptationSet>
1920
<AdaptationSet mimeType="video/mp4" startWithSAP="1" scanType="progressive" id="7357" segmentAlignment="true">
2021
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" xmlns:cenc="urn:mpeg:cenc:2013" cenc:default_KID="08e36702-8f33-436c-a5dd-60ffe5571e60" value="cenc"></ContentProtection>

‎mpd/fixtures/live_profile.mpd‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role>
1414
<SegmentTemplate duration="1968" initialization="$RepresentationID$/audio/en/init.mp4" media="$RepresentationID$/audio/en/seg-$Number$.m4f" startNumber="0" timescale="1000"></SegmentTemplate>
1515
<Representation audioSamplingRate="44100" bandwidth="67095" codecs="mp4a.40.2" id="800"></Representation>
16+
<Accessibility schemeIdUri="urn:tva:metadata:cs:AudioPurposeCS:2007" value="1"></Accessibility>
1617
</AdaptationSet>
1718
<AdaptationSet mimeType="video/mp4" startWithSAP="1" scanType="progressive" id="7357" segmentAlignment="true">
1819
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" xmlns:cenc="urn:mpeg:cenc:2013" cenc:default_KID="08e36702-8f33-436c-a5dd-60ffe5571e60" value="cenc"></ContentProtection>

‎mpd/fixtures/live_profile_dynamic.mpd‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role>
1414
<SegmentTemplate duration="1968" initialization="$RepresentationID$/audio/en/init.mp4" media="$RepresentationID$/audio/en/seg-$Number$.m4f" startNumber="0" timescale="1000"></SegmentTemplate>
1515
<Representation audioSamplingRate="44100" bandwidth="67095" codecs="mp4a.40.2" id="800"></Representation>
16+
<Accessibility schemeIdUri="urn:tva:metadata:cs:AudioPurposeCS:2007" value="1"></Accessibility>
1617
</AdaptationSet>
1718
<AdaptationSet mimeType="video/mp4" startWithSAP="1" scanType="progressive" id="7357" segmentAlignment="true">
1819
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" xmlns:cenc="urn:mpeg:cenc:2013" cenc:default_KID="08e36702-8f33-436c-a5dd-60ffe5571e60" value="cenc"></ContentProtection>

‎mpd/fixtures/ondemand_profile.mpd‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<Initialization range="0-628"></Initialization>
1717
</SegmentBase>
1818
</Representation>
19+
<Accessibility schemeIdUri="urn:tva:metadata:cs:AudioPurposeCS:2007" value="1"></Accessibility>
1920
</AdaptationSet>
2021
<AdaptationSet mimeType="video/mp4" startWithSAP="1" scanType="progressive" id="7357" segmentAlignment="true">
2122
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" xmlns:cenc="urn:mpeg:cenc:2013" cenc:default_KID="08e36702-8f33-436c-a5dd-60ffe5571e60" value="cenc"></ContentProtection>

‎mpd/mpd.go‎

Lines changed: 84 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/hex"
66
"encoding/xml"
77
"errors"
8+
"fmt"
89
"strings"
910
"time"
1011

@@ -33,6 +34,12 @@ const (
3334
AUDIO_CHANNEL_CONFIGURATION_MPEG_DOLBY AudioChannelConfigurationScheme = "tag:dolby.com,2014:dash:audio_channel_configuration:2011"
3435
)
3536

37+
// AccessibilityElementScheme is the scheme definition for an Accessibility element
38+
type AccessibilityElementScheme string
39+
40+
// Accessibility descriptor values for Audio Description
41+
const ACCESSIBILITY_ELEMENT_SCHEME_DESCRIPTIVE_AUDIO AccessibilityElementScheme = "urn:tva:metadata:cs:AudioPurposeCS:2007"
42+
3643
// Constants for some known MIME types, this is a limited list and others can be used.
3744
const (
3845
DASH_MIME_TYPE_VIDEO_MP4 string = "video/mp4"
@@ -50,6 +57,7 @@ var (
5057
ErrSegmentTemplateLiveProfileOnly = errors.New("Segment template can only be used with Live Profile")
5158
ErrSegmentTemplateNil = errors.New("Segment Template nil ")
5259
ErrRepresentationNil = errors.New("Representation nil")
60+
ErrAccessibilityNil = errors.New("Accessibility nil")
5361
ErrBaseURLEmpty = errors.New("Base URL empty")
5462
ErrSegmentBaseOnDemandProfileOnly = errors.New("Segment Base can only be used with On-Demand Profile")
5563
ErrSegmentBaseNil = errors.New("Segment Base nil")
@@ -117,46 +125,48 @@ type CommonAttributesAndElements struct {
117125

118126
type AdaptationSet struct {
119127
CommonAttributesAndElements
120-
XMLName xml.Name `xml:"AdaptationSet"`
121-
ID *string `xml:"id,attr"`
122-
SegmentAlignment *bool `xml:"segmentAlignment,attr"`
123-
Lang *string `xml:"lang,attr"`
124-
Group *string `xml:"group,attr"`
125-
PAR *string `xml:"par,attr"`
126-
MinBandwidth *string `xml:"minBandwidth,attr"`
127-
MaxBandwidth *string `xml:"maxBandwidth,attr"`
128-
MinWidth *string `xml:"minWidth,attr"`
129-
MaxWidth *string `xml:"maxWidth,attr"`
130-
ContentType *string `xml:"contentType,attr"`
131-
ContentProtection []ContentProtectioner `xml:"ContentProtection,omitempty"` // Common attribute, can be deprecated here
132-
Roles []*Role `xml:"Role,omitempty"`
133-
SegmentBase *SegmentBase `xml:"SegmentBase,omitempty"`
134-
SegmentList *SegmentList `xml:"SegmentList,omitempty"`
135-
SegmentTemplate *SegmentTemplate `xml:"SegmentTemplate,omitempty"` // Live Profile Only
136-
Representations []*Representation `xml:"Representation,omitempty"`
128+
XMLName xml.Name `xml:"AdaptationSet"`
129+
ID *string `xml:"id,attr"`
130+
SegmentAlignment *bool `xml:"segmentAlignment,attr"`
131+
Lang *string `xml:"lang,attr"`
132+
Group *string `xml:"group,attr"`
133+
PAR *string `xml:"par,attr"`
134+
MinBandwidth *string `xml:"minBandwidth,attr"`
135+
MaxBandwidth *string `xml:"maxBandwidth,attr"`
136+
MinWidth *string `xml:"minWidth,attr"`
137+
MaxWidth *string `xml:"maxWidth,attr"`
138+
ContentType *string `xml:"contentType,attr"`
139+
ContentProtection []ContentProtectioner `xml:"ContentProtection,omitempty"` // Common attribute, can be deprecated here
140+
Roles []*Role `xml:"Role,omitempty"`
141+
SegmentBase *SegmentBase `xml:"SegmentBase,omitempty"`
142+
SegmentList *SegmentList `xml:"SegmentList,omitempty"`
143+
SegmentTemplate *SegmentTemplate `xml:"SegmentTemplate,omitempty"` // Live Profile Only
144+
Representations []*Representation `xml:"Representation,omitempty"`
145+
AccessibilityElems []*Accessibility `xml:"Accessibility,omitempty"`
137146
}
138147

139148
func (as *AdaptationSet) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
140149

141150
adaptationSet := struct {
142151
CommonAttributesAndElements
143-
XMLName xml.Name `xml:"AdaptationSet"`
144-
ID *string `xml:"id,attr"`
145-
SegmentAlignment *bool `xml:"segmentAlignment,attr"`
146-
Lang *string `xml:"lang,attr"`
147-
Group *string `xml:"group,attr"`
148-
PAR *string `xml:"par,attr"`
149-
MinBandwidth *string `xml:"minBandwidth,attr"`
150-
MaxBandwidth *string `xml:"maxBandwidth,attr"`
151-
MinWidth *string `xml:"minWidth,attr"`
152-
MaxWidth *string `xml:"maxWidth,attr"`
153-
ContentType *string `xml:"contentType,attr"`
154-
ContentProtection []ContentProtectioner `xml:"ContentProtection,omitempty"` // Common attribute, can be deprecated here
155-
Roles []*Role `xml:"Role,omitempty"`
156-
SegmentBase *SegmentBase `xml:"SegmentBase,omitempty"`
157-
SegmentList *SegmentList `xml:"SegmentList,omitempty"`
158-
SegmentTemplate *SegmentTemplate `xml:"SegmentTemplate,omitempty"` // Live Profile Only
159-
Representations []*Representation `xml:"Representation,omitempty"`
152+
XMLName xml.Name `xml:"AdaptationSet"`
153+
ID *string `xml:"id,attr"`
154+
SegmentAlignment *bool `xml:"segmentAlignment,attr"`
155+
Lang *string `xml:"lang,attr"`
156+
Group *string `xml:"group,attr"`
157+
PAR *string `xml:"par,attr"`
158+
MinBandwidth *string `xml:"minBandwidth,attr"`
159+
MaxBandwidth *string `xml:"maxBandwidth,attr"`
160+
MinWidth *string `xml:"minWidth,attr"`
161+
MaxWidth *string `xml:"maxWidth,attr"`
162+
ContentType *string `xml:"contentType,attr"`
163+
ContentProtection []ContentProtectioner `xml:"ContentProtection,omitempty"` // Common attribute, can be deprecated here
164+
Roles []*Role `xml:"Role,omitempty"`
165+
SegmentBase *SegmentBase `xml:"SegmentBase,omitempty"`
166+
SegmentList *SegmentList `xml:"SegmentList,omitempty"`
167+
SegmentTemplate *SegmentTemplate `xml:"SegmentTemplate,omitempty"` // Live Profile Only
168+
Representations []*Representation `xml:"Representation,omitempty"`
169+
AccessibilityElems []*Accessibility `xml:"Accessibility,omitempty"`
160170
}{}
161171

162172
var (
@@ -240,8 +250,14 @@ func (as *AdaptationSet) UnmarshalXML(d *xml.Decoder, start xml.StartElement) er
240250
return err
241251
}
242252
representations = append(representations, rp)
253+
case "Accessibility":
254+
ac := new(Accessibility)
255+
err = d.DecodeElement(ac, &tt)
256+
if err != nil {
257+
return err
258+
}
243259
default:
244-
return errors.New("Unrecognized element in AdaptationSet")
260+
return fmt.Errorf("unrecognized element in AdaptationSet %q", tt.Name.Local)
245261
}
246262
case xml.EndElement:
247263
if tt == start.End() {
@@ -435,6 +451,12 @@ type Representation struct {
435451
SegmentTemplate *SegmentTemplate `xml:"SegmentTemplate,omitempty"`
436452
}
437453

454+
type Accessibility struct {
455+
AdaptationSet *AdaptationSet `xml:"-"`
456+
SchemeIdUri *string `xml:"schemeIdUri,attr,omitempty"`
457+
Value *string `xml:"value,attr,omitempty"`
458+
}
459+
438460
type AudioChannelConfiguration struct {
439461
SchemeIDURI *string `xml:"schemeIdUri,attr"`
440462
// Value will be an int for non-Dolby Schemes, and a hexstring for Dolby Schemes, hence we make it a string
@@ -1019,6 +1041,16 @@ func (as *AdaptationSet) addRepresentation(r *Representation) error {
10191041
return nil
10201042
}
10211043

1044+
// Internal helper method for adding an Accessibility element to an AdaptationSet.
1045+
func (as *AdaptationSet) addAccessibility(a *Accessibility) error {
1046+
if a == nil {
1047+
return ErrAccessibilityNil
1048+
}
1049+
a.AdaptationSet = as
1050+
as.AccessibilityElems = append(as.AccessibilityElems, a)
1051+
return nil
1052+
}
1053+
10221054
// Adds a new Role to an AdaptationSet
10231055
// schemeIdUri - Scheme ID URI string (i.e. urn:mpeg:dash:role:2011)
10241056
// value - Value for this role, (i.e. caption, subtitle, main, alternate, supplementary, commentary, dub)
@@ -1032,6 +1064,23 @@ func (as *AdaptationSet) AddNewRole(schemeIDURI string, value string) (*Role, er
10321064
return r, nil
10331065
}
10341066

1067+
// AddNewAccessibilityElement adds a new accessibility element to an adaptation set
1068+
// schemeIdUri - Scheme ID URI for the Accessibility element (i.e. urn:tva:metadata:cs:AudioPurposeCS:2007)
1069+
// value - specified value based on scheme
1070+
func (as *AdaptationSet) AddNewAccessibilityElement(scheme AccessibilityElementScheme, val string) (*Accessibility, error) {
1071+
accessibility := &Accessibility{
1072+
SchemeIdUri: Strptr((string)(scheme)),
1073+
Value: Strptr(val),
1074+
}
1075+
1076+
err := as.addAccessibility(accessibility)
1077+
if err != nil {
1078+
return nil, err
1079+
}
1080+
1081+
return accessibility, nil
1082+
}
1083+
10351084
// Sets the BaseURL for a Representation.
10361085
// baseURL - Base URL as a string (i.e. 800k/output-audio-und.mp4)
10371086
func (r *Representation) SetNewBaseURL(baseURL string) error {

‎mpd/mpd_read_write_test.go‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,35 @@ func TestExampleAddNewPeriod(t *testing.T) {
180180
testfixtures.CompareFixture(t, "fixtures/newperiod.mpd", xmlStr)
181181
}
182182

183+
func TestAddNewAccessibilityElementWriteToString(t *testing.T) {
184+
m := NewMPD(DASH_PROFILE_LIVE, VALID_MEDIA_PRESENTATION_DURATION, VALID_MIN_BUFFER_TIME)
185+
audioAS, err := m.AddNewAdaptationSetAudioWithID("7357", DASH_MIME_TYPE_AUDIO_MP4, VALID_SEGMENT_ALIGNMENT,
186+
VALID_START_WITH_SAP, VALID_LANG)
187+
if err != nil {
188+
t.Errorf("AddNewAccessibilityElement() error adding audio adaptation set: %v", err)
189+
return
190+
}
191+
192+
_, err = audioAS.AddNewAccessibilityElement(ACCESSIBILITY_ELEMENT_SCHEME_DESCRIPTIVE_AUDIO, "1")
193+
if err != nil {
194+
t.Errorf("AddNewAccessibilityElement() error adding accessibility element: %v", err)
195+
return
196+
}
197+
198+
xmlStr, err := m.WriteToString()
199+
require.NoError(t, err)
200+
expectedXML := `<?xml version="1.0" encoding="UTF-8"?>
201+
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" profiles="urn:mpeg:dash:profile:isoff-live:2011" type="static" mediaPresentationDuration="PT6M16S" minBufferTime="PT1.97S">
202+
<Period>
203+
<AdaptationSet mimeType="audio/mp4" startWithSAP="1" id="7357" segmentAlignment="true" lang="en">
204+
<Accessibility schemeIdUri="urn:tva:metadata:cs:AudioPurposeCS:2007" value="1"></Accessibility>
205+
</AdaptationSet>
206+
</Period>
207+
</MPD>
208+
`
209+
require.EqualString(t, expectedXML, xmlStr)
210+
}
211+
183212
func LiveProfile() *MPD {
184213
m := NewMPD(DASH_PROFILE_LIVE, VALID_MEDIA_PRESENTATION_DURATION, VALID_MIN_BUFFER_TIME,
185214
AttrAvailabilityStartTime(VALID_AVAILABILITY_START_TIME))
@@ -194,6 +223,7 @@ func LiveProfile() *MPD {
194223

195224
_, _ = audioAS.SetNewSegmentTemplate(1968, "$RepresentationID$/audio/en/init.mp4", "$RepresentationID$/audio/en/seg-$Number$.m4f", 0, 1000)
196225
_, _ = audioAS.AddNewRepresentationAudio(44100, 67095, "mp4a.40.2", "800")
226+
_, _ = audioAS.AddNewAccessibilityElement(ACCESSIBILITY_ELEMENT_SCHEME_DESCRIPTIVE_AUDIO, "1")
197227

198228
videoAS, _ := m.AddNewAdaptationSetVideoWithID("7357", DASH_MIME_TYPE_VIDEO_MP4, VALID_SCAN_TYPE, VALID_SEGMENT_ALIGNMENT, VALID_START_WITH_SAP)
199229

@@ -250,6 +280,7 @@ func LiveProfileDynamic() *MPD {
250280

251281
_, _ = audioAS.SetNewSegmentTemplate(1968, "$RepresentationID$/audio/en/init.mp4", "$RepresentationID$/audio/en/seg-$Number$.m4f", 0, 1000)
252282
_, _ = audioAS.AddNewRepresentationAudio(44100, 67095, "mp4a.40.2", "800")
283+
_, _ = audioAS.AddNewAccessibilityElement(ACCESSIBILITY_ELEMENT_SCHEME_DESCRIPTIVE_AUDIO, "1")
253284

254285
videoAS, _ := m.AddNewAdaptationSetVideoWithID("7357", DASH_MIME_TYPE_VIDEO_MP4, VALID_SCAN_TYPE, VALID_SEGMENT_ALIGNMENT, VALID_START_WITH_SAP)
255286

@@ -305,6 +336,7 @@ func HbbTVProfile() *MPD {
305336
_, _ = audioAS.SetNewSegmentTemplate(1968, "$RepresentationID$/audio/en/init.mp4", "$RepresentationID$/audio/en/seg-$Number$.m4f", 0, 1000)
306337
r, _ := audioAS.AddNewRepresentationAudio(44100, 67095, "mp4a.40.2", "800")
307338
_, _ = r.AddNewAudioChannelConfiguration(AUDIO_CHANNEL_CONFIGURATION_MPEG_DASH, "2")
339+
_, _ = audioAS.AddNewAccessibilityElement(ACCESSIBILITY_ELEMENT_SCHEME_DESCRIPTIVE_AUDIO, "1")
308340

309341
videoAS, _ := m.AddNewAdaptationSetVideoWithID("7357", DASH_MIME_TYPE_VIDEO_MP4, VALID_SCAN_TYPE, VALID_SEGMENT_ALIGNMENT, VALID_START_WITH_SAP)
310342

@@ -353,6 +385,7 @@ func OnDemandProfile() *MPD {
353385
_, _ = audioAS.AddNewContentProtectionRoot("08e367028f33436ca5dd60ffe5571e60")
354386
_, _ = audioAS.AddNewContentProtectionSchemeWidevineWithPSSH(getValidWVHeaderBytes())
355387
_, _ = audioAS.AddNewContentProtectionSchemePlayreadyWithPSSH(VALID_PLAYREADY_PRO)
388+
_, _ = audioAS.AddNewAccessibilityElement(ACCESSIBILITY_ELEMENT_SCHEME_DESCRIPTIVE_AUDIO, "1")
356389

357390
audioRep, _ := audioAS.AddNewRepresentationAudio(44100, 128558, "mp4a.40.5", "800k/audio-und")
358391
_ = audioRep.SetNewBaseURL("800k/output-audio-und.mp4")

‎mpd/mpd_test.go‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,3 +457,30 @@ func getValidWVHeaderBytes() []byte {
457457
}
458458
return wvHeader
459459
}
460+
461+
func TestAddNewAccessibilityElement(t *testing.T) {
462+
m := NewMPD(DASH_PROFILE_LIVE, VALID_MEDIA_PRESENTATION_DURATION, VALID_MIN_BUFFER_TIME)
463+
audioAS, err := m.AddNewAdaptationSetAudioWithID("7357", DASH_MIME_TYPE_AUDIO_MP4, VALID_SEGMENT_ALIGNMENT,
464+
VALID_START_WITH_SAP, VALID_LANG)
465+
if err != nil {
466+
t.Errorf("AddNewAccessibilityElement() error adding audio adaptation set: %v", err)
467+
return
468+
}
469+
470+
_, err = audioAS.AddNewAccessibilityElement(ACCESSIBILITY_ELEMENT_SCHEME_DESCRIPTIVE_AUDIO, "1")
471+
if err != nil {
472+
t.Errorf("AddNewAccessibilityElement() error adding accessibility element: %v", err)
473+
return
474+
}
475+
476+
if g, e := len(audioAS.AccessibilityElems), 1; g != e {
477+
t.Errorf("AddNewAccessibilityElement() wrong number of accessibility elements, got: %d, expected: %d",
478+
g, e)
479+
return
480+
}
481+
482+
elem := audioAS.AccessibilityElems[0]
483+
484+
require.EqualStringPtr(t, Strptr((string)(ACCESSIBILITY_ELEMENT_SCHEME_DESCRIPTIVE_AUDIO)), elem.SchemeIdUri)
485+
require.EqualStringPtr(t, Strptr("1"), elem.Value)
486+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /