Skip to content

Commit f6b7445

Browse files
authored
Merge branch 'Dash-Industry-Forum:development' into development
2 parents 96a81d2 + 27212ad commit f6b7445

14 files changed

Lines changed: 765 additions & 765 deletions

File tree

externals/tXml.js

Lines changed: 0 additions & 596 deletions
This file was deleted.

index.d.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2029,6 +2029,8 @@ declare namespace dashjs {
20292029

20302030
addABRCustomRule(type: string, rulename: string, rule: object): void;
20312031

2032+
addExternalSubtitle(externalSubtitle: ExternalSubtitle): void;
2033+
20322034
addRequestInterceptor(interceptor: RequestInterceptor): void;
20332035

20342036
addResponseInterceptor(interceptor: ResponseInterceptor): void;
@@ -2095,6 +2097,8 @@ declare namespace dashjs {
20952097

20962098
getDvrWindow(): DvrWindow;
20972099

2100+
getExternalSubtitles(): ExternalSubtitle[];
2101+
20982102
getInitialMediaSettingsFor(type: MediaType): MediaSettings;
20992103

21002104
getLowLatencyModeEnabled(): boolean;
@@ -2173,6 +2177,10 @@ declare namespace dashjs {
21732177

21742178
removeAllABRCustomRule(): void;
21752179

2180+
removeExternalSubtitleById(id: string): void;
2181+
2182+
removeExternalSubtitleByUrl(url:string): void;
2183+
21762184
removeRequestInterceptor(interceptor: RequestInterceptor): void;
21772185

21782186
removeResponseInterceptor(interceptor: ResponseInterceptor): void;
@@ -3748,6 +3756,8 @@ declare namespace dashjs {
37483756
export interface CustomParametersModel {
37493757
addAbrCustomRule(type: string, rulename: string, rule: object): void;
37503758

3759+
addExternalSubtitle(externalSubtitleObj: object): void;
3760+
37513761
addRequestInterceptor(interceptor: Function): void;
37523762

37533763
addResponseInterceptor(interceptor: Function): void;
@@ -3762,6 +3772,8 @@ declare namespace dashjs {
37623772

37633773
getCustomInitialTrackSelectionFunction(): Function;
37643774

3775+
getExternalSubtitles(): Array<ExternalSubtitle>
3776+
37653777
getLicenseRequestFilters(): Array<Function>;
37663778

37673779
getLicenseResponseFilters(): Array<Function>;
@@ -3784,6 +3796,10 @@ declare namespace dashjs {
37843796

37853797
removeAllAbrCustomRule(): void;
37863798

3799+
removeExternalSubtitleById(id: string): void;
3800+
3801+
removeExternalSubtitleByUrl(url: string): void;
3802+
37873803
removeRequestInterceptor(interceptor: Function): void;
37883804

37893805
removeResponseInterceptor(interceptor: Function): void;
@@ -5476,6 +5492,19 @@ declare namespace dashjs {
54765492
streamId: string | null;
54775493
}
54785494

5495+
export class ExternalSubtitle {
5496+
constructor(externalSubtitleObject: object);
5497+
5498+
id: string;
5499+
url: string;
5500+
language: string;
5501+
mimeType: string;
5502+
bandwidth: number;
5503+
periodId: string | null;
5504+
5505+
serializeToMpdParserFormat(): object;
5506+
}
5507+
54795508
export class FragmentRequest {
54805509
constructor(url: string);
54815510

index.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,31 @@
2929
* POSSIBILITY OF SUCH DAMAGE.
3030
*/
3131

32-
import { MediaPlayer } from './index_mediaplayerOnly.js';
32+
import {MediaPlayer} from './index_mediaplayerOnly.js';
3333
import MetricsReporting from './src/streaming/metrics/MetricsReporting.js';
3434
import Protection from './src/streaming/protection/Protection.js';
3535
import MediaPlayerFactory from './src/streaming/MediaPlayerFactory.js';
3636
import Debug from './src/core/Debug.js';
3737
import Constants from './src/streaming/constants/Constants.js';
38-
import { supportsMediaSource } from './src/streaming/utils/Capabilities.js';
38+
import {supportsMediaSource} from './src/streaming/utils/Capabilities.js';
39+
import ExternalSubtitle from './src/streaming/vo/ExternalSubtitle.js';
3940

4041
dashjs.Protection = Protection;
4142
dashjs.MetricsReporting = MetricsReporting;
4243
dashjs.MediaPlayerFactory = MediaPlayerFactory;
4344
dashjs.Debug = Debug;
4445
dashjs.supportsMediaSource = supportsMediaSource;
4546
dashjs.Constants = Constants;
47+
dashjs.ExternalSubtitle = ExternalSubtitle;
4648

4749
export default dashjs;
48-
export { MediaPlayer, Protection, MetricsReporting, MediaPlayerFactory, Debug, supportsMediaSource, Constants };
50+
export {
51+
MediaPlayer,
52+
Protection,
53+
MetricsReporting,
54+
MediaPlayerFactory,
55+
Debug,
56+
supportsMediaSource,
57+
Constants,
58+
ExternalSubtitle
59+
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dashjs",
3-
"version": "5.0.3",
3+
"version": "5.1.0",
44
"description": "A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.",
55
"author": "Dash Industry Forum",
66
"license": "BSD-3-Clause",
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>External WebVTT example</title>
6+
<script class="code" src="../../contrib/akamai/controlbar/ControlBar.js"></script>
7+
8+
<script src="../../dist/modern/umd/dash.all.debug.js"></script>
9+
10+
<!-- Bootstrap core CSS -->
11+
<link href="../lib/bootstrap/bootstrap.min.css" rel="stylesheet">
12+
<link href="../lib/main.css" rel="stylesheet">
13+
<link rel="stylesheet" href="../../contrib/akamai/controlbar/controlbar.css">
14+
15+
<style>
16+
video {
17+
width: 100%;
18+
}
19+
20+
.dash-video-player {
21+
position: relative; /* This position relative is needed to position the menus */
22+
margin: 0 auto;
23+
line-height: 1.0;
24+
}
25+
</style>
26+
27+
<script class="code">
28+
29+
function init() {
30+
var url = 'https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd',
31+
video = document.querySelector('video'),
32+
controlbar,
33+
player;
34+
var externalSubtitle = new dashjs.ExternalSubtitle({
35+
id: 'external_1',
36+
url: 'https://reference.dashif.org/other/content/webvtt/external-subtitle.vtt',
37+
mimeType: 'text/vtt',
38+
language: 'en',
39+
bandwidth: 256
40+
});
41+
var externalSubtitleTwo = new dashjs.ExternalSubtitle({
42+
id: 'external_2',
43+
url: 'https://reference.dashif.org/other/content/webvtt/external-subtitle-2.vtt',
44+
mimeType: 'text/vtt',
45+
language: 'de',
46+
bandwidth: 256
47+
});
48+
49+
player = dashjs.MediaPlayer().create();
50+
player.initialize(video, null, true);
51+
controlbar = new ControlBar(player); /* Checkout ControlBar.js for more info on how to target/add text tracks to UI */
52+
controlbar.initialize();
53+
player.addExternalSubtitle(externalSubtitle);
54+
player.addExternalSubtitle(externalSubtitleTwo);
55+
player.attachSource(url);
56+
}
57+
</script>
58+
</head>
59+
<body>
60+
61+
<main>
62+
<div class="container py-4">
63+
<header class="pb-3 mb-4 border-bottom">
64+
<img class=""
65+
src="../lib/img/dashjs-logo.png"
66+
width="200">
67+
</header>
68+
<div class="row">
69+
<div class="col-md-4">
70+
<div class="h-100 p-5 bg-light border rounded-3">
71+
<h3>WebVTT Dash Demo</h3>
72+
<p>This example shows how to use the dash.js API to add an external WebVTT file that is not part of
73+
the DASH manifest. </p>
74+
</div>
75+
</div>
76+
<div class="col-md-8">
77+
<div class="dash-video-player code">
78+
<div class="videoContainer" id="videoContainer">
79+
<video preload="auto" autoplay></video>
80+
<div id="videoController" class="video-controller unselectable">
81+
<div id="playPauseBtn" class="btn-play-pause" title="Play/Pause">
82+
<span id="iconPlayPause" class="icon-play"></span>
83+
</div>
84+
<span id="videoTime" class="time-display">00:00:00</span>
85+
<div id="fullscreenBtn" class="btn-fullscreen control-icon-layout" title="Fullscreen">
86+
<span class="icon-fullscreen-enter"></span>
87+
</div>
88+
<div id="bitrateListBtn" class="control-icon-layout" title="Bitrate List">
89+
<span class="icon-bitrate"></span>
90+
</div>
91+
<input type="range" id="volumebar" class="volumebar" value="1" min="0" max="1" step=".01"/>
92+
<div id="muteBtn" class="btn-mute control-icon-layout" title="Mute">
93+
<span id="iconMute" class="icon-mute-off"></span>
94+
</div>
95+
<div id="trackSwitchBtn" class="control-icon-layout" title="A/V Tracks">
96+
<span class="icon-tracks"></span>
97+
</div>
98+
<div id="captionBtn" class="btn-caption control-icon-layout" title="Closed Caption">
99+
<span class="icon-caption"></span>
100+
</div>
101+
<span id="videoDuration" class="duration-display">00:00:00</span>
102+
<div class="seekContainer">
103+
<div id="seekbar" class="seekbar seekbar-complete">
104+
<div id="seekbar-buffer" class="seekbar seekbar-buffer"></div>
105+
<div id="seekbar-play" class="seekbar seekbar-play"></div>
106+
</div>
107+
</div>
108+
</div>
109+
</div>
110+
</div>
111+
</div>
112+
</div>
113+
<div class="row">
114+
<div class="col-md-12">
115+
<div id="code-output"></div>
116+
</div>
117+
</div>
118+
<footer class="pt-3 mt-4 text-muted border-top">
119+
&copy; DASH-IF
120+
</footer>
121+
</div>
122+
</main>
123+
124+
125+
<script>
126+
document.addEventListener('DOMContentLoaded', function () {
127+
init();
128+
});
129+
</script>
130+
<script src="../highlighter.js"></script>
131+
</body>
132+
</html>

samples/samples.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,19 @@
547547
"Audio",
548548
"Events"
549549
]
550+
},
551+
{
552+
"title": "Adding external WebVTT subtitles",
553+
"description": "Example showing how to add external WebVTT subtitles that are not specified in the manifest",
554+
"href": "captioning/external-subtitle.html",
555+
"image": "lib/img/bbb-1.jpg",
556+
"labels": [
557+
"VoD",
558+
"External caption",
559+
"Video",
560+
"Audio",
561+
"Events"
562+
]
550563
}
551564
]
552565
},

src/dash/parser/DashParser.js

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import NumericMatcher from './matchers/NumericMatcher.js';
3737
import LangMatcher from './matchers/LangMatcher.js';
3838
import RepresentationBaseValuesMap from './maps/RepresentationBaseValuesMap.js';
3939
import SegmentValuesMap from './maps/SegmentValuesMap.js';
40-
import * as tXml from '../../../externals/tXml.js';
40+
import { parseXml as cmlParseXml } from '@svta/common-media-library/xml/parseXml.js';
4141

4242
// List of node that shall be represented as arrays
4343
const arrayNodes = [
@@ -135,31 +135,84 @@ function DashParser(config) {
135135
return manifest;
136136
}
137137

138+
function processXml(data) {
139+
const xml = cmlParseXml(data);
140+
const root = xml.childNodes.find(child => child.nodeName === 'MPD' || child.nodeName === 'Patch') || xml.childNodes[0];
138141

139-
function parseXml(data) {
140-
try {
141-
let root = tXml.parse(data, {
142-
parseNode: true,
143-
attrMatchers: matchers,
144-
nodesAsArray: arrayNodes
145-
});
146-
let ret = {};
147-
// If root element is xml node, then get first child node as root
148-
if (root.tagName.toLowerCase().indexOf('xml') !== -1) {
149-
for (let key in root) {
150-
if (Array.isArray(root[key])) {
151-
ret[key] = root[key][0];
152-
break;
153-
} else if (typeof root[key] === 'object') {
154-
ret[key] = root[key];
155-
break;
142+
function processNode(node) {
143+
// Convert tag name
144+
let p = node.nodeName.indexOf(':');
145+
if (p !== -1) {
146+
node.__prefix = node.prefix;
147+
node.nodeName = node.localName;
148+
}
149+
150+
const { childNodes, attributes, nodeName } = node;
151+
node.tagName = nodeName;
152+
153+
// Convert attributes
154+
for (let k in attributes) {
155+
let value = attributes[k];
156+
157+
if (nodeName === 'S') {
158+
value = parseInt(value);
159+
}
160+
else {
161+
for (let i = 0, len = matchers.length; i < len; i++) {
162+
const matcher = matchers[i];
163+
if (matcher.test(nodeName, k, value)) {
164+
value = matcher.converter(value);
165+
break;
166+
}
167+
}
168+
}
169+
170+
node[k] = value;
171+
}
172+
173+
// Convert children
174+
const len = childNodes?.length;
175+
176+
for (let i = 0; i < len; i++) {
177+
const child = childNodes[i];
178+
179+
if (child.nodeName === '#text') {
180+
node.__text = child.nodeValue;
181+
continue;
182+
}
183+
184+
processNode(child);
185+
186+
const { nodeName } = child;
187+
188+
if (Array.isArray(node[nodeName])) {
189+
node[nodeName].push(child);
190+
}
191+
else if (arrayNodes.indexOf(nodeName) !== -1) {
192+
if (!node[nodeName]) {
193+
node[nodeName] = [];
156194
}
195+
node[nodeName].push(child);
196+
} else {
197+
node[nodeName] = child;
157198
}
158-
} else {
159-
ret[root.tagName] = root;
160-
delete root.tagName;
161199
}
162-
return ret;
200+
201+
node.__children = childNodes;
202+
}
203+
204+
processNode(root);
205+
206+
return root;
207+
}
208+
209+
function parseXml(data) {
210+
try {
211+
const root = processXml(data);
212+
213+
return {
214+
[root.tagName]: root
215+
};
163216
} catch (e) {
164217
return null;
165218
}

0 commit comments

Comments
 (0)