cloudinary-video-player 1.6.2-edge.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +4 -0
- package/.snyk +19 -0
- package/.travis.yml +8 -0
- package/CHANGELOG.md +329 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/dist/cld-video-player.css +2110 -0
- package/dist/cld-video-player.js +5249 -0
- package/dist/cld-video-player.light.css +1766 -0
- package/dist/cld-video-player.light.js +1399 -0
- package/dist/cld-video-player.light.min.css +1 -0
- package/dist/cld-video-player.light.min.js +2 -0
- package/dist/cld-video-player.light.min.js.LICENSE.txt +23 -0
- package/dist/cld-video-player.min.css +1 -0
- package/dist/cld-video-player.min.js +2 -0
- package/dist/cld-video-player.min.js.LICENSE.txt +26 -0
- package/dist/fonts/cloudinary_icon_for_black_bg.svg +69 -0
- package/dist/fonts/cloudinary_icon_for_white_bg.svg +69 -0
- package/docs/360.html +102 -0
- package/docs/_template.html +93 -0
- package/docs/adaptive-streaming.html +297 -0
- package/docs/analytics.html +140 -0
- package/docs/api.html +302 -0
- package/docs/audio.html +136 -0
- package/docs/autoplay-fallback.html +138 -0
- package/docs/autoplay-on-scroll.html +107 -0
- package/docs/codec-fallback.html +158 -0
- package/docs/colors.html +135 -0
- package/docs/components.html +284 -0
- package/docs/custom-cld-errors.html +134 -0
- package/docs/floating-player.html +98 -0
- package/docs/fluid.html +117 -0
- package/docs/force-hls-subtitles-ios.html +159 -0
- package/docs/index.html +83 -0
- package/docs/interaction-area.html +398 -0
- package/docs/live-customer.html +128 -0
- package/docs/multiple-players.html +125 -0
- package/docs/playlist-by-tag-cap.html +182 -0
- package/docs/playlist-by-tag.html +133 -0
- package/docs/playlist.html +133 -0
- package/docs/poster.html +155 -0
- package/docs/raw-url.html +104 -0
- package/docs/recommendations.html +155 -0
- package/docs/scripts.js +156 -0
- package/docs/seek-thumbs.html +90 -0
- package/docs/shoppable.html +335 -0
- package/docs/subtitles-and-captions.html +267 -0
- package/docs/transformations.html +171 -0
- package/docs/ui-config.html +108 -0
- package/docs/vast-vpaid.html +149 -0
- package/env.example.js +6 -0
- package/env.js +6 -0
- package/jest-puppeteer.config.js +14 -0
- package/jest.config.js +196 -0
- package/package.json +99 -0
- package/sandbox.config.json +3 -0
- package/setupJest.js +1 -0
- package/src/assets/fonts/VideoJS.svg +120 -0
- package/src/assets/fonts/VideoJS.ttf +0 -0
- package/src/assets/fonts/VideoJS.woff +0 -0
- package/src/assets/fonts/icons.json +120 -0
- package/src/assets/icons/cloudinary_icon_for_black_bg.svg +69 -0
- package/src/assets/icons/cloudinary_icon_for_white_bg.svg +69 -0
- package/src/assets/icons/cloudinary_logo_for_dark_bg.svg +188 -0
- package/src/assets/icons/cloudinary_logo_for_white_bg.svg +188 -0
- package/src/assets/icons/info-circle.svg +17 -0
- package/src/assets/styles/ads-label.scss +16 -0
- package/src/assets/styles/components/interaction-areas.scss +158 -0
- package/src/assets/styles/components/playlist.scss +213 -0
- package/src/assets/styles/components/themedButton.scss +48 -0
- package/src/assets/styles/components/thumbnail.scss +94 -0
- package/src/assets/styles/components/title-bar.scss +67 -0
- package/src/assets/styles/components/triangle-volume-bar.scss +52 -0
- package/src/assets/styles/icons.scss +257 -0
- package/src/assets/styles/main.scss +324 -0
- package/src/assets/styles/mixins/aspect-ratio.scss +16 -0
- package/src/assets/styles/mixins/disable-transition.scss +3 -0
- package/src/assets/styles/mixins/mixins.scss +5 -0
- package/src/assets/styles/mixins/skin.scss +64 -0
- package/src/assets/styles/variables.scss +2 -0
- package/src/assets/styles/videojs-ima.scss +252 -0
- package/src/components/component-utils.js +20 -0
- package/src/components/index.js +21 -0
- package/src/components/interaction-area/interaction-area.const.js +30 -0
- package/src/components/interaction-area/interaction-area.service.js +223 -0
- package/src/components/interaction-area/interaction-area.utils.js +236 -0
- package/src/components/jumpButtons/jump-10-minus.js +21 -0
- package/src/components/jumpButtons/jump-10-plus.js +20 -0
- package/src/components/logoButton/logo-button.const.js +3 -0
- package/src/components/logoButton/logo-button.js +30 -0
- package/src/components/logoButton/logo-button.scss +15 -0
- package/src/components/playlist/components/playlist-button.js +34 -0
- package/src/components/playlist/components/playlist-next-button.js +18 -0
- package/src/components/playlist/components/playlist-previous-button.js +18 -0
- package/src/components/playlist/components/playlist.js +5 -0
- package/src/components/playlist/components/playlist.scss +15 -0
- package/src/components/playlist/components/upcoming-video-overlay.js +149 -0
- package/src/components/playlist/components/upcoming-video-overlay.scss +86 -0
- package/src/components/playlist/layout/playlist-layout-custom.js +21 -0
- package/src/components/playlist/layout/playlist-layout-horizontal.js +16 -0
- package/src/components/playlist/layout/playlist-layout-vertical.js +19 -0
- package/src/components/playlist/layout/playlist-layout.js +110 -0
- package/src/components/playlist/panel/playlist-panel-item.js +86 -0
- package/src/components/playlist/panel/playlist-panel.js +92 -0
- package/src/components/playlist/playlist-widget.js +119 -0
- package/src/components/playlist/playlist.const.js +14 -0
- package/src/components/playlist/playlist.js +413 -0
- package/src/components/playlist/thumbnail/thumbnail.js +69 -0
- package/src/components/progress-control-events-blocker/progress-control-events-blocker.js +17 -0
- package/src/components/qualitySelector/quality-selector.scss +10 -0
- package/src/components/qualitySelector/qualitySelector.js +152 -0
- package/src/components/recommendations-overlay/index.js +3 -0
- package/src/components/recommendations-overlay/recommendations-overlay-content.js +57 -0
- package/src/components/recommendations-overlay/recommendations-overlay-hide-button.js +18 -0
- package/src/components/recommendations-overlay/recommendations-overlay-item.js +35 -0
- package/src/components/recommendations-overlay/recommendations-overlay-primary-item.js +81 -0
- package/src/components/recommendations-overlay/recommendations-overlay-secondary-item.js +48 -0
- package/src/components/recommendations-overlay/recommendations-overlay-secondary-items-container.js +35 -0
- package/src/components/recommendations-overlay/recommendations-overlay.js +94 -0
- package/src/components/recommendations-overlay/recommendations-overlay.scss +182 -0
- package/src/components/shoppable-bar/layout/bar-layout.js +111 -0
- package/src/components/shoppable-bar/layout/shoppable-panel-toggle.js +64 -0
- package/src/components/shoppable-bar/layout/shoppable-products-overlay.js +87 -0
- package/src/components/shoppable-bar/panel/shoppable-panel-item.js +105 -0
- package/src/components/shoppable-bar/panel/shoppable-panel.js +172 -0
- package/src/components/shoppable-bar/shoppable-post-widget.js +110 -0
- package/src/components/shoppable-bar/shoppable-widget.const.js +52 -0
- package/src/components/shoppable-bar/shoppable-widget.js +111 -0
- package/src/components/shoppable-bar/shoppable-widget.scss +359 -0
- package/src/components/themeButton/themedButton.const.js +3 -0
- package/src/components/themeButton/themedButton.js +25 -0
- package/src/components/title-bar/title-bar.js +79 -0
- package/src/config/defaults.js +25 -0
- package/src/extended-events.js +228 -0
- package/src/index.js +18 -0
- package/src/mixins/eventable.js +54 -0
- package/src/mixins/playlistable.js +106 -0
- package/src/plugins/analytics/index.js +245 -0
- package/src/plugins/autoplay-on-scroll/index.js +86 -0
- package/src/plugins/cloudinary/common.js +216 -0
- package/src/plugins/cloudinary/event-handler-registry.js +46 -0
- package/src/plugins/cloudinary/index.js +345 -0
- package/src/plugins/cloudinary/models/audio-source/audio-source.const.js +11 -0
- package/src/plugins/cloudinary/models/audio-source/audio-source.js +82 -0
- package/src/plugins/cloudinary/models/base-source.js +107 -0
- package/src/plugins/cloudinary/models/image-source.js +26 -0
- package/src/plugins/cloudinary/models/video-source/video-source.const.js +32 -0
- package/src/plugins/cloudinary/models/video-source/video-source.js +239 -0
- package/src/plugins/cloudinary/models/video-source/video-source.utils.js +57 -0
- package/src/plugins/colors/index.js +303 -0
- package/src/plugins/context-menu/components/context-menu-item.js +12 -0
- package/src/plugins/context-menu/components/context-menu.js +63 -0
- package/src/plugins/context-menu/context-menu.scss +30 -0
- package/src/plugins/context-menu/contextMenuContent.js +53 -0
- package/src/plugins/context-menu/index.js +134 -0
- package/src/plugins/dash/index.js +26 -0
- package/src/plugins/dash/setup-audio-tracks.js +112 -0
- package/src/plugins/dash/setup-text-tracks.js +195 -0
- package/src/plugins/dash/videojs-dash.js +372 -0
- package/src/plugins/floating-player/floating-player.scss +74 -0
- package/src/plugins/floating-player/index.js +129 -0
- package/src/plugins/ima/index.js +1775 -0
- package/src/plugins/index.js +31 -0
- package/src/plugins/interactive-plugin/index.js +10 -0
- package/src/plugins/videojs-http-source-selector/components/SourceMenuButton.js +98 -0
- package/src/plugins/videojs-http-source-selector/components/SourceMenuItem.js +52 -0
- package/src/plugins/videojs-http-source-selector/plugin.js +82 -0
- package/src/plugins/videojs-http-source-selector/plugin.scss +9 -0
- package/src/plugins/vtt-thumbnails/index.js +526 -0
- package/src/plugins/vtt-thumbnails/vtt-thumbnails.scss +29 -0
- package/src/utils/api.js +32 -0
- package/src/utils/apply-with-props.js +32 -0
- package/src/utils/array.js +22 -0
- package/src/utils/assign.js +27 -0
- package/src/utils/attributes-normalizer.js +72 -0
- package/src/utils/cloudinary.js +165 -0
- package/src/utils/css-prefix.js +43 -0
- package/src/utils/dom.js +74 -0
- package/src/utils/find.js +28 -0
- package/src/utils/fontFace.js +25 -0
- package/src/utils/groupBy.js +12 -0
- package/src/utils/index.js +29 -0
- package/src/utils/matches.js +11 -0
- package/src/utils/mixin.js +5 -0
- package/src/utils/object.js +26 -0
- package/src/utils/playButton.js +9 -0
- package/src/utils/positioning.js +78 -0
- package/src/utils/querystring.js +12 -0
- package/src/utils/slicing.js +21 -0
- package/src/utils/string.js +15 -0
- package/src/utils/throttle.js +30 -0
- package/src/utils/time.js +77 -0
- package/src/utils/type-inference.js +35 -0
- package/src/validators/validators-functions.js +48 -0
- package/src/validators/validators-types.js +78 -0
- package/src/validators/validators.js +110 -0
- package/src/video-player.const.js +68 -0
- package/src/video-player.js +761 -0
- package/src/video-player.utils.js +123 -0
- package/test/adaptive-streaming.test.js +38 -0
- package/test/ads.test.js +35 -0
- package/test/analytics.test.js +111 -0
- package/test/api.test.js +111 -0
- package/test/autoplay.scroll.test.js +23 -0
- package/test/basic-ui.test.js +59 -0
- package/test/colors.test.js +58 -0
- package/test/components.test.js +21 -0
- package/test/custom-error.test.js +24 -0
- package/test/fluid.test.js +36 -0
- package/test/isValidConfig.test.js +224 -0
- package/test/mocks/cloudinary-core-mock.js +0 -0
- package/test/mocks/styleMock.js +1 -0
- package/test/multiplayer.test.js +25 -0
- package/test/playlist.test.js +60 -0
- package/test/puppeteer/vp-env.js +19 -0
- package/test/recommendations.test.js +38 -0
- package/test/title-bar.test.js +28 -0
- package/test/ui-conf.test.js +49 -0
- package/test/unit/cloudinaryConfig.test.js +22 -0
- package/test/unit/cloudinaryUtils.test.js +53 -0
- package/test/unit/utils.test.js +27 -0
- package/test/unit/videoSource.test.js +454 -0
- package/tsconfig.json +15 -0
- package/types/video-player-tests.js +12 -0
- package/types/video-player-tests.ts +31 -0
- package/types/video-player.d.ts +570 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import dashjs from 'dashjs';
|
|
2
|
+
import videojs from 'video.js';
|
|
3
|
+
import { find } from 'utils/find';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Setup audio tracks. Take the tracks from dash and add the tracks to videojs. Listen for when
|
|
7
|
+
* videojs changes tracks and apply that to the dash player because videojs doesn't do this
|
|
8
|
+
* natively.
|
|
9
|
+
*
|
|
10
|
+
* @private
|
|
11
|
+
* @param {videojs} player the videojs player instance
|
|
12
|
+
* @param {videojs.tech} tech the videojs tech being used
|
|
13
|
+
*/
|
|
14
|
+
function handlePlaybackMetadataLoaded(player, tech) {
|
|
15
|
+
const mediaPlayer = player.dash.mediaPlayer;
|
|
16
|
+
|
|
17
|
+
const dashAudioTracks = mediaPlayer.getTracksFor('audio');
|
|
18
|
+
const videojsAudioTracks = player.audioTracks();
|
|
19
|
+
|
|
20
|
+
function generateIdFromTrackIndex(index) {
|
|
21
|
+
return `dash-audio-${index}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function findDashAudioTrack(subDashAudioTracks, videojsAudioTrack) {
|
|
25
|
+
return find(subDashAudioTracks, (track) => generateIdFromTrackIndex(track.index) === videojsAudioTrack.id);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Safari creates a single native `AudioTrack` (not `videojs.AudioTrack`) when loading. Clear all
|
|
29
|
+
// automatically generated audio tracks so we can create them all ourself.
|
|
30
|
+
if (videojsAudioTracks.length) {
|
|
31
|
+
tech.clearTracks(['audio']);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const currentAudioTrack = mediaPlayer.getCurrentTrackFor('audio');
|
|
35
|
+
|
|
36
|
+
dashAudioTracks.forEach((dashTrack) => {
|
|
37
|
+
let localizedLabel = null;
|
|
38
|
+
|
|
39
|
+
if (Array.isArray(dashTrack.labels)) {
|
|
40
|
+
for (let i = 0; i < dashTrack.labels.length; i++) {
|
|
41
|
+
if (
|
|
42
|
+
dashTrack.labels[i].lang &&
|
|
43
|
+
player.language().indexOf(dashTrack.labels[i].lang.toLowerCase()) !== -1
|
|
44
|
+
) {
|
|
45
|
+
localizedLabel = dashTrack.labels[i];
|
|
46
|
+
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let label = null;
|
|
53
|
+
|
|
54
|
+
if (localizedLabel) {
|
|
55
|
+
label = localizedLabel.text;
|
|
56
|
+
} else if (Array.isArray(dashTrack.labels) && dashTrack.labels.length === 1) {
|
|
57
|
+
label = dashTrack.labels[0].text;
|
|
58
|
+
} else {
|
|
59
|
+
label = dashTrack.lang;
|
|
60
|
+
|
|
61
|
+
if (dashTrack.roles && dashTrack.roles.length) {
|
|
62
|
+
label += ' (' + dashTrack.roles.join(', ') + ')';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Add the track to the player's audio track list.
|
|
67
|
+
videojsAudioTracks.addTrack(
|
|
68
|
+
new videojs.AudioTrack({
|
|
69
|
+
enabled: dashTrack === currentAudioTrack,
|
|
70
|
+
id: generateIdFromTrackIndex(dashTrack.index),
|
|
71
|
+
kind: dashTrack.kind || 'main',
|
|
72
|
+
label,
|
|
73
|
+
language: dashTrack.lang
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const audioTracksChangeHandler = () => {
|
|
79
|
+
for (let i = 0; i < videojsAudioTracks.length; i++) {
|
|
80
|
+
const track = videojsAudioTracks[i];
|
|
81
|
+
|
|
82
|
+
if (track.enabled) {
|
|
83
|
+
// Find the audio track we just selected by the id
|
|
84
|
+
const dashAudioTrack = findDashAudioTrack(dashAudioTracks, track);
|
|
85
|
+
|
|
86
|
+
// Set is as the current track
|
|
87
|
+
mediaPlayer.setCurrentTrack(dashAudioTrack);
|
|
88
|
+
|
|
89
|
+
// Stop looping
|
|
90
|
+
// eslint-disable-next-line no-continue
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
videojsAudioTracks.addEventListener('change', audioTracksChangeHandler);
|
|
97
|
+
player.dash.mediaPlayer.on(dashjs.MediaPlayer.events.STREAM_TEARDOWN_COMPLETE, () => {
|
|
98
|
+
videojsAudioTracks.removeEventListener('change', audioTracksChangeHandler);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/*
|
|
103
|
+
* Call `handlePlaybackMetadataLoaded` when `mediaPlayer` emits
|
|
104
|
+
* `dashjs.MediaPlayer.events.PLAYBACK_METADATA_LOADED`.
|
|
105
|
+
*/
|
|
106
|
+
export default function setupAudioTracks(player, tech) {
|
|
107
|
+
// When `dashjs` finishes loading metadata, create audio tracks for `video.js`.
|
|
108
|
+
player.dash.mediaPlayer.on(
|
|
109
|
+
dashjs.MediaPlayer.events.PLAYBACK_METADATA_LOADED,
|
|
110
|
+
handlePlaybackMetadataLoaded.bind(null, player, tech)
|
|
111
|
+
);
|
|
112
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import dashjs from 'dashjs';
|
|
2
|
+
import videojs from 'video.js';
|
|
3
|
+
import window from 'global/window';
|
|
4
|
+
|
|
5
|
+
function find(l, f) {
|
|
6
|
+
for (let i = 0; i < l.length; i++) {
|
|
7
|
+
if (f(l[i])) {
|
|
8
|
+
return l[i];
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* Attach text tracks from dash.js to videojs
|
|
15
|
+
*
|
|
16
|
+
* @param {videojs} player the videojs player instance
|
|
17
|
+
* @param {array} tracks the tracks loaded by dash.js to attach to videojs
|
|
18
|
+
*
|
|
19
|
+
* @private
|
|
20
|
+
*/
|
|
21
|
+
function attachDashTextTracksToVideojs(player, tech, tracks) {
|
|
22
|
+
const trackDictionary = [];
|
|
23
|
+
|
|
24
|
+
// Add remote tracks
|
|
25
|
+
const tracksAttached = tracks
|
|
26
|
+
// Map input data to match HTMLTrackElement spec
|
|
27
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLTrackElement
|
|
28
|
+
.map((track) => {
|
|
29
|
+
let localizedLabel = null;
|
|
30
|
+
|
|
31
|
+
if (Array.isArray(track.labels)) {
|
|
32
|
+
for (let i = 0; i < track.labels.length; i++) {
|
|
33
|
+
if (
|
|
34
|
+
track.labels[i].lang &&
|
|
35
|
+
player.language().indexOf(track.labels[i].lang.toLowerCase()) !== -1
|
|
36
|
+
) {
|
|
37
|
+
localizedLabel = track.labels[i];
|
|
38
|
+
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let label = null;
|
|
45
|
+
|
|
46
|
+
if (localizedLabel) {
|
|
47
|
+
label = localizedLabel.text;
|
|
48
|
+
} else if (Array.isArray(track.labels) && track.labels.length === 1) {
|
|
49
|
+
label = track.labels[0].text;
|
|
50
|
+
} else {
|
|
51
|
+
label = track.lang || track.label;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
dashTrack: track,
|
|
56
|
+
trackConfig: {
|
|
57
|
+
label,
|
|
58
|
+
language: track.lang,
|
|
59
|
+
srclang: track.lang,
|
|
60
|
+
kind: track.kind
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// Add track to videojs track list
|
|
66
|
+
.map(({ trackConfig, dashTrack }) => {
|
|
67
|
+
const remoteTextTrack = player.addRemoteTextTrack(trackConfig, false);
|
|
68
|
+
|
|
69
|
+
trackDictionary.push({ textTrack: remoteTextTrack.track, dashTrack });
|
|
70
|
+
|
|
71
|
+
// Don't add the cues becuase we're going to let dash handle it natively. This will ensure
|
|
72
|
+
// that dash handle external time text files and fragmented text tracks.
|
|
73
|
+
//
|
|
74
|
+
// Example file with external time text files:
|
|
75
|
+
// https://storage.googleapis.com/shaka-demo-assets/sintel-mp4-wvtt/dash.mpd
|
|
76
|
+
|
|
77
|
+
return remoteTextTrack;
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
;
|
|
81
|
+
|
|
82
|
+
/*
|
|
83
|
+
* Scan `videojs.textTracks()` to find one that is showing. Set the dash text track.
|
|
84
|
+
*/
|
|
85
|
+
function updateActiveDashTextTrack() {
|
|
86
|
+
const dashMediaPlayer = player.dash.mediaPlayer;
|
|
87
|
+
const textTracks = player.textTracks();
|
|
88
|
+
let activeTextTrackIndex = -1;
|
|
89
|
+
|
|
90
|
+
// Iterate through the tracks and find the one marked as showing. If none are showing,
|
|
91
|
+
// `activeTextTrackIndex` will be set to `-1`, disabling text tracks.
|
|
92
|
+
for (let i = 0; i < textTracks.length; i += 1) {
|
|
93
|
+
const textTrack = textTracks[i];
|
|
94
|
+
|
|
95
|
+
if (textTrack.mode === 'showing') {
|
|
96
|
+
// Find the dash track we want to use
|
|
97
|
+
|
|
98
|
+
/* jshint loopfunc: true */
|
|
99
|
+
const dictionaryLookupResult = find(trackDictionary,
|
|
100
|
+
(track) => track.textTrack === textTrack);
|
|
101
|
+
/* jshint loopfunc: false */
|
|
102
|
+
|
|
103
|
+
const dashTrackToActivate = dictionaryLookupResult
|
|
104
|
+
? dictionaryLookupResult.dashTrack
|
|
105
|
+
: null;
|
|
106
|
+
|
|
107
|
+
// If we found a track, get it's index.
|
|
108
|
+
if (dashTrackToActivate) {
|
|
109
|
+
activeTextTrackIndex = tracks.indexOf(dashTrackToActivate);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// If the text track has changed, then set it in dash
|
|
115
|
+
if (activeTextTrackIndex !== dashMediaPlayer.getCurrentTextTrackIndex()) {
|
|
116
|
+
dashMediaPlayer.setTextTrack(activeTextTrackIndex);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Update dash when videojs's selected text track changes.
|
|
121
|
+
player.textTracks().on('change', updateActiveDashTextTrack);
|
|
122
|
+
|
|
123
|
+
// Cleanup event listeners whenever we start loading a new source
|
|
124
|
+
player.dash.mediaPlayer.on(dashjs.MediaPlayer.events.STREAM_TEARDOWN_COMPLETE, () => {
|
|
125
|
+
player.textTracks().off('change', updateActiveDashTextTrack);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Initialize the text track on our first run-through
|
|
129
|
+
updateActiveDashTextTrack();
|
|
130
|
+
|
|
131
|
+
return tracksAttached;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* Wait for dash to emit `TEXT_TRACKS_ADDED` and then attach the text tracks loaded by dash if
|
|
136
|
+
* we're not using native text tracks.
|
|
137
|
+
*
|
|
138
|
+
* @param {videojs} player the videojs player instance
|
|
139
|
+
* @private
|
|
140
|
+
*/
|
|
141
|
+
export default function setupTextTracks(player, tech, options) {
|
|
142
|
+
// Clear VTTCue if it was shimmed by vttjs and let dash.js use TextTrackCue.
|
|
143
|
+
// This is necessary because dash.js creates text tracks
|
|
144
|
+
// using addTextTrack which is incompatible with vttjs.VTTCue in IE11
|
|
145
|
+
if (window.VTTCue && !(/\[native code\]/).test(window.VTTCue.toString())) {
|
|
146
|
+
window.VTTCue = false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Store the tracks that we've added so we can remove them later.
|
|
150
|
+
let dashTracksAttachedToVideoJs = [];
|
|
151
|
+
|
|
152
|
+
// We're relying on the user to disable native captions. Show an error if they didn't do so.
|
|
153
|
+
if (tech.featuresNativeTextTracks) {
|
|
154
|
+
videojs.log.error('You must pass {html: {nativeCaptions: false}} in the videojs constructor ' +
|
|
155
|
+
'to use text tracks in videojs-contrib-dash');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const mediaPlayer = player.dash.mediaPlayer;
|
|
160
|
+
|
|
161
|
+
// Clear the tracks that we added. We don't clear them all because someone else can add tracks.
|
|
162
|
+
function clearDashTracks() {
|
|
163
|
+
dashTracksAttachedToVideoJs.forEach(player.removeRemoteTextTrack.bind(player));
|
|
164
|
+
|
|
165
|
+
dashTracksAttachedToVideoJs = [];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// eslint-disable-next-line no-unused-vars
|
|
169
|
+
function handleTextTracksAdded({ index, tracks }) {
|
|
170
|
+
// Stop listening for this event. We only want to hear it once.
|
|
171
|
+
mediaPlayer.off(dashjs.MediaPlayer.events.TEXT_TRACKS_ADDED, handleTextTracksAdded);
|
|
172
|
+
|
|
173
|
+
// Cleanup old tracks
|
|
174
|
+
clearDashTracks();
|
|
175
|
+
|
|
176
|
+
if (!tracks.length) {
|
|
177
|
+
// Don't try to add text tracks if there aren't any
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Save the tracks so we can remove them later
|
|
182
|
+
dashTracksAttachedToVideoJs = attachDashTextTracksToVideojs(player, tech, tracks, options);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Attach dash text tracks whenever we dash emits `TEXT_TRACKS_ADDED`.
|
|
186
|
+
mediaPlayer.on(dashjs.MediaPlayer.events.TEXT_TRACKS_ADDED, handleTextTracksAdded);
|
|
187
|
+
|
|
188
|
+
// When the player can play, remove the initialization events. We might not have received
|
|
189
|
+
// TEXT_TRACKS_ADDED` so we have to stop listening for it or we'll get errors when we load new
|
|
190
|
+
// videos and are listening for the same event in multiple places, including cleaned up
|
|
191
|
+
// mediaPlayers.
|
|
192
|
+
mediaPlayer.on(dashjs.MediaPlayer.events.CAN_PLAY, () => {
|
|
193
|
+
mediaPlayer.off(dashjs.MediaPlayer.events.TEXT_TRACKS_ADDED, handleTextTracksAdded);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import window from 'global/window';
|
|
2
|
+
import videojs from 'video.js';
|
|
3
|
+
import dashjs from 'dashjs';
|
|
4
|
+
import setupAudioTracks from './setup-audio-tracks';
|
|
5
|
+
import setupTextTracks from './setup-text-tracks';
|
|
6
|
+
import document from 'global/document';
|
|
7
|
+
import { assign } from '../../utils/assign';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* videojs-contrib-dash
|
|
11
|
+
*
|
|
12
|
+
* Use Dash.js to playback DASH content inside of Video.js via a SourceHandler
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
class Html5DashJS {
|
|
16
|
+
constructor(source, tech, options) {
|
|
17
|
+
// Get options from tech if not provided for backwards compatibility
|
|
18
|
+
options = options || tech.options_;
|
|
19
|
+
|
|
20
|
+
this.player = videojs(options.playerId);
|
|
21
|
+
this.player.dash = this.player.dash || {};
|
|
22
|
+
|
|
23
|
+
this.tech_ = tech;
|
|
24
|
+
this.el_ = tech.el();
|
|
25
|
+
this.elParent_ = this.el_.parentNode;
|
|
26
|
+
this.hasFiniteDuration_ = false;
|
|
27
|
+
|
|
28
|
+
// Do nothing if the src is falsey
|
|
29
|
+
if (!source.src) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// While the manifest is loading and Dash.js has not finished initializing
|
|
34
|
+
// we must defer events and functions calls with isReady_ and then `triggerReady`
|
|
35
|
+
// again later once everything is setup
|
|
36
|
+
tech.isReady_ = false;
|
|
37
|
+
|
|
38
|
+
if (Html5DashJS.updateSourceData) {
|
|
39
|
+
videojs.log.warn('updateSourceData has been deprecated.' +
|
|
40
|
+
' Please switch to using hook("updatesource", callback).');
|
|
41
|
+
source = Html5DashJS.updateSourceData(source);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// call updatesource hooks
|
|
45
|
+
Html5DashJS.hooks('updatesource').forEach((hook) => {
|
|
46
|
+
source = hook(source);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const manifestSource = source.src;
|
|
50
|
+
|
|
51
|
+
this.keySystemOptions_ = Html5DashJS.buildDashJSProtData(source.keySystemOptions);
|
|
52
|
+
|
|
53
|
+
// eslint-disable-next-line new-cap
|
|
54
|
+
this.player.dash.mediaPlayer = dashjs.MediaPlayer().create();
|
|
55
|
+
|
|
56
|
+
this.mediaPlayer_ = this.player.dash.mediaPlayer;
|
|
57
|
+
|
|
58
|
+
// For whatever reason, we need to call setTextDefaultEnabled(false) to get
|
|
59
|
+
// VTT captions to show, even though we're doing virtually the same thing
|
|
60
|
+
// in setup-text-tracks.js
|
|
61
|
+
this.mediaPlayer_.setTextDefaultEnabled(false);
|
|
62
|
+
|
|
63
|
+
// Log MedaPlayer messages through video.js
|
|
64
|
+
if (Html5DashJS.useVideoJSDebug) {
|
|
65
|
+
videojs.log.warn('useVideoJSDebug has been deprecated.' +
|
|
66
|
+
' Please switch to using hook("beforeinitialize", callback).');
|
|
67
|
+
Html5DashJS.useVideoJSDebug(this.mediaPlayer_);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (Html5DashJS.beforeInitialize) {
|
|
71
|
+
videojs.log.warn('beforeInitialize has been deprecated.' +
|
|
72
|
+
' Please switch to using hook("beforeinitialize", callback).');
|
|
73
|
+
Html5DashJS.beforeInitialize(this.player, this.mediaPlayer_);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
Html5DashJS.hooks('beforeinitialize').forEach((hook) => {
|
|
77
|
+
hook(this.player, this.mediaPlayer_);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Must run controller before these two lines or else there is no
|
|
81
|
+
// element to bind to.
|
|
82
|
+
this.mediaPlayer_.initialize();
|
|
83
|
+
|
|
84
|
+
// Retrigger a dash.js-specific error event as a player error
|
|
85
|
+
// See src/streaming/utils/ErrorHandler.js in dash.js code
|
|
86
|
+
// Handled with error (playback is stopped):
|
|
87
|
+
// - capabilityError
|
|
88
|
+
// - downloadError
|
|
89
|
+
// - manifestError
|
|
90
|
+
// - mediaSourceError
|
|
91
|
+
// - mediaKeySessionError
|
|
92
|
+
// Not handled:
|
|
93
|
+
// - timedTextError (video can still play)
|
|
94
|
+
// - mediaKeyMessageError (only fires under 'might not work' circumstances)
|
|
95
|
+
// eslint-disable-next-line complexity
|
|
96
|
+
this.retriggerError_ = (event) => {
|
|
97
|
+
if (event.type === 'error') {
|
|
98
|
+
|
|
99
|
+
/* initialize errros for ref https://cdn.dashjs.org/latest/jsdoc/core_errors_Errors.js.html
|
|
100
|
+
map to general init error
|
|
101
|
+
*/
|
|
102
|
+
if (event.error.code >= 10 && event.error.code <= 35) {
|
|
103
|
+
this.player.error({ code: 4, dashjsErrorCode: event.error.code, message: event.error.message });
|
|
104
|
+
} else if (event.error.code < 10) { // network errors
|
|
105
|
+
this.player.error({ code: event.error.code, dashjsErrorCode: event.error.code, message: event.error.message });
|
|
106
|
+
} else if (event.error.code >= 100 && event.error.code <= 114) { // Protection Errors https://cdn.dashjs.org/latest/jsdoc/streaming_protection_errors_ProtectionErrors.js.html
|
|
107
|
+
this.player.error({ code: 5, dashjsErrorCode: event.error.code, message: event.error.message });
|
|
108
|
+
} else {
|
|
109
|
+
this.player.error({ code: event.error.code, message: event.error.message });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// only reset the dash player in 10ms async, so that the rest of the
|
|
114
|
+
// calling function finishes
|
|
115
|
+
setTimeout(() => {
|
|
116
|
+
this.mediaPlayer_.reset();
|
|
117
|
+
}, 10);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
this.mediaPlayer_.on(dashjs.MediaPlayer.events.ERROR, this.retriggerError_);
|
|
121
|
+
|
|
122
|
+
this.getDuration_ = (event) => {
|
|
123
|
+
const periods = event.data.Period_asArray;
|
|
124
|
+
const oldHasFiniteDuration = this.hasFiniteDuration_;
|
|
125
|
+
|
|
126
|
+
if (event.data.mediaPresentationDuration || periods[periods.length - 1].duration) {
|
|
127
|
+
this.hasFiniteDuration_ = true;
|
|
128
|
+
} else {
|
|
129
|
+
// in case we run into a weird situation where we're VOD but then
|
|
130
|
+
// switch to live
|
|
131
|
+
this.hasFiniteDuration_ = false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (this.hasFiniteDuration_ !== oldHasFiniteDuration) {
|
|
135
|
+
this.player.trigger('durationchange');
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
this.mediaPlayer_.on(dashjs.MediaPlayer.events.MANIFEST_LOADED, this.getDuration_);
|
|
140
|
+
|
|
141
|
+
// Apply all dash options that are set
|
|
142
|
+
if (options.dash) {
|
|
143
|
+
Object.keys(options.dash).forEach((key) => {
|
|
144
|
+
const dashOptionsKey = 'set' + key.charAt(0).toUpperCase() + key.slice(1);
|
|
145
|
+
let value = options.dash[key];
|
|
146
|
+
|
|
147
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
148
|
+
if (this.mediaPlayer_.hasOwnProperty(dashOptionsKey)) {
|
|
149
|
+
// Providing a key without `set` prefix is now deprecated.
|
|
150
|
+
videojs.log.warn('Using dash options in videojs-contrib-dash without the set prefix ' +
|
|
151
|
+
`has been deprecated. Change '${key}' to '${dashOptionsKey}'`);
|
|
152
|
+
|
|
153
|
+
// Set key so it will still work
|
|
154
|
+
key = dashOptionsKey;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
158
|
+
if (!this.mediaPlayer_.hasOwnProperty(key)) {
|
|
159
|
+
videojs.log.warn(
|
|
160
|
+
`Warning: dash configuration option unrecognized: ${key}`
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Guarantee `value` is an array
|
|
167
|
+
if (!Array.isArray(value)) {
|
|
168
|
+
value = [value];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
this.mediaPlayer_[key](...value);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.mediaPlayer_.attachView(this.el_);
|
|
176
|
+
|
|
177
|
+
// Dash.js autoplays by default, video.js will handle autoplay
|
|
178
|
+
this.mediaPlayer_.setAutoPlay(false);
|
|
179
|
+
|
|
180
|
+
// Setup audio tracks
|
|
181
|
+
// eslint-disable-next-line no-useless-call
|
|
182
|
+
setupAudioTracks.call(null, this.player, tech);
|
|
183
|
+
|
|
184
|
+
// Setup text tracks
|
|
185
|
+
// eslint-disable-next-line no-useless-call
|
|
186
|
+
setupTextTracks.call(null, this.player, tech, options);
|
|
187
|
+
|
|
188
|
+
// Attach the source with any protection data
|
|
189
|
+
this.mediaPlayer_.setProtectionData(this.keySystemOptions_);
|
|
190
|
+
this.mediaPlayer_.attachSource(manifestSource);
|
|
191
|
+
this.tech_.triggerReady();
|
|
192
|
+
// map videojs seek
|
|
193
|
+
this.player.on(this.tech_, 'seeking', () => {
|
|
194
|
+
// handle seek the same way as in dash.js
|
|
195
|
+
this.mediaPlayer_.seek((this.tech_.currentTime() - 8).toFixed(2));
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/*
|
|
200
|
+
* Iterate over the `keySystemOptions` array and convert each object into
|
|
201
|
+
* the type of object Dash.js expects in the `protData` argument.
|
|
202
|
+
*
|
|
203
|
+
* Also rename 'licenseUrl' property in the options to an 'serverURL' property
|
|
204
|
+
*/
|
|
205
|
+
static buildDashJSProtData(keySystemOptions) {
|
|
206
|
+
const output = {};
|
|
207
|
+
|
|
208
|
+
if (!keySystemOptions || !Array.isArray(keySystemOptions)) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
for (let i = 0; i < keySystemOptions.length; i++) {
|
|
213
|
+
const keySystem = keySystemOptions[i];
|
|
214
|
+
const options = videojs.mergeOptions({}, keySystem.options);
|
|
215
|
+
|
|
216
|
+
if (options.licenseUrl) {
|
|
217
|
+
options.serverURL = options.licenseUrl;
|
|
218
|
+
delete options.licenseUrl;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
output[keySystem.name] = options;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return output;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
dispose() {
|
|
228
|
+
if (this.mediaPlayer_) {
|
|
229
|
+
this.mediaPlayer_.off(dashjs.MediaPlayer.events.ERROR, this.retriggerError_);
|
|
230
|
+
this.mediaPlayer_.off(dashjs.MediaPlayer.events.MANIFEST_LOADED, this.getDuration_);
|
|
231
|
+
this.mediaPlayer_.reset();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (this.player.dash) {
|
|
235
|
+
delete this.player.dash;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
duration() {
|
|
240
|
+
if (this.mediaPlayer_.isDynamic() && !this.hasFiniteDuration_) {
|
|
241
|
+
return Infinity;
|
|
242
|
+
}
|
|
243
|
+
return this.mediaPlayer_.duration();
|
|
244
|
+
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Get a list of hooks for a specific lifecycle
|
|
249
|
+
*
|
|
250
|
+
* @param {string} type the lifecycle to get hooks from
|
|
251
|
+
* @param {Function=|Function[]=} hook Optionally add a hook tothe lifecycle
|
|
252
|
+
* @return {Array} an array of hooks or epty if none
|
|
253
|
+
* @method hooks
|
|
254
|
+
*/
|
|
255
|
+
static hooks(type, hook) {
|
|
256
|
+
Html5DashJS.hooks_[type] = Html5DashJS.hooks_[type] || [];
|
|
257
|
+
|
|
258
|
+
if (hook) {
|
|
259
|
+
Html5DashJS.hooks_[type] = Html5DashJS.hooks_[type].concat(hook);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return Html5DashJS.hooks_[type];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Add a function hook to a specific dash lifecycle
|
|
267
|
+
*
|
|
268
|
+
* @param {string} type the lifecycle to hook the function to
|
|
269
|
+
* @param {Function|Function[]} hook the function or array of functions to attach
|
|
270
|
+
* @method hook
|
|
271
|
+
*/
|
|
272
|
+
static hook(type, hook) {
|
|
273
|
+
Html5DashJS.hooks(type, hook);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Remove a hook from a specific dash lifecycle.
|
|
278
|
+
*
|
|
279
|
+
* @param {string} type the lifecycle that the function hooked to
|
|
280
|
+
* @param {Function} hook The hooked function to remove
|
|
281
|
+
* @return {boolean} True if the function was removed, false if not found
|
|
282
|
+
* @method removeHook
|
|
283
|
+
*/
|
|
284
|
+
static removeHook(type, hook) {
|
|
285
|
+
const index = Html5DashJS.hooks(type).indexOf(hook);
|
|
286
|
+
|
|
287
|
+
if (index === -1) {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
Html5DashJS.hooks_[type] = Html5DashJS.hooks_[type].slice();
|
|
292
|
+
Html5DashJS.hooks_[type].splice(index, 1);
|
|
293
|
+
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
Html5DashJS.hooks_ = {};
|
|
299
|
+
|
|
300
|
+
const canHandleKeySystems = function(source) {
|
|
301
|
+
// copy the source
|
|
302
|
+
source = assign({}, source);
|
|
303
|
+
|
|
304
|
+
if (Html5DashJS.updateSourceData) {
|
|
305
|
+
videojs.log.warn('updateSourceData has been deprecated.' +
|
|
306
|
+
' Please switch to using hook("updatesource", callback).');
|
|
307
|
+
source = Html5DashJS.updateSourceData(source);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// call updatesource hooks
|
|
311
|
+
Html5DashJS.hooks('updatesource').forEach((hook) => {
|
|
312
|
+
source = hook(source);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const videoEl = document.createElement('video');
|
|
316
|
+
|
|
317
|
+
if (source.keySystemOptions &&
|
|
318
|
+
!(window.navigator.requestMediaKeySystemAccess ||
|
|
319
|
+
// IE11 Win 8.1
|
|
320
|
+
videoEl.msSetMediaKeys)) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return true;
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
videojs.DashSourceHandler = function() {
|
|
328
|
+
return {
|
|
329
|
+
canHandleSource(source) {
|
|
330
|
+
const dashExtRE = /\.mpd/i;
|
|
331
|
+
|
|
332
|
+
if (!canHandleKeySystems(source)) {
|
|
333
|
+
return '';
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (videojs.DashSourceHandler.canPlayType(source.type)) {
|
|
337
|
+
return 'probably';
|
|
338
|
+
} else if (dashExtRE.test(source.src)) {
|
|
339
|
+
return 'maybe';
|
|
340
|
+
}
|
|
341
|
+
return '';
|
|
342
|
+
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
handleSource(source, tech, options) {
|
|
346
|
+
return new Html5DashJS(source, tech, options);
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
canPlayType(type) {
|
|
350
|
+
return videojs.DashSourceHandler.canPlayType(type);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
videojs.DashSourceHandler.canPlayType = function(type) {
|
|
356
|
+
const dashTypeRE = /^application\/dash\+xml/i;
|
|
357
|
+
|
|
358
|
+
if (dashTypeRE.test(type)) {
|
|
359
|
+
return 'probably';
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return '';
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// Only add the SourceHandler if the browser supports MediaSourceExtensions
|
|
366
|
+
if (window.MediaSource) {
|
|
367
|
+
// eslint-disable-next-line new-cap
|
|
368
|
+
videojs.getTech('Html5').registerSourceHandler(videojs.DashSourceHandler(), 0);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
videojs.Html5DashJS = Html5DashJS;
|
|
372
|
+
export default Html5DashJS;
|