@gcorevideo/player 0.0.1
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/assets/icons/new/arrow-left.svg +5 -0
- package/assets/icons/new/arrow-right.svg +5 -0
- package/assets/icons/new/check.svg +5 -0
- package/assets/icons/new/close.svg +12 -0
- package/assets/icons/new/full.svg +8 -0
- package/assets/icons/new/fullscreen-off.svg +14 -0
- package/assets/icons/new/fullscreen-on.svg +14 -0
- package/assets/icons/new/gear-hd.svg +16 -0
- package/assets/icons/new/gear.svg +12 -0
- package/assets/icons/new/hd.svg +8 -0
- package/assets/icons/new/pause.svg +5 -0
- package/assets/icons/new/pip.svg +5 -0
- package/assets/icons/new/play.svg +10 -0
- package/assets/icons/new/replayleft.svg +5 -0
- package/assets/icons/new/replayright.svg +5 -0
- package/assets/icons/new/speed.svg +5 -0
- package/assets/icons/new/stats.svg +3 -0
- package/assets/icons/new/stop.svg +3 -0
- package/assets/icons/new/subtitles-off.svg +5 -0
- package/assets/icons/new/subtitles-on.svg +6 -0
- package/assets/icons/new/volume-max.svg +5 -0
- package/assets/icons/new/volume-min.svg +5 -0
- package/assets/icons/new/volume-off.svg +5 -0
- package/assets/icons/old/cardboard.svg +4 -0
- package/assets/icons/old/close-share.svg +13 -0
- package/assets/icons/old/close.svg +13 -0
- package/assets/icons/old/fb.svg +13 -0
- package/assets/icons/old/fullscreen.svg +12 -0
- package/assets/icons/old/language.svg +1 -0
- package/assets/icons/old/pause.svg +12 -0
- package/assets/icons/old/play.svg +12 -0
- package/assets/icons/old/quality-arrow.svg +13 -0
- package/assets/icons/old/reload.svg +4 -0
- package/assets/icons/old/share.svg +13 -0
- package/assets/icons/old/sound-off.svg +15 -0
- package/assets/icons/old/sound-on.svg +15 -0
- package/assets/icons/old/streams.svg +3 -0
- package/assets/icons/old/twitter.svg +13 -0
- package/assets/icons/old/wn.svg +15 -0
- package/assets/icons/standard/01-play.svg +3 -0
- package/assets/icons/standard/02-pause.svg +3 -0
- package/assets/icons/standard/03-stop.svg +3 -0
- package/assets/icons/standard/04-volume.svg +3 -0
- package/assets/icons/standard/05-mute.svg +3 -0
- package/assets/icons/standard/06-expand.svg +3 -0
- package/assets/icons/standard/07-shrink.svg +3 -0
- package/assets/icons/standard/08-hd.svg +3 -0
- package/assets/icons/standard/09-cc.svg +8 -0
- package/assets/icons/standard/10-reload.svg +4 -0
- package/assets/style/main.scss +50 -0
- package/assets/style/theme.scss +42 -0
- package/assets/style/variables.scss +7 -0
- package/dist/DashPlayback-6wKK0_pL.js +666 -0
- package/dist/DashPlayback-8U6_s4Jc.js +666 -0
- package/dist/DashPlayback-BeZz7mN9.js +663 -0
- package/dist/DashPlayback-CRdja67F.js +667 -0
- package/dist/DashPlayback-D0df6zGg.js +663 -0
- package/dist/DashPlayback-D7egS-CZ.js +664 -0
- package/dist/DashPlayback-DH5lZMRR.js +663 -0
- package/dist/DashPlayback-DZfIc9sK.js +665 -0
- package/dist/DashPlayback-VhCxbQhn.js +666 -0
- package/dist/HlsPlayback-Avwy8-0O.js +749 -0
- package/dist/index.css +125 -0
- package/dist/index.js +467 -0
- package/lib/Player.d.ts +50 -0
- package/lib/Player.d.ts.map +1 -0
- package/lib/Player.js +310 -0
- package/lib/backend.d.ts +3 -0
- package/lib/backend.d.ts.map +1 -0
- package/lib/backend.js +10 -0
- package/lib/constants.d.ts +19 -0
- package/lib/constants.d.ts.map +1 -0
- package/lib/constants.js +18 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +9 -0
- package/lib/internal.types.d.ts +105 -0
- package/lib/internal.types.d.ts.map +1 -0
- package/lib/internal.types.js +1 -0
- package/lib/playback.types.d.ts +13 -0
- package/lib/playback.types.d.ts.map +1 -0
- package/lib/playback.types.js +1 -0
- package/lib/plugins/audio-selector/AudioSelector.d.ts +48 -0
- package/lib/plugins/audio-selector/AudioSelector.d.ts.map +1 -0
- package/lib/plugins/audio-selector/AudioSelector.js +282 -0
- package/lib/plugins/big-mute-button/BigMuteButton.d.ts +33 -0
- package/lib/plugins/big-mute-button/BigMuteButton.d.ts.map +1 -0
- package/lib/plugins/big-mute-button/BigMuteButton.js +148 -0
- package/lib/plugins/bottom-gear/BottomGear.d.ts +30 -0
- package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -0
- package/lib/plugins/bottom-gear/BottomGear.js +103 -0
- package/lib/plugins/click-to-pause/ClickToPause.d.ts +16 -0
- package/lib/plugins/click-to-pause/ClickToPause.d.ts.map +1 -0
- package/lib/plugins/click-to-pause/ClickToPause.js +73 -0
- package/lib/plugins/dash-playback/DashPlayback.d.ts +81 -0
- package/lib/plugins/dash-playback/DashPlayback.d.ts.map +1 -0
- package/lib/plugins/dash-playback/DashPlayback.js +658 -0
- package/lib/plugins/dash-plugin/DashPlayback.d.ts +86 -0
- package/lib/plugins/dash-plugin/DashPlayback.d.ts.map +1 -0
- package/lib/plugins/dash-plugin/DashPlayback.js +659 -0
- package/lib/plugins/disable-controls/DisableControls.d.ts +15 -0
- package/lib/plugins/disable-controls/DisableControls.d.ts.map +1 -0
- package/lib/plugins/disable-controls/DisableControls.js +69 -0
- package/lib/plugins/dvr-controls/DVRControls.d.ts +27 -0
- package/lib/plugins/dvr-controls/DVRControls.d.ts.map +1 -0
- package/lib/plugins/dvr-controls/DVRControls.js +110 -0
- package/lib/plugins/hls-playback/HlsPlayback.d.ts +102 -0
- package/lib/plugins/hls-playback/HlsPlayback.d.ts.map +1 -0
- package/lib/plugins/hls-playback/HlsPlayback.js +747 -0
- package/lib/plugins/level-selector/LevelSelector.d.ts +48 -0
- package/lib/plugins/level-selector/LevelSelector.d.ts.map +1 -0
- package/lib/plugins/level-selector/LevelSelector.js +287 -0
- package/lib/plugins/media-control/MediaControl.d.ts +186 -0
- package/lib/plugins/media-control/MediaControl.d.ts.map +1 -0
- package/lib/plugins/media-control/MediaControl.js +1000 -0
- package/lib/plugins/poster/Poster.d.ts +41 -0
- package/lib/plugins/poster/Poster.d.ts.map +1 -0
- package/lib/plugins/poster/Poster.js +186 -0
- package/lib/trace/LogTracer.d.ts +12 -0
- package/lib/trace/LogTracer.d.ts.map +1 -0
- package/lib/trace/LogTracer.js +17 -0
- package/lib/trace/SentryTracer.d.ts +11 -0
- package/lib/trace/SentryTracer.d.ts.map +1 -0
- package/lib/trace/SentryTracer.js +18 -0
- package/lib/trace/Tracer.d.ts +13 -0
- package/lib/trace/Tracer.d.ts.map +1 -0
- package/lib/trace/Tracer.js +15 -0
- package/lib/trace/index.d.ts +18 -0
- package/lib/trace/index.d.ts.map +1 -0
- package/lib/trace/index.js +27 -0
- package/lib/trace/types.d.ts +8 -0
- package/lib/trace/types.d.ts.map +1 -0
- package/lib/trace/types.js +1 -0
- package/lib/types.d.ts +82 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +1 -0
- package/lib/utils/Logger.d.ts +23 -0
- package/lib/utils/Logger.d.ts.map +1 -0
- package/lib/utils/Logger.js +81 -0
- package/lib/utils/canAutoplay.d.ts +6 -0
- package/lib/utils/canAutoplay.d.ts.map +1 -0
- package/lib/utils/canAutoplay.js +30 -0
- package/lib/utils/errors.d.ts +2 -0
- package/lib/utils/errors.d.ts.map +1 -0
- package/lib/utils/errors.js +6 -0
- package/lib/utils/queryParams.d.ts +2 -0
- package/lib/utils/queryParams.d.ts.map +1 -0
- package/lib/utils/queryParams.js +4 -0
- package/lib/utils/scripts-load.d.ts +2 -0
- package/lib/utils/scripts-load.d.ts.map +1 -0
- package/lib/utils/scripts-load.js +20 -0
- package/lib/utils/types.d.ts +4 -0
- package/lib/utils/types.d.ts.map +1 -0
- package/lib/utils/types.js +1 -0
- package/lib/utils/utils.d.ts +7 -0
- package/lib/utils/utils.d.ts.map +1 -0
- package/lib/utils/utils.js +57 -0
- package/package.json +57 -0
- package/rollup.config.js +34 -0
- package/src/Player.ts +390 -0
- package/src/backend.ts +12 -0
- package/src/constants.ts +17 -0
- package/src/index.ts +9 -0
- package/src/internal.types.ts +126 -0
- package/src/playback.types.ts +15 -0
- package/src/plugins/dash-playback/DashPlayback.ts +808 -0
- package/src/plugins/dash-playback/_DashPlayback.js +688 -0
- package/src/plugins/hls-playback/HlsPlayback.ts +909 -0
- package/src/plugins/hls-playback/hls.js +706 -0
- package/src/trace/LogTracer.ts +23 -0
- package/src/trace/SentryTracer.ts +18 -0
- package/src/trace/Tracer.ts +27 -0
- package/src/trace/index.ts +32 -0
- package/src/trace/types.ts +7 -0
- package/src/types.ts +100 -0
- package/src/typings/@clappr/core/error_mixin.d.ts +15 -0
- package/src/typings/@clappr/core/events.d.ts +7 -0
- package/src/typings/@clappr/core/html5_video.d.ts +28 -0
- package/src/typings/@clappr/core/playback.d.ts +5 -0
- package/src/typings/@clappr/core/player.d.ts +83 -0
- package/src/typings/@clappr/plugins.d.ts +29 -0
- package/src/typings/clappr-zepto.xd.xts +44 -0
- package/src/typings/globals.d.ts +8 -0
- package/src/utils/Logger.ts +107 -0
- package/src/utils/canAutoplay.ts +39 -0
- package/src/utils/errors.ts +6 -0
- package/src/utils/queryParams.ts +5 -0
- package/src/utils/scripts-load.ts +26 -0
- package/src/utils/types.ts +5 -0
- package/src/utils/utils.ts +64 -0
- package/tsconfig.json +43 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
import { HTML5Video, Playback, Events, Log, Utils } from '@clappr/core';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
import DASHJS from 'dashjs';
|
|
4
|
+
import { trace } from './index.js';
|
|
5
|
+
import 'hls.js';
|
|
6
|
+
import 'event-lite';
|
|
7
|
+
import 'can-autoplay';
|
|
8
|
+
import 'kibo';
|
|
9
|
+
|
|
10
|
+
// Copyright 2014 Globo.com Player authors. All rights reserved.
|
|
11
|
+
// Use of this source code is governed by a BSD-style
|
|
12
|
+
// license that can be found in the LICENSE file.
|
|
13
|
+
const AUTO = -1;
|
|
14
|
+
const { now } = Utils;
|
|
15
|
+
const T = "DashPlayback";
|
|
16
|
+
class DashPlayback extends HTML5Video {
|
|
17
|
+
_levels = null;
|
|
18
|
+
_currentLevel = null;
|
|
19
|
+
_durationExcludesAfterLiveSyncPoint = false;
|
|
20
|
+
_isReadyState = false;
|
|
21
|
+
_playableRegionDuration = 0;
|
|
22
|
+
_playableRegionStartTime = 0;
|
|
23
|
+
_playbackType = Playback.VOD;
|
|
24
|
+
_playlistType = null;
|
|
25
|
+
// #EXT-X-PROGRAM-DATE-TIME
|
|
26
|
+
_programDateTime = 0;
|
|
27
|
+
_dash = null;
|
|
28
|
+
_extrapolatedWindowDuration = 0;
|
|
29
|
+
_extrapolatedWindowNumSegments = 0;
|
|
30
|
+
_lastDuration = null;
|
|
31
|
+
_lastTimeUpdate = { current: 0, total: 0 };
|
|
32
|
+
_localStartTimeCorrelation = null;
|
|
33
|
+
_localEndTimeCorrelation = null;
|
|
34
|
+
_recoverAttemptsRemaining = 0;
|
|
35
|
+
_recoveredAudioCodecError = false;
|
|
36
|
+
_recoveredDecodingError = false;
|
|
37
|
+
startChangeQuality = false;
|
|
38
|
+
manifestInfo = null;
|
|
39
|
+
// #EXT-X-TARGETDURATION
|
|
40
|
+
_segmentTargetDuration = null;
|
|
41
|
+
_timeUpdateTimer = null;
|
|
42
|
+
get name() {
|
|
43
|
+
return 'dash';
|
|
44
|
+
}
|
|
45
|
+
get levels() {
|
|
46
|
+
return this._levels || [];
|
|
47
|
+
}
|
|
48
|
+
get currentLevel() {
|
|
49
|
+
if (this._currentLevel === null) {
|
|
50
|
+
return AUTO;
|
|
51
|
+
}
|
|
52
|
+
// 0 is a valid level ID
|
|
53
|
+
return this._currentLevel;
|
|
54
|
+
}
|
|
55
|
+
get isReady() {
|
|
56
|
+
return this._isReadyState;
|
|
57
|
+
}
|
|
58
|
+
set currentLevel(id) {
|
|
59
|
+
this._currentLevel = id;
|
|
60
|
+
this.trigger(Events.PLAYBACK_LEVEL_SWITCH_START);
|
|
61
|
+
const cfg = {
|
|
62
|
+
streaming: {
|
|
63
|
+
abr: {
|
|
64
|
+
autoSwitchBitrate: {
|
|
65
|
+
video: id === -1,
|
|
66
|
+
},
|
|
67
|
+
ABRStrategy: 'abrL2A'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to switch levels');
|
|
72
|
+
const dash = this._dash;
|
|
73
|
+
this.options.dash && dash.updateSettings({ ...this.options.dash, ...cfg });
|
|
74
|
+
if (id !== -1) {
|
|
75
|
+
this._dash.setQualityFor('video', id);
|
|
76
|
+
}
|
|
77
|
+
if (this._playbackType === Playback.VOD) {
|
|
78
|
+
const curr_time = this._dash.time();
|
|
79
|
+
this.startChangeQuality = true;
|
|
80
|
+
dash.seek(0);
|
|
81
|
+
setTimeout(() => {
|
|
82
|
+
dash.seek(curr_time);
|
|
83
|
+
dash.play();
|
|
84
|
+
this.startChangeQuality = false;
|
|
85
|
+
}, 100);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
get _startTime() {
|
|
89
|
+
if (this._playbackType === Playback.LIVE && this._playlistType !== 'EVENT') {
|
|
90
|
+
return this._extrapolatedStartTime;
|
|
91
|
+
}
|
|
92
|
+
return this._playableRegionStartTime;
|
|
93
|
+
}
|
|
94
|
+
get _now() {
|
|
95
|
+
return now();
|
|
96
|
+
}
|
|
97
|
+
// the time in the video element which should represent the start of the sliding window
|
|
98
|
+
// extrapolated to increase in real time (instead of jumping as the early segments are removed)
|
|
99
|
+
get _extrapolatedStartTime() {
|
|
100
|
+
if (!this._localStartTimeCorrelation) {
|
|
101
|
+
return this._playableRegionStartTime;
|
|
102
|
+
}
|
|
103
|
+
const corr = this._localStartTimeCorrelation;
|
|
104
|
+
const timePassed = this._now - corr.local;
|
|
105
|
+
const extrapolatedWindowStartTime = (corr.remote + timePassed) / 1000;
|
|
106
|
+
// cap at the end of the extrapolated window duration
|
|
107
|
+
return Math.min(extrapolatedWindowStartTime, this._playableRegionStartTime + this._extrapolatedWindowDuration);
|
|
108
|
+
}
|
|
109
|
+
// the time in the video element which should represent the end of the content
|
|
110
|
+
// extrapolated to increase in real time (instead of jumping as segments are added)
|
|
111
|
+
get _extrapolatedEndTime() {
|
|
112
|
+
const actualEndTime = this._playableRegionStartTime + this._playableRegionDuration;
|
|
113
|
+
if (!this._localEndTimeCorrelation) {
|
|
114
|
+
return actualEndTime;
|
|
115
|
+
}
|
|
116
|
+
const corr = this._localEndTimeCorrelation;
|
|
117
|
+
const timePassed = this._now - corr.local;
|
|
118
|
+
const extrapolatedEndTime = (corr.remote + timePassed) / 1000;
|
|
119
|
+
return Math.max(actualEndTime - this._extrapolatedWindowDuration, Math.min(extrapolatedEndTime, actualEndTime));
|
|
120
|
+
}
|
|
121
|
+
get _duration() {
|
|
122
|
+
if (!this._dash) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
return this._dash.duration();
|
|
126
|
+
}
|
|
127
|
+
constructor(options, i18n, playerError) {
|
|
128
|
+
super(options, i18n, playerError);
|
|
129
|
+
console.log("DashPlayback constructor", { options, i18n, playerError });
|
|
130
|
+
// backwards compatibility (TODO: remove on 0.3.0)
|
|
131
|
+
// this.options.playback || (this.options.playback = this.options);
|
|
132
|
+
// The size of the start time extrapolation window measured as a multiple of segments.
|
|
133
|
+
// Should be 2 or higher, or 0 to disable. Should only need to be increased above 2 if more than one segment is
|
|
134
|
+
// removed from the start of the playlist at a time. E.g if the playlist is cached for 10 seconds and new chunks are
|
|
135
|
+
// added/removed every 5.
|
|
136
|
+
this._extrapolatedWindowNumSegments = this.options.playback?.extrapolatedWindowNumSegments ?? 2;
|
|
137
|
+
if (this.options.playbackType) {
|
|
138
|
+
this._playbackType = this.options.playbackType;
|
|
139
|
+
}
|
|
140
|
+
// this._lastTimeUpdate = { current: 0, total: 0 };
|
|
141
|
+
// this._lastDuration = null;
|
|
142
|
+
// for hls streams which have dvr with a sliding window,
|
|
143
|
+
// the content at the start of the playlist is removed as new
|
|
144
|
+
// content is appended at the end.
|
|
145
|
+
// this means the actual playable start time will increase as the
|
|
146
|
+
// start content is deleted
|
|
147
|
+
// For streams with dvr where the entire recording is kept from the
|
|
148
|
+
// beginning this should stay as 0
|
|
149
|
+
// this._playableRegionStartTime = 0;
|
|
150
|
+
// {local, remote} remote is the time in the video element that should represent 0
|
|
151
|
+
// local is the system time when the 'remote' measurment took place
|
|
152
|
+
// this._localStartTimeCorrelation = null;
|
|
153
|
+
// {local, remote} remote is the time in the video element that should represents the end
|
|
154
|
+
// local is the system time when the 'remote' measurment took place
|
|
155
|
+
// this._localEndTimeCorrelation = null;
|
|
156
|
+
// if content is removed from the beginning then this empty area should
|
|
157
|
+
// be ignored. "playableRegionDuration" excludes the empty area
|
|
158
|
+
// this._playableRegionDuration = 0;
|
|
159
|
+
// #EXT-X-PROGRAM-DATE-TIME
|
|
160
|
+
// this._programDateTime = 0;
|
|
161
|
+
// this.manifestInfo = null;
|
|
162
|
+
// true when the actual duration is longer than hlsjs's live sync point
|
|
163
|
+
// when this is false playableRegionDuration will be the actual duration
|
|
164
|
+
// when this is true playableRegionDuration will exclude the time after the sync point
|
|
165
|
+
// this._durationExcludesAfterLiveSyncPoint = false;
|
|
166
|
+
// // #EXT-X-TARGETDURATION
|
|
167
|
+
// this._segmentTargetDuration = null;
|
|
168
|
+
// #EXT-X-PLAYLIST-TYPE
|
|
169
|
+
// this._playlistType = null;
|
|
170
|
+
if (this.options.hlsRecoverAttempts) {
|
|
171
|
+
this._recoverAttemptsRemaining = this.options.hlsRecoverAttempts;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
_setup() {
|
|
175
|
+
trace(`${T} _setup`, { el: this.el });
|
|
176
|
+
const dash = DASHJS.MediaPlayer().create();
|
|
177
|
+
this._dash = dash;
|
|
178
|
+
this._dash.initialize();
|
|
179
|
+
const cfg = this.options.dash ?? {};
|
|
180
|
+
cfg.streaming = cfg.streaming || {};
|
|
181
|
+
cfg.streaming.text = cfg.streaming.text || { defaultEnabled: false };
|
|
182
|
+
this.options.dash && this._dash.updateSettings(cfg);
|
|
183
|
+
this._dash.attachView(this.el);
|
|
184
|
+
this._dash.setAutoPlay(false);
|
|
185
|
+
this._dash.attachSource(this.options.src);
|
|
186
|
+
this._dash.on(DASHJS.MediaPlayer.events.ERROR, this._onDASHJSSError);
|
|
187
|
+
this._dash.on(DASHJS.MediaPlayer.events.PLAYBACK_ERROR, this._onPlaybackError);
|
|
188
|
+
this._dash.on(DASHJS.MediaPlayer.events.STREAM_INITIALIZED, () => {
|
|
189
|
+
const bitrates = dash.getBitrateInfoListFor('video');
|
|
190
|
+
trace(`${T} STREAM_INITIALIZED`, { bitrates });
|
|
191
|
+
this._updatePlaybackType();
|
|
192
|
+
this._fillLevels(bitrates);
|
|
193
|
+
dash.on(DASHJS.MediaPlayer.events.QUALITY_CHANGE_REQUESTED, (evt) => {
|
|
194
|
+
// TODO
|
|
195
|
+
assert.ok(this._levels, 'An array of levels is required to change quality');
|
|
196
|
+
const newLevel = this._levels.find((level) => level.id === evt.newQuality); // TODO or simply this._levels[evt.newQuality]?
|
|
197
|
+
assert.ok(newLevel, 'A valid level is required to change quality');
|
|
198
|
+
this.onLevelSwitch(newLevel.level);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
this._dash.on(DASHJS.MediaPlayer.events.METRIC_ADDED, (e) => {
|
|
202
|
+
// console.log(`${T} onMetricAdded`, e);
|
|
203
|
+
// TODO
|
|
204
|
+
// Listen for the first manifest request in order to update player UI
|
|
205
|
+
if (e.metric === 'DVRInfo') { // TODO fix typings
|
|
206
|
+
assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to get metrics');
|
|
207
|
+
const dvrInfo = this._dash.getDashMetrics().getCurrentDVRInfo('video');
|
|
208
|
+
// trace(`${T} onMetricAdded DVRInfo`, {metric: e.metric, dvrInfo});
|
|
209
|
+
if (dvrInfo) {
|
|
210
|
+
// Extract time info
|
|
211
|
+
this.manifestInfo = dvrInfo.manifestInfo;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
this._dash.on(DASHJS.MediaPlayer.events.PLAYBACK_RATE_CHANGED, () => {
|
|
216
|
+
this.trigger('dash:playback-rate-changed');
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
render() {
|
|
220
|
+
this._ready();
|
|
221
|
+
return super.render();
|
|
222
|
+
}
|
|
223
|
+
_ready() {
|
|
224
|
+
this._isReadyState = true;
|
|
225
|
+
this.trigger(Events.PLAYBACK_READY, this.name);
|
|
226
|
+
}
|
|
227
|
+
// TODO
|
|
228
|
+
// _recover(evt, data, error) {
|
|
229
|
+
// console.warn('recover', evt, data, error);
|
|
230
|
+
// assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to recover');
|
|
231
|
+
// // TODO figure out what's going on here
|
|
232
|
+
// const dash = this._dash;
|
|
233
|
+
// if (!this._recoveredDecodingError) {
|
|
234
|
+
// this._recoveredDecodingError = true;
|
|
235
|
+
// // dash.recoverMediaError();
|
|
236
|
+
// } else if (!this._recoveredAudioCodecError) {
|
|
237
|
+
// this._recoveredAudioCodecError = true;
|
|
238
|
+
// // dash.swapAudioCodec();
|
|
239
|
+
// // dash.recoverMediaError();
|
|
240
|
+
// } else {
|
|
241
|
+
// // TODO what does it have to do with hlsjs?
|
|
242
|
+
// Log.error('hlsjs: failed to recover', { evt, data });
|
|
243
|
+
// error.level = PlayerError.Levels.FATAL;
|
|
244
|
+
// const formattedError = this.createError(error);
|
|
245
|
+
// this.trigger(Events.PLAYBACK_ERROR, formattedError);
|
|
246
|
+
// this.stop();
|
|
247
|
+
// }
|
|
248
|
+
// }
|
|
249
|
+
// override
|
|
250
|
+
_setupSrc() {
|
|
251
|
+
console.log(`${T} _setupSrc`);
|
|
252
|
+
// this playback manages the src on the video element itself
|
|
253
|
+
}
|
|
254
|
+
_startTimeUpdateTimer() {
|
|
255
|
+
this._stopTimeUpdateTimer();
|
|
256
|
+
this._timeUpdateTimer = setInterval(() => {
|
|
257
|
+
this._onDurationChange();
|
|
258
|
+
this._onTimeUpdate();
|
|
259
|
+
}, 100);
|
|
260
|
+
}
|
|
261
|
+
_stopTimeUpdateTimer() {
|
|
262
|
+
if (this._timeUpdateTimer) {
|
|
263
|
+
clearInterval(this._timeUpdateTimer);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
getProgramDateTime() {
|
|
267
|
+
return this._programDateTime;
|
|
268
|
+
}
|
|
269
|
+
// the duration on the video element itself should not be used
|
|
270
|
+
// as this does not necesarily represent the duration of the stream
|
|
271
|
+
// https://github.com/clappr/clappr/issues/668#issuecomment-157036678
|
|
272
|
+
getDuration() {
|
|
273
|
+
assert.ok(this._duration !== null, 'A valid duration is required to get the duration');
|
|
274
|
+
return this._duration;
|
|
275
|
+
}
|
|
276
|
+
getCurrentTime() {
|
|
277
|
+
// e.g. can be < 0 if user pauses near the start
|
|
278
|
+
// eventually they will then be kicked to the end by hlsjs if they run out of buffer
|
|
279
|
+
// before the official start time
|
|
280
|
+
return this._dash ? this._dash.time() : 0;
|
|
281
|
+
}
|
|
282
|
+
// the time that "0" now represents relative to when playback started
|
|
283
|
+
// for a stream with a sliding window this will increase as content is
|
|
284
|
+
// removed from the beginning
|
|
285
|
+
getStartTimeOffset() {
|
|
286
|
+
return this._startTime;
|
|
287
|
+
}
|
|
288
|
+
seekPercentage(percentage) {
|
|
289
|
+
let seekTo = this._duration;
|
|
290
|
+
if (percentage > 0) {
|
|
291
|
+
assert.ok(this._duration !== null, 'A valid duration is required to seek by percentage');
|
|
292
|
+
seekTo = this._duration * (percentage / 100);
|
|
293
|
+
}
|
|
294
|
+
assert.ok(seekTo !== null, 'A valid seek time is required');
|
|
295
|
+
this.seek(seekTo);
|
|
296
|
+
}
|
|
297
|
+
seek(time) {
|
|
298
|
+
if (time < 0) {
|
|
299
|
+
// eslint-disable-next-line max-len
|
|
300
|
+
Log.warn('Attempt to seek to a negative time. Resetting to live point. Use seekToLivePoint() to seek to the live point.');
|
|
301
|
+
time = this.getDuration();
|
|
302
|
+
}
|
|
303
|
+
this.dvrEnabled && this._updateDvr(time < this.getDuration() - 10);
|
|
304
|
+
assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to seek');
|
|
305
|
+
this._dash.seek(time);
|
|
306
|
+
}
|
|
307
|
+
seekToLivePoint() {
|
|
308
|
+
this.seek(this.getDuration());
|
|
309
|
+
}
|
|
310
|
+
_updateDvr(status) {
|
|
311
|
+
this.trigger(Events.PLAYBACK_DVR, status);
|
|
312
|
+
this.trigger(Events.PLAYBACK_STATS_ADD, { 'dvr': status });
|
|
313
|
+
}
|
|
314
|
+
_updateSettings() {
|
|
315
|
+
if (this._playbackType === Playback.VOD) {
|
|
316
|
+
this.settings.left = ['playpause', 'position', 'duration'];
|
|
317
|
+
// this.settings.left.push('playstop');
|
|
318
|
+
}
|
|
319
|
+
else if (this.dvrEnabled) {
|
|
320
|
+
this.settings.left = ['playpause'];
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
this.settings.left = ['playstop'];
|
|
324
|
+
}
|
|
325
|
+
this.settings.seekEnabled = this.isSeekEnabled();
|
|
326
|
+
this.trigger(Events.PLAYBACK_SETTINGSUPDATE);
|
|
327
|
+
}
|
|
328
|
+
_onPlaybackError = (event) => {
|
|
329
|
+
// TODO
|
|
330
|
+
};
|
|
331
|
+
_onDASHJSSError = (event) => {
|
|
332
|
+
// TODO
|
|
333
|
+
// only report/handle errors if they are fatal
|
|
334
|
+
// hlsjs should automatically handle non fatal errors
|
|
335
|
+
this._stopTimeUpdateTimer();
|
|
336
|
+
if (event.error === 'capability' && event.event === 'mediasource') {
|
|
337
|
+
// No support for MSE
|
|
338
|
+
const formattedError = this.createError(event.error);
|
|
339
|
+
this.trigger(Events.PLAYBACK_ERROR, formattedError);
|
|
340
|
+
Log.error('The media cannot be played because it requires a feature ' +
|
|
341
|
+
'that your browser does not support.');
|
|
342
|
+
}
|
|
343
|
+
else if (event.error === 'manifestError' && (
|
|
344
|
+
// Manifest type not supported
|
|
345
|
+
(event.event.id === 'createParser') ||
|
|
346
|
+
// Codec(s) not supported
|
|
347
|
+
(event.event.id === 'codec') ||
|
|
348
|
+
// No streams available to stream
|
|
349
|
+
(event.event.id === 'nostreams') ||
|
|
350
|
+
// Error creating Stream object
|
|
351
|
+
(event.event.id === 'nostreamscomposed') ||
|
|
352
|
+
// syntax error parsing the manifest
|
|
353
|
+
(event.event.id === 'parse') ||
|
|
354
|
+
// a stream has multiplexed audio+video
|
|
355
|
+
(event.event.id === 'multiplexedrep'))) {
|
|
356
|
+
// These errors have useful error messages, so we forward it on
|
|
357
|
+
const formattedError = this.createError(event.error);
|
|
358
|
+
this.trigger(Events.PLAYBACK_ERROR, formattedError);
|
|
359
|
+
if (event.error) {
|
|
360
|
+
Log.error(event.event.message);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
else if (event.error === 'mediasource') {
|
|
364
|
+
// This error happens when dash.js fails to allocate a SourceBuffer
|
|
365
|
+
// OR the underlying video element throws a `MediaError`.
|
|
366
|
+
// If it's a buffer allocation fail, the message states which buffer
|
|
367
|
+
// (audio/video/text) failed allocation.
|
|
368
|
+
// If it's a `MediaError`, dash.js inspects the error object for
|
|
369
|
+
// additional information to append to the error type.
|
|
370
|
+
const formattedError = this.createError(event.error);
|
|
371
|
+
this.trigger(Events.PLAYBACK_ERROR, formattedError);
|
|
372
|
+
Log.error(event.event);
|
|
373
|
+
}
|
|
374
|
+
else if (event.error === 'capability' && event.event === 'encryptedmedia') {
|
|
375
|
+
// Browser doesn't support EME
|
|
376
|
+
const formattedError = this.createError(event.error);
|
|
377
|
+
this.trigger(Events.PLAYBACK_ERROR, formattedError);
|
|
378
|
+
Log.error('The media cannot be played because it requires encryption ' +
|
|
379
|
+
'that your browser does not support.');
|
|
380
|
+
}
|
|
381
|
+
else if (event.error === 'key_session') {
|
|
382
|
+
// This block handles pretty much all errors thrown by the
|
|
383
|
+
// encryption subsystem
|
|
384
|
+
const formattedError = this.createError(event.error);
|
|
385
|
+
this.trigger(Events.PLAYBACK_ERROR, formattedError);
|
|
386
|
+
Log.error(event.event);
|
|
387
|
+
}
|
|
388
|
+
else if (event.error === 'download') {
|
|
389
|
+
const formattedError = this.createError(event.error);
|
|
390
|
+
this.trigger(Events.PLAYBACK_ERROR, formattedError);
|
|
391
|
+
Log.error('The media playback was aborted because too many consecutive ' +
|
|
392
|
+
'download errors occurred.');
|
|
393
|
+
// } else if (event.error === 'mssError') {
|
|
394
|
+
// const formattedError = this.createError(event.error);
|
|
395
|
+
// this.trigger(Events.PLAYBACK_ERROR, formattedError);
|
|
396
|
+
// if (event.error) {
|
|
397
|
+
// Log.error(event.error.message);
|
|
398
|
+
// }
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
// ignore the error
|
|
402
|
+
if (typeof event.error === "object") {
|
|
403
|
+
const formattedError = this.createError(event.error);
|
|
404
|
+
this.trigger(Events.PLAYBACK_ERROR, formattedError);
|
|
405
|
+
Log.error(event.error.message);
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
Log.error(event.error);
|
|
409
|
+
}
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
// only reset the dash player in 10ms async, so that the rest of the
|
|
413
|
+
// calling function finishes
|
|
414
|
+
setTimeout(() => {
|
|
415
|
+
assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to reset');
|
|
416
|
+
this._dash.reset();
|
|
417
|
+
}, 10);
|
|
418
|
+
};
|
|
419
|
+
_onTimeUpdate() {
|
|
420
|
+
if (this.startChangeQuality) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const update = {
|
|
424
|
+
current: this.getCurrentTime(),
|
|
425
|
+
total: this.getDuration(),
|
|
426
|
+
firstFragDateTime: this.getProgramDateTime()
|
|
427
|
+
};
|
|
428
|
+
const isSame = this._lastTimeUpdate && (update.current === this._lastTimeUpdate.current &&
|
|
429
|
+
update.total === this._lastTimeUpdate.total);
|
|
430
|
+
if (isSame) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
this._lastTimeUpdate = update;
|
|
434
|
+
this.trigger(Events.PLAYBACK_TIMEUPDATE, update, this.name);
|
|
435
|
+
}
|
|
436
|
+
_onDurationChange() {
|
|
437
|
+
const duration = this.getDuration();
|
|
438
|
+
if (this._lastDuration === duration) {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
this._lastDuration = duration;
|
|
442
|
+
super._onDurationChange();
|
|
443
|
+
}
|
|
444
|
+
get dvrEnabled() {
|
|
445
|
+
assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to get the DVR status');
|
|
446
|
+
return this._dash?.getDVRWindowSize() >= this._minDvrSize && this.getPlaybackType() === Playback.LIVE;
|
|
447
|
+
}
|
|
448
|
+
_onProgress() {
|
|
449
|
+
if (!this._dash) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
let buffer = this._dash.getDashMetrics().getCurrentBufferLevel('video');
|
|
453
|
+
if (!buffer) {
|
|
454
|
+
buffer = this._dash.getDashMetrics().getCurrentBufferLevel('audio');
|
|
455
|
+
}
|
|
456
|
+
const progress = {
|
|
457
|
+
start: this.getCurrentTime(),
|
|
458
|
+
current: this.getCurrentTime() + buffer,
|
|
459
|
+
total: this.getDuration()
|
|
460
|
+
};
|
|
461
|
+
this.trigger(Events.PLAYBACK_PROGRESS, progress, {});
|
|
462
|
+
}
|
|
463
|
+
play() {
|
|
464
|
+
trace(`${T} play`, { dash: !!this._dash });
|
|
465
|
+
if (!this._dash) {
|
|
466
|
+
this._setup();
|
|
467
|
+
}
|
|
468
|
+
super.play();
|
|
469
|
+
this._startTimeUpdateTimer();
|
|
470
|
+
}
|
|
471
|
+
pause() {
|
|
472
|
+
if (!this._dash) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
super.pause();
|
|
476
|
+
if (this.dvrEnabled) {
|
|
477
|
+
this._updateDvr(true);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
stop() {
|
|
481
|
+
if (this._dash) {
|
|
482
|
+
this._stopTimeUpdateTimer();
|
|
483
|
+
this._dash.reset();
|
|
484
|
+
super.stop();
|
|
485
|
+
this._dash = null;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
destroy() {
|
|
489
|
+
this._stopTimeUpdateTimer();
|
|
490
|
+
if (this._dash) {
|
|
491
|
+
this._dash.off(DASHJS.MediaPlayer.events.ERROR, this._onDASHJSSError);
|
|
492
|
+
this._dash.off(DASHJS.MediaPlayer.events.PLAYBACK_ERROR, this._onPlaybackError);
|
|
493
|
+
this._dash.off(DASHJS.MediaPlayer.events.MANIFEST_LOADED, this.getDuration);
|
|
494
|
+
this._dash.reset();
|
|
495
|
+
}
|
|
496
|
+
this._dash = null;
|
|
497
|
+
return super.destroy();
|
|
498
|
+
}
|
|
499
|
+
_updatePlaybackType() {
|
|
500
|
+
assert.ok(this._dash, 'An instance of dashjs MediaPlayer is required to update the playback type');
|
|
501
|
+
this._playbackType = this._dash.isDynamic() ? Playback.LIVE : Playback.VOD;
|
|
502
|
+
trace(`${T} _updatePlaybackType`, {
|
|
503
|
+
playbackType: this._playbackType,
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
_fillLevels(levels) {
|
|
507
|
+
// trace(`${T} _fillLevels`, {levels});
|
|
508
|
+
// TOOD check that levels[i].qualityIndex === i
|
|
509
|
+
this._levels = levels.map((level) => {
|
|
510
|
+
return { id: level.qualityIndex, level: level };
|
|
511
|
+
});
|
|
512
|
+
this.trigger(Events.PLAYBACK_LEVELS_AVAILABLE, this._levels);
|
|
513
|
+
}
|
|
514
|
+
// _onLevelUpdated(_: any, data) {
|
|
515
|
+
// this._segmentTargetDuration = data.details.targetduration;
|
|
516
|
+
// this._playlistType = data.details.type || null;
|
|
517
|
+
// let startTimeChanged = false;
|
|
518
|
+
// let durationChanged = false;
|
|
519
|
+
// const fragments = data.details.fragments;
|
|
520
|
+
// const previousPlayableRegionStartTime = this._playableRegionStartTime;
|
|
521
|
+
// const previousPlayableRegionDuration = this._playableRegionDuration;
|
|
522
|
+
// if (fragments.length === 0) {
|
|
523
|
+
// return;
|
|
524
|
+
// }
|
|
525
|
+
// // #EXT-X-PROGRAM-DATE-TIME
|
|
526
|
+
// if (fragments[0].rawProgramDateTime) {
|
|
527
|
+
// this._programDateTime = fragments[0].rawProgramDateTime;
|
|
528
|
+
// }
|
|
529
|
+
// if (this._playableRegionStartTime !== fragments[0].start) {
|
|
530
|
+
// startTimeChanged = true;
|
|
531
|
+
// this._playableRegionStartTime = fragments[0].start;
|
|
532
|
+
// }
|
|
533
|
+
// if (startTimeChanged) {
|
|
534
|
+
// if (!this._localStartTimeCorrelation) {
|
|
535
|
+
// // set the correlation to map to middle of the extrapolation window
|
|
536
|
+
// this._localStartTimeCorrelation = {
|
|
537
|
+
// local: this._now,
|
|
538
|
+
// remote: (fragments[0].start + (this._extrapolatedWindowDuration / 2)) * 1000
|
|
539
|
+
// };
|
|
540
|
+
// } else {
|
|
541
|
+
// // check if the correlation still works
|
|
542
|
+
// const corr = this._localStartTimeCorrelation;
|
|
543
|
+
// const timePassed = this._now - corr.local;
|
|
544
|
+
// // this should point to a time within the extrapolation window
|
|
545
|
+
// const startTime = (corr.remote + timePassed) / 1000;
|
|
546
|
+
// if (startTime < fragments[0].start) {
|
|
547
|
+
// // our start time is now earlier than the first chunk
|
|
548
|
+
// // (maybe the chunk was removed early)
|
|
549
|
+
// // reset correlation so that it sits at the beginning of the first available chunk
|
|
550
|
+
// this._localStartTimeCorrelation = {
|
|
551
|
+
// local: this._now,
|
|
552
|
+
// remote: fragments[0].start * 1000
|
|
553
|
+
// };
|
|
554
|
+
// } else if (startTime > previousPlayableRegionStartTime + this._extrapolatedWindowDuration) {
|
|
555
|
+
// // start time was past the end of the old extrapolation window (so would have been capped)
|
|
556
|
+
// // see if now that time would be inside the window, and if it would be set the correlation
|
|
557
|
+
// // so that it resumes from the time it was at at the end of the old window
|
|
558
|
+
// // update the correlation so that the time starts counting again from the value it's on now
|
|
559
|
+
// this._localStartTimeCorrelation = {
|
|
560
|
+
// local: this._now,
|
|
561
|
+
// remote: Math.max(
|
|
562
|
+
// fragments[0].start,
|
|
563
|
+
// previousPlayableRegionStartTime + this._extrapolatedWindowDuration
|
|
564
|
+
// ) * 1000
|
|
565
|
+
// };
|
|
566
|
+
// }
|
|
567
|
+
// }
|
|
568
|
+
// }
|
|
569
|
+
// let newDuration = data.details.totalduration;
|
|
570
|
+
// // if it's a live stream then shorten the duration to remove access
|
|
571
|
+
// // to the area after hlsjs's live sync point
|
|
572
|
+
// // seeks to areas after this point sometimes have issues
|
|
573
|
+
// if (this._playbackType === Playback.LIVE) {
|
|
574
|
+
// const fragmentTargetDuration = data.details.targetduration;
|
|
575
|
+
// const hlsjsConfig = this.options.playback.hlsjsConfig || {};
|
|
576
|
+
// // eslint-disable-next-line no-undef
|
|
577
|
+
// const liveSyncDurationCount = hlsjsConfig.liveSyncDurationCount || HLSJS.DefaultConfig.liveSyncDurationCount;
|
|
578
|
+
// const hiddenAreaDuration = fragmentTargetDuration * liveSyncDurationCount;
|
|
579
|
+
// if (hiddenAreaDuration <= newDuration) {
|
|
580
|
+
// newDuration -= hiddenAreaDuration;
|
|
581
|
+
// this._durationExcludesAfterLiveSyncPoint = true;
|
|
582
|
+
// } else {
|
|
583
|
+
// this._durationExcludesAfterLiveSyncPoint = false;
|
|
584
|
+
// }
|
|
585
|
+
// }
|
|
586
|
+
// if (newDuration !== this._playableRegionDuration) {
|
|
587
|
+
// durationChanged = true;
|
|
588
|
+
// this._playableRegionDuration = newDuration;
|
|
589
|
+
// }
|
|
590
|
+
// // Note the end time is not the playableRegionDuration
|
|
591
|
+
// // The end time will always increase even if content is removed from the beginning
|
|
592
|
+
// const endTime = fragments[0].start + newDuration;
|
|
593
|
+
// const previousEndTime = previousPlayableRegionStartTime + previousPlayableRegionDuration;
|
|
594
|
+
// const endTimeChanged = endTime !== previousEndTime;
|
|
595
|
+
// if (endTimeChanged) {
|
|
596
|
+
// if (!this._localEndTimeCorrelation) {
|
|
597
|
+
// // set the correlation to map to the end
|
|
598
|
+
// this._localEndTimeCorrelation = {
|
|
599
|
+
// local: this._now,
|
|
600
|
+
// remote: endTime * 1000
|
|
601
|
+
// };
|
|
602
|
+
// } else {
|
|
603
|
+
// // check if the correlation still works
|
|
604
|
+
// const corr = this._localEndTimeCorrelation;
|
|
605
|
+
// const timePassed = this._now - corr.local;
|
|
606
|
+
// // this should point to a time within the extrapolation window from the end
|
|
607
|
+
// const extrapolatedEndTime = (corr.remote + timePassed) / 1000;
|
|
608
|
+
// if (extrapolatedEndTime > endTime) {
|
|
609
|
+
// this._localEndTimeCorrelation = {
|
|
610
|
+
// local: this._now,
|
|
611
|
+
// remote: endTime * 1000
|
|
612
|
+
// };
|
|
613
|
+
// } else if (extrapolatedEndTime < endTime - this._extrapolatedWindowDuration) {
|
|
614
|
+
// // our extrapolated end time is now earlier than the extrapolation window from the actual end time
|
|
615
|
+
// // (maybe a chunk became available early)
|
|
616
|
+
// // reset correlation so that it sits at the beginning of the extrapolation window from the end time
|
|
617
|
+
// this._localEndTimeCorrelation = {
|
|
618
|
+
// local: this._now,
|
|
619
|
+
// remote: (endTime - this._extrapolatedWindowDuration) * 1000
|
|
620
|
+
// };
|
|
621
|
+
// } else if (extrapolatedEndTime > previousEndTime) {
|
|
622
|
+
// // end time was past the old end time (so would have been capped)
|
|
623
|
+
// // set the correlation so that it resumes from the time it was at at the end of the old window
|
|
624
|
+
// this._localEndTimeCorrelation = {
|
|
625
|
+
// local: this._now,
|
|
626
|
+
// remote: previousEndTime * 1000
|
|
627
|
+
// };
|
|
628
|
+
// }
|
|
629
|
+
// }
|
|
630
|
+
// }
|
|
631
|
+
// // now that the values have been updated call any methods that use on them so they get the updated values
|
|
632
|
+
// // immediately
|
|
633
|
+
// durationChanged && this._onDurationChange();
|
|
634
|
+
// startTimeChanged && this._onProgress();
|
|
635
|
+
// }
|
|
636
|
+
// _onFragmentLoaded(evt, data) {
|
|
637
|
+
// this.trigger(Events.PLAYBACK_FRAGMENT_LOADED, data);
|
|
638
|
+
// }
|
|
639
|
+
onLevelSwitch(currentLevel) {
|
|
640
|
+
trace(`${T} onLevelSwitch`, { currentLevel });
|
|
641
|
+
this.trigger(Events.PLAYBACK_BITRATE, {
|
|
642
|
+
height: currentLevel.height,
|
|
643
|
+
width: currentLevel.width,
|
|
644
|
+
bitrate: currentLevel.bitrate,
|
|
645
|
+
level: currentLevel.qualityIndex
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
getPlaybackType() {
|
|
649
|
+
return this._playbackType;
|
|
650
|
+
}
|
|
651
|
+
isSeekEnabled() {
|
|
652
|
+
return (this._playbackType === Playback.VOD || this.dvrEnabled);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
DashPlayback.canPlay = function (resource, mimeType) {
|
|
656
|
+
console.log(`${T} canPlay resource:%s mimeType:%s`, resource, mimeType);
|
|
657
|
+
const resourceParts = resource.split('?')[0].match(/.*\.(.*)$/) || [];
|
|
658
|
+
const isDash = ((resourceParts.length > 1 && resourceParts[1].toLowerCase() === 'mpd') ||
|
|
659
|
+
mimeType === 'application/dash+xml' || mimeType === 'video/mp4');
|
|
660
|
+
const ctor = window.MediaSource || ('WebKitMediaSource' in window ? window.WebKitMediaSource : undefined);
|
|
661
|
+
const isSupportByBrowser = typeof ctor === 'function';
|
|
662
|
+
console.log(`${T} canPlay isSupportByBrowser:%s isDash:%s`, isSupportByBrowser, isDash);
|
|
663
|
+
return !!(isSupportByBrowser && isDash);
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
export { DashPlayback as default };
|