shaka-player 4.2.8 → 4.2.10
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/.github/workflows/build-and-test.yaml +4 -4
- package/.github/workflows/demo-version-index.yaml +6 -0
- package/.github/workflows/selenium-lab-tests.yaml +78 -17
- package/.github/workflows/update-screenshots.yaml +142 -0
- package/AUTHORS +2 -1
- package/CHANGELOG.md +50 -0
- package/CONTRIBUTORS +4 -1
- package/demo/common/asset.js +15 -0
- package/demo/common/assets.js +4 -2
- package/demo/common/message_ids.js +1 -0
- package/demo/custom.js +19 -0
- package/demo/locales/en.json +1 -0
- package/demo/locales/source.json +4 -0
- package/demo/main.js +14 -0
- package/dist/controls.css +1 -1
- package/dist/demo.compiled.debug.js +170 -290
- package/dist/demo.compiled.debug.map +4 -4
- package/dist/demo.compiled.js +170 -290
- package/dist/demo.compiled.map +4 -4
- package/dist/deps.js +2 -2
- package/dist/locales.js +30 -30
- package/dist/receiver.compiled.debug.js +52 -81
- package/dist/receiver.compiled.debug.map +4 -4
- package/dist/receiver.compiled.js +52 -81
- package/dist/receiver.compiled.map +4 -4
- package/dist/shaka-player.compiled.d.ts +19 -3
- package/dist/shaka-player.compiled.debug.d.ts +19 -3
- package/dist/shaka-player.compiled.debug.externs.js +17 -12
- package/dist/shaka-player.compiled.debug.js +181 -176
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +17 -12
- package/dist/shaka-player.compiled.js +825 -823
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +19 -3
- package/dist/shaka-player.ui.debug.d.ts +19 -3
- package/dist/shaka-player.ui.debug.externs.js +5 -0
- package/dist/shaka-player.ui.debug.js +191 -186
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +5 -0
- package/dist/shaka-player.ui.js +955 -951
- package/dist/shaka-player.ui.map +3 -3
- package/docs/tutorials/a11y.md +0 -4
- package/docs/tutorials/ad_monetization.md +0 -4
- package/docs/tutorials/basic-usage.md +0 -5
- package/docs/tutorials/config.md +0 -5
- package/docs/tutorials/debugging.md +0 -5
- package/docs/tutorials/drm-config.md +0 -5
- package/docs/tutorials/errors.md +94 -0
- package/docs/tutorials/fairplay.md +4 -4
- package/docs/tutorials/index.json +2 -0
- package/docs/tutorials/license-server-auth.md +0 -5
- package/docs/tutorials/license-wrapping.md +0 -5
- package/docs/tutorials/network-and-buffering-config.md +0 -5
- package/docs/tutorials/ui-customization.md +0 -5
- package/docs/tutorials/ui.md +0 -7
- package/docs/tutorials/welcome.md +0 -5
- package/docs/tutorials/widevine-service-certs.md +45 -0
- package/externs/ima.js +9 -0
- package/externs/network_information.js +7 -2
- package/externs/shaka/abr_manager.js +6 -0
- package/externs/shaka/net.js +1 -0
- package/externs/shaka/player.js +13 -7
- package/lib/abr/simple_abr_manager.js +27 -3
- package/lib/ads/client_side_ad_manager.js +11 -5
- package/lib/dash/dash_parser.js +49 -48
- package/lib/debug/log.js +9 -13
- package/lib/hls/hls_parser.js +2 -0
- package/lib/media/drm_engine.js +4 -2
- package/lib/media/media_source_engine.js +11 -4
- package/lib/media/segment_index.js +2 -0
- package/lib/media/streaming_engine.js +8 -3
- package/lib/media/transmuxer.js +13 -7
- package/lib/media/video_wrapper.js +3 -3
- package/lib/net/http_fetch_plugin.js +79 -57
- package/lib/net/networking_engine.js +1 -0
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/player.js +24 -3
- package/lib/polyfill/media_capabilities.js +15 -8
- package/lib/polyfill/mediasource.js +24 -2
- package/lib/text/mp4_vtt_parser.js +4 -0
- package/lib/text/text_engine.js +1 -0
- package/lib/text/ui_text_displayer.js +1 -0
- package/lib/text/vtt_text_parser.js +4 -5
- package/lib/util/manifest_parser_utils.js +1 -2
- package/lib/util/mime_utils.js +12 -1
- package/lib/util/periods.js +5 -5
- package/lib/util/stream_utils.js +28 -16
- package/maintained-branches.md +3 -4
- package/package.json +1 -1
- package/test/media/content_workarounds_unit.js +43 -0
- package/test/media/drm_engine_integration.js +4 -17
- package/test/media/streaming_engine_unit.js +6 -1
- package/test/net/networking_engine_unit.js +6 -1
- package/test/player_integration.js +39 -0
- package/test/player_unit.js +57 -0
- package/test/polyfill/media_capabilities_unit.js +20 -23
- package/test/test/assets/text-clip-alt.vtt +63 -0
- package/test/test/util/fake_drm_engine.js +3 -0
- package/test/test/util/simple_fakes.js +6 -0
- package/test/test/util/test_scheme.js +9 -14
- package/test/text/mp4_vtt_parser_unit.js +8 -0
- package/test/text/vtt_text_parser_unit.js +26 -2
- package/test/text/web_vtt_layout_integration.js +0 -3
- package/test/util/mime_utils_unit.js +10 -0
package/lib/hls/hls_parser.js
CHANGED
|
@@ -2903,6 +2903,7 @@ shaka.hls.HlsParser.AUDIO_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
|
2903
2903
|
'cmfa': 'audio/mp4',
|
|
2904
2904
|
// MPEG2-TS also uses video/ for audio: https://bit.ly/TsMse
|
|
2905
2905
|
'ts': 'video/mp2t',
|
|
2906
|
+
'tsa': 'video/mp2t',
|
|
2906
2907
|
};
|
|
2907
2908
|
|
|
2908
2909
|
|
|
@@ -2919,6 +2920,7 @@ shaka.hls.HlsParser.VIDEO_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
|
2919
2920
|
'm4f': 'video/mp4',
|
|
2920
2921
|
'cmfv': 'video/mp4',
|
|
2921
2922
|
'ts': 'video/mp2t',
|
|
2923
|
+
'tsv': 'video/mp2t',
|
|
2922
2924
|
};
|
|
2923
2925
|
|
|
2924
2926
|
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -480,8 +480,10 @@ shaka.media.DrmEngine = class {
|
|
|
480
480
|
() => this.closeOpenSessions_());
|
|
481
481
|
}
|
|
482
482
|
|
|
483
|
-
const manifestInitData = this.currentDrmInfo_
|
|
484
|
-
|
|
483
|
+
const manifestInitData = this.currentDrmInfo_ ?
|
|
484
|
+
this.currentDrmInfo_.initData.find(
|
|
485
|
+
(initDataOverride) => initDataOverride.initData.length > 0,
|
|
486
|
+
) : null;
|
|
485
487
|
|
|
486
488
|
/**
|
|
487
489
|
* We can attach media keys before the playback actually begins when:
|
|
@@ -192,6 +192,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
192
192
|
'audio/mp4; codecs="ec-3"',
|
|
193
193
|
'audio/mp4; codecs="opus"',
|
|
194
194
|
'audio/mp4; codecs="flac"',
|
|
195
|
+
'audio/mp4; codecs="dtsc"', // DTS Digital Surround
|
|
196
|
+
'audio/mp4; codecs="dtse"', // DTS Express
|
|
197
|
+
'audio/mp4; codecs="dtsx"', // DTS:X
|
|
195
198
|
// WebM types
|
|
196
199
|
'video/webm; codecs="vp8"',
|
|
197
200
|
'video/webm; codecs="vp9"',
|
|
@@ -556,7 +559,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
556
559
|
// For HLS CEA-608/708 CLOSED-CAPTIONS, text data is embedded in
|
|
557
560
|
// the video stream, so textEngine may not have been initialized.
|
|
558
561
|
if (!this.textEngine_) {
|
|
559
|
-
this.reinitText(
|
|
562
|
+
this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE,
|
|
563
|
+
this.sequenceMode_);
|
|
560
564
|
}
|
|
561
565
|
|
|
562
566
|
if (transmuxedData.metadata) {
|
|
@@ -584,7 +588,8 @@ shaka.media.MediaSourceEngine = class {
|
|
|
584
588
|
data = transmuxedData.data;
|
|
585
589
|
} else if (hasClosedCaptions) {
|
|
586
590
|
if (!this.textEngine_) {
|
|
587
|
-
this.reinitText(
|
|
591
|
+
this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE,
|
|
592
|
+
this.sequenceMode_);
|
|
588
593
|
}
|
|
589
594
|
// If it is the init segment for closed captions, initialize the closed
|
|
590
595
|
// caption parser.
|
|
@@ -910,8 +915,10 @@ shaka.media.MediaSourceEngine = class {
|
|
|
910
915
|
async endOfStream(reason) {
|
|
911
916
|
await this.enqueueBlockingOperation_(() => {
|
|
912
917
|
// If endOfStream() has already been called on the media source,
|
|
913
|
-
// don't call it again.
|
|
914
|
-
|
|
918
|
+
// don't call it again. Also do not call if readyState is
|
|
919
|
+
// 'closed' (not attached to video element) since it is not a
|
|
920
|
+
// valid operation.
|
|
921
|
+
if (this.ended() || this.mediaSource_.readyState === 'closed') {
|
|
915
922
|
return;
|
|
916
923
|
}
|
|
917
924
|
// Tizen won't let us pass undefined, but it will let us omit the
|
|
@@ -77,6 +77,9 @@ shaka.media.StreamingEngine = class {
|
|
|
77
77
|
/** @private {?shaka.extern.Stream} */
|
|
78
78
|
this.currentTextStream_ = null;
|
|
79
79
|
|
|
80
|
+
/** @private {number} */
|
|
81
|
+
this.textStreamSequenceId_ = 0;
|
|
82
|
+
|
|
80
83
|
/**
|
|
81
84
|
* Maps a content type, e.g., 'audio', 'video', or 'text', to a MediaState.
|
|
82
85
|
*
|
|
@@ -220,6 +223,8 @@ shaka.media.StreamingEngine = class {
|
|
|
220
223
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
221
224
|
goog.asserts.assert(!this.mediaStates_.has(ContentType.TEXT),
|
|
222
225
|
'Should not call loadNewTextStream_ while streaming text!');
|
|
226
|
+
this.textStreamSequenceId_++;
|
|
227
|
+
const currentSequenceId = this.textStreamSequenceId_;
|
|
223
228
|
|
|
224
229
|
try {
|
|
225
230
|
// Clear MediaSource's buffered text, so that the new text stream will
|
|
@@ -242,7 +247,7 @@ shaka.media.StreamingEngine = class {
|
|
|
242
247
|
const streamText =
|
|
243
248
|
textDisplayer.isTextVisible() || this.config_.alwaysStreamText;
|
|
244
249
|
|
|
245
|
-
if (streamText) {
|
|
250
|
+
if (streamText && (this.textStreamSequenceId_ == currentSequenceId)) {
|
|
246
251
|
const state = this.createMediaState_(stream);
|
|
247
252
|
this.mediaStates_.set(ContentType.TEXT, state);
|
|
248
253
|
this.scheduleUpdate_(state, 0);
|
|
@@ -1559,10 +1564,10 @@ shaka.media.StreamingEngine = class {
|
|
|
1559
1564
|
'waiting for another stream to recover...');
|
|
1560
1565
|
}
|
|
1561
1566
|
|
|
1562
|
-
// QuotaExceededError gets thrown if
|
|
1567
|
+
// QuotaExceededError gets thrown if eviction didn't help to make room
|
|
1563
1568
|
// for a segment. We want to wait for a while (4 seconds is just an
|
|
1564
1569
|
// arbitrary number) before updating to give the playhead a chance to
|
|
1565
|
-
// advance, so we don't
|
|
1570
|
+
// advance, so we don't immediately throw again.
|
|
1566
1571
|
this.scheduleUpdate_(mediaState, 4);
|
|
1567
1572
|
}
|
|
1568
1573
|
|
package/lib/media/transmuxer.js
CHANGED
|
@@ -29,9 +29,7 @@ shaka.media.Transmuxer = class {
|
|
|
29
29
|
this.muxjs_ = shaka.dependencies.muxjs();
|
|
30
30
|
|
|
31
31
|
/** @private {muxjs.mp4.Transmuxer} */
|
|
32
|
-
this.muxTransmuxer_ =
|
|
33
|
-
'keepOriginalTimestamps': true,
|
|
34
|
-
});
|
|
32
|
+
this.muxTransmuxer_ = null;
|
|
35
33
|
|
|
36
34
|
/** @private {shaka.util.PublicPromise} */
|
|
37
35
|
this.transmuxPromise_ = null;
|
|
@@ -48,16 +46,22 @@ shaka.media.Transmuxer = class {
|
|
|
48
46
|
/** @private {boolean} */
|
|
49
47
|
this.isTransmuxing_ = false;
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
if (this.muxjs_) {
|
|
50
|
+
this.muxTransmuxer_ = new this.muxjs_.mp4.Transmuxer({
|
|
51
|
+
'keepOriginalTimestamps': true,
|
|
52
|
+
});
|
|
53
|
+
this.muxTransmuxer_.on('data', (segment) => this.onTransmuxed_(segment));
|
|
54
|
+
this.muxTransmuxer_.on('done', () => this.onTransmuxDone_());
|
|
55
|
+
}
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
/**
|
|
57
59
|
* @override
|
|
58
60
|
*/
|
|
59
61
|
destroy() {
|
|
60
|
-
this.muxTransmuxer_
|
|
62
|
+
if (this.muxTransmuxer_) {
|
|
63
|
+
this.muxTransmuxer_.dispose();
|
|
64
|
+
}
|
|
61
65
|
this.muxTransmuxer_ = null;
|
|
62
66
|
return Promise.resolve();
|
|
63
67
|
}
|
|
@@ -156,6 +160,8 @@ shaka.media.Transmuxer = class {
|
|
|
156
160
|
* metadata: !Array.<!Object>}>}
|
|
157
161
|
*/
|
|
158
162
|
transmux(data) {
|
|
163
|
+
goog.asserts.assert(this.muxTransmuxer_,
|
|
164
|
+
'mux.js should be available.');
|
|
159
165
|
goog.asserts.assert(!this.isTransmuxing_,
|
|
160
166
|
'No transmuxing should be in progress.');
|
|
161
167
|
this.isTransmuxing_ = true;
|
|
@@ -135,15 +135,15 @@ shaka.media.VideoWrapper = class {
|
|
|
135
135
|
// calling |Player.load|, meaning that |currentTime| is more meaningful than
|
|
136
136
|
// |startTime|.
|
|
137
137
|
//
|
|
138
|
-
// Seeking to the current time is a work around for Issue 1298
|
|
139
|
-
// do this, the video may get stuck and not play.
|
|
138
|
+
// Seeking to the current time is a work around for Issue 1298 and 4888.
|
|
139
|
+
// If we don't do this, the video may get stuck and not play.
|
|
140
140
|
//
|
|
141
141
|
// TODO: Need further investigation why it happens. Before and after
|
|
142
142
|
// setting the current time, video.readyState is 1, video.paused is true,
|
|
143
143
|
// and video.buffered's TimeRanges length is 0.
|
|
144
144
|
// See: https://github.com/shaka-project/shaka-player/issues/1298
|
|
145
145
|
this.mover_.moveTo(
|
|
146
|
-
this.video_.currentTime == 0 ?
|
|
146
|
+
(!this.video_.currentTime || this.video_.currentTime == 0) ?
|
|
147
147
|
startTime :
|
|
148
148
|
this.video_.currentTime);
|
|
149
149
|
}
|
|
@@ -120,65 +120,77 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
120
120
|
// not the body yet.
|
|
121
121
|
headersReceived(shaka.net.HttpFetchPlugin.headersToGenericObject_(
|
|
122
122
|
response.headers));
|
|
123
|
-
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
123
|
+
|
|
124
|
+
// In new versions of Chromium, HEAD requests now have a response body
|
|
125
|
+
// that is null.
|
|
126
|
+
// So just don't try to download the body at all, if it's a HEAD request,
|
|
127
|
+
// to avoid null reference errors.
|
|
128
|
+
// See: https://crbug.com/1297060
|
|
129
|
+
if (init.method != 'HEAD') {
|
|
130
|
+
goog.asserts.assert(response.body,
|
|
131
|
+
'non-HEAD responses should have a body');
|
|
132
|
+
|
|
133
|
+
// Getting the reader in this way allows us to observe the process of
|
|
134
|
+
// downloading the body, instead of just waiting for an opaque promise
|
|
135
|
+
// to resolve.
|
|
136
|
+
// We first clone the response because calling getReader locks the body
|
|
137
|
+
// stream; if we didn't clone it here, we would be unable to get the
|
|
138
|
+
// response's arrayBuffer later.
|
|
139
|
+
const reader = response.clone().body.getReader();
|
|
140
|
+
|
|
141
|
+
const contentLengthRaw = response.headers.get('Content-Length');
|
|
142
|
+
const contentLength =
|
|
143
|
+
contentLengthRaw ? parseInt(contentLengthRaw, 10) : 0;
|
|
144
|
+
|
|
145
|
+
const start = (controller) => {
|
|
146
|
+
const push = async () => {
|
|
147
|
+
let readObj;
|
|
148
|
+
try {
|
|
149
|
+
readObj = await reader.read();
|
|
150
|
+
} catch (e) {
|
|
151
|
+
// If we abort the request, we'll get an error here. Just ignore
|
|
152
|
+
// it since real errors will be reported when we read the buffer
|
|
153
|
+
// below.
|
|
154
|
+
shaka.log.v1('error reading from stream', e.message);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!readObj.done) {
|
|
159
|
+
loaded += readObj.value.byteLength;
|
|
160
|
+
if (streamDataCallback) {
|
|
161
|
+
await streamDataCallback(readObj.value);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const currentTime = Date.now();
|
|
166
|
+
// If the time between last time and this time we got progress event
|
|
167
|
+
// is long enough, or if a whole segment is downloaded, call
|
|
168
|
+
// progressUpdated().
|
|
169
|
+
if (currentTime - lastTime > 100 || readObj.done) {
|
|
170
|
+
progressUpdated(currentTime - lastTime, loaded - lastLoaded,
|
|
171
|
+
contentLength - loaded);
|
|
172
|
+
lastLoaded = loaded;
|
|
173
|
+
lastTime = currentTime;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (readObj.done) {
|
|
177
|
+
goog.asserts.assert(!readObj.value,
|
|
178
|
+
'readObj should be unset when "done" is true.');
|
|
179
|
+
controller.close();
|
|
180
|
+
} else {
|
|
181
|
+
controller.enqueue(readObj.value);
|
|
182
|
+
push();
|
|
151
183
|
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const currentTime = Date.now();
|
|
155
|
-
// If the time between last time and this time we got progress event
|
|
156
|
-
// is long enough, or if a whole segment is downloaded, call
|
|
157
|
-
// progressUpdated().
|
|
158
|
-
if (currentTime - lastTime > 100 || readObj.done) {
|
|
159
|
-
progressUpdated(currentTime - lastTime, loaded - lastLoaded,
|
|
160
|
-
contentLength - loaded);
|
|
161
|
-
lastLoaded = loaded;
|
|
162
|
-
lastTime = currentTime;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (readObj.done) {
|
|
166
|
-
goog.asserts.assert(!readObj.value,
|
|
167
|
-
'readObj should be unset when "done" is true.');
|
|
168
|
-
controller.close();
|
|
169
|
-
} else {
|
|
170
|
-
controller.enqueue(readObj.value);
|
|
171
|
-
push();
|
|
172
|
-
}
|
|
184
|
+
};
|
|
185
|
+
push();
|
|
173
186
|
};
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
arrayBuffer = await response.arrayBuffer();
|
|
187
|
+
// Create a ReadableStream to use the reader. We don't need to use the
|
|
188
|
+
// actual stream for anything, though, as we are using the response's
|
|
189
|
+
// arrayBuffer method to get the body, so we don't store the
|
|
190
|
+
// ReadableStream.
|
|
191
|
+
new ReadableStream({start}); // eslint-disable-line no-new
|
|
192
|
+
arrayBuffer = await response.arrayBuffer();
|
|
193
|
+
}
|
|
182
194
|
} catch (error) {
|
|
183
195
|
if (abortStatus.canceled) {
|
|
184
196
|
throw new shaka.util.Error(
|
|
@@ -243,6 +255,16 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
243
255
|
} else {
|
|
244
256
|
return false;
|
|
245
257
|
}
|
|
258
|
+
// Old fetch implementations hasn't body and ReadableStream implementation
|
|
259
|
+
// See: https://github.com/shaka-project/shaka-player/issues/5088
|
|
260
|
+
if (window.Response) {
|
|
261
|
+
const response = new Response('');
|
|
262
|
+
if (!response.body) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
246
268
|
return !!(window.fetch && window.AbortController);
|
|
247
269
|
}
|
|
248
270
|
};
|
|
@@ -312,6 +312,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
312
312
|
if (!responseAndGotProgress.gotProgress &&
|
|
313
313
|
this.onProgressUpdated_ &&
|
|
314
314
|
!response.fromCache &&
|
|
315
|
+
request.method != 'HEAD' &&
|
|
315
316
|
type == shaka.net.NetworkingEngine.RequestType.SEGMENT) {
|
|
316
317
|
this.onProgressUpdated_(response.timeMs, response.data.byteLength);
|
|
317
318
|
}
|
|
@@ -67,7 +67,7 @@ shaka.offline.indexeddb.StorageMechanism = class {
|
|
|
67
67
|
shaka.util.Error.Category.STORAGE,
|
|
68
68
|
shaka.util.Error.Code.INDEXED_DB_INIT_TIMED_OUT));
|
|
69
69
|
});
|
|
70
|
-
timeOutTimer.tickAfter(
|
|
70
|
+
timeOutTimer.tickAfter(5);
|
|
71
71
|
|
|
72
72
|
const open = window.indexedDB.open(name, version);
|
|
73
73
|
open.onsuccess = (event) => {
|
package/lib/player.js
CHANGED
|
@@ -806,7 +806,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
806
806
|
}
|
|
807
807
|
|
|
808
808
|
this.abrManagerFactory_ = null;
|
|
809
|
-
this.abrManager_ = null;
|
|
810
809
|
this.config_ = null;
|
|
811
810
|
this.stats_ = null;
|
|
812
811
|
this.videoContainer_ = null;
|
|
@@ -817,6 +816,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
817
816
|
this.networkingEngine_ = null;
|
|
818
817
|
}
|
|
819
818
|
|
|
819
|
+
if (this.abrManager_) {
|
|
820
|
+
this.abrManager_.release();
|
|
821
|
+
this.abrManager_ = null;
|
|
822
|
+
}
|
|
823
|
+
|
|
820
824
|
// FakeEventTarget implements IReleasable
|
|
821
825
|
super.release();
|
|
822
826
|
}
|
|
@@ -1232,6 +1236,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1232
1236
|
*/
|
|
1233
1237
|
shouldUseSrcEquals_(payload) {
|
|
1234
1238
|
const Platform = shaka.util.Platform;
|
|
1239
|
+
const MimeUtils = shaka.util.MimeUtils;
|
|
1235
1240
|
|
|
1236
1241
|
// If we are using a platform that does not support media source, we will
|
|
1237
1242
|
// fall back to src= to handle all playback.
|
|
@@ -1286,7 +1291,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1286
1291
|
// version there.
|
|
1287
1292
|
|
|
1288
1293
|
// Native HLS can be preferred on any platform via this flag:
|
|
1289
|
-
if (
|
|
1294
|
+
if (MimeUtils.isHlsType(mimeType) &&
|
|
1295
|
+
this.config_.streaming.preferNativeHls) {
|
|
1290
1296
|
return true;
|
|
1291
1297
|
}
|
|
1292
1298
|
|
|
@@ -2433,6 +2439,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2433
2439
|
// goes wrong.
|
|
2434
2440
|
new shaka.util.Timer(resolve).tickAfter(1);
|
|
2435
2441
|
});
|
|
2442
|
+
} else if (textTracks.length > 0) {
|
|
2443
|
+
this.isTextVisible_ = true;
|
|
2436
2444
|
}
|
|
2437
2445
|
|
|
2438
2446
|
// If we have moved on to another piece of content while waiting for
|
|
@@ -4084,6 +4092,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4084
4092
|
firstVariantWithLabel.language, '', 0, label);
|
|
4085
4093
|
|
|
4086
4094
|
this.chooseVariantAndSwitch_();
|
|
4095
|
+
} else if (this.video_ && this.video_.audioTracks) {
|
|
4096
|
+
const audioTracks = Array.from(this.video_.audioTracks);
|
|
4097
|
+
|
|
4098
|
+
let trackMatch = null;
|
|
4099
|
+
|
|
4100
|
+
for (const audioTrack of audioTracks) {
|
|
4101
|
+
if (audioTrack.label == label) {
|
|
4102
|
+
trackMatch = audioTrack;
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
if (trackMatch) {
|
|
4106
|
+
this.switchHtml5Track_(trackMatch);
|
|
4107
|
+
}
|
|
4087
4108
|
}
|
|
4088
4109
|
}
|
|
4089
4110
|
|
|
@@ -6605,7 +6626,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6605
6626
|
* @define {string} A version number taken from git at compile time.
|
|
6606
6627
|
* @export
|
|
6607
6628
|
*/
|
|
6608
|
-
shaka.Player.version = 'v4.2.
|
|
6629
|
+
shaka.Player.version = 'v4.2.10-uncompiled';
|
|
6609
6630
|
|
|
6610
6631
|
// Initialize the deprecation system using the version string we just set
|
|
6611
6632
|
// on the player.
|
|
@@ -8,7 +8,6 @@ goog.provide('shaka.polyfill.MediaCapabilities');
|
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.log');
|
|
10
10
|
goog.require('shaka.polyfill');
|
|
11
|
-
goog.require('shaka.util.Error');
|
|
12
11
|
goog.require('shaka.util.Platform');
|
|
13
12
|
|
|
14
13
|
|
|
@@ -116,6 +115,19 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
116
115
|
if (shaka.util.Platform.isChromecast()) {
|
|
117
116
|
isSupported =
|
|
118
117
|
shaka.polyfill.MediaCapabilities.canCastDisplayType_(videoConfig);
|
|
118
|
+
} else if (shaka.util.Platform.isTizen()) {
|
|
119
|
+
let extendedType = videoConfig.contentType;
|
|
120
|
+
if (videoConfig.width && videoConfig.height) {
|
|
121
|
+
extendedType += `; width=${videoConfig.width}`;
|
|
122
|
+
extendedType += `; height=${videoConfig.height}`;
|
|
123
|
+
}
|
|
124
|
+
if (videoConfig.framerate) {
|
|
125
|
+
extendedType += `; framerate=${videoConfig.framerate}`;
|
|
126
|
+
}
|
|
127
|
+
if (videoConfig.bitrate) {
|
|
128
|
+
extendedType += `; bitrate=${videoConfig.bitrate}`;
|
|
129
|
+
}
|
|
130
|
+
isSupported = MediaSource.isTypeSupported(extendedType);
|
|
119
131
|
} else {
|
|
120
132
|
isSupported = MediaSource.isTypeSupported(videoConfig.contentType);
|
|
121
133
|
}
|
|
@@ -226,13 +238,8 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
226
238
|
* @private
|
|
227
239
|
*/
|
|
228
240
|
static canCastDisplayType_(videoConfig) {
|
|
229
|
-
if (!(window.cast
|
|
230
|
-
|
|
231
|
-
throw new shaka.util.Error(
|
|
232
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
233
|
-
shaka.util.Error.Category.CAST,
|
|
234
|
-
shaka.util.Error.Code.CAST_API_UNAVAILABLE);
|
|
235
|
-
} else if (!(cast.__platform__ && cast.__platform__.canDisplayType)) {
|
|
241
|
+
if (!(window.cast &&
|
|
242
|
+
cast.__platform__ && cast.__platform__.canDisplayType)) {
|
|
236
243
|
shaka.log.warning('Expected cast APIs to be available! Falling back to ' +
|
|
237
244
|
'MediaSource.isTypeSupported() for type support.');
|
|
238
245
|
return MediaSource.isTypeSupported(videoConfig.contentType);
|
|
@@ -53,14 +53,19 @@ shaka.polyfill.MediaSource = class {
|
|
|
53
53
|
// Offsetting the end of the removal range seems to help.
|
|
54
54
|
// Bug filed: https://bugs.webkit.org/show_bug.cgi?id=177884
|
|
55
55
|
shaka.polyfill.MediaSource.patchRemovalRange_();
|
|
56
|
-
} else {
|
|
57
|
-
shaka.log.info('Patching Safari 13 MSE bugs.');
|
|
56
|
+
} else if (safariVersion <= 15) {
|
|
57
|
+
shaka.log.info('Patching Safari 13 & 14 & 15 MSE bugs.');
|
|
58
58
|
// Safari 13 does not correctly implement abort() on SourceBuffer.
|
|
59
59
|
// Calling abort() before appending a segment causes that segment to be
|
|
60
60
|
// incomplete in the buffer.
|
|
61
61
|
// Bug filed: https://bugs.webkit.org/show_bug.cgi?id=165342
|
|
62
62
|
shaka.polyfill.MediaSource.stubAbort_();
|
|
63
63
|
}
|
|
64
|
+
} else if (shaka.util.Platform.isEdge()) {
|
|
65
|
+
shaka.log.info('Rejecting TS container.');
|
|
66
|
+
// TS content is broken on Edge in general.
|
|
67
|
+
// See https://github.com/shaka-project/shaka-player/issues/4955
|
|
68
|
+
shaka.polyfill.MediaSource.rejectContainer_('mp2t');
|
|
64
69
|
} else if (shaka.util.Platform.isTizen2() ||
|
|
65
70
|
shaka.util.Platform.isTizen3() ||
|
|
66
71
|
shaka.util.Platform.isTizen4()) {
|
|
@@ -122,6 +127,23 @@ shaka.polyfill.MediaSource = class {
|
|
|
122
127
|
};
|
|
123
128
|
}
|
|
124
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Patch |MediaSource.isTypeSupported| to always reject |container|. This is
|
|
132
|
+
* used when we know that we are on a platform that does not work well with
|
|
133
|
+
* a given container.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} container
|
|
136
|
+
* @private
|
|
137
|
+
*/
|
|
138
|
+
static rejectContainer_(container) {
|
|
139
|
+
const isTypeSupported = MediaSource.isTypeSupported;
|
|
140
|
+
|
|
141
|
+
MediaSource.isTypeSupported = (mimeType) => {
|
|
142
|
+
const actualContainer = shaka.util.MimeUtils.getContainerType(mimeType);
|
|
143
|
+
return actualContainer != container && isTypeSupported(mimeType);
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
125
147
|
/**
|
|
126
148
|
* Patch |MediaSource.isTypeSupported| to always reject |codec|. This is used
|
|
127
149
|
* when we know that we are on a platform that does not work well with a given
|
package/lib/text/text_engine.js
CHANGED
|
@@ -139,6 +139,7 @@ shaka.text.TextEngine = class {
|
|
|
139
139
|
// into TextEngine and TextDisplayer.
|
|
140
140
|
if (mimeType == shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE ||
|
|
141
141
|
mimeType == shaka.util.MimeUtils.CEA708_CLOSED_CAPTION_MIMETYPE) {
|
|
142
|
+
this.parser_ = null;
|
|
142
143
|
return;
|
|
143
144
|
}
|
|
144
145
|
|
|
@@ -474,6 +474,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
474
474
|
wrapper = shaka.util.Dom.createHTMLElement('span');
|
|
475
475
|
wrapper.classList.add('shaka-text-wrapper');
|
|
476
476
|
wrapper.style.backgroundColor = cue.backgroundColor;
|
|
477
|
+
wrapper.style.lineHeight = 'normal';
|
|
477
478
|
cueElement.appendChild(wrapper);
|
|
478
479
|
}
|
|
479
480
|
|
|
@@ -391,8 +391,7 @@ shaka.text.VttTextParser = class {
|
|
|
391
391
|
end += timeOffset;
|
|
392
392
|
|
|
393
393
|
// Get the payload.
|
|
394
|
-
const payload =
|
|
395
|
-
text.slice(1).join('\n').trim());
|
|
394
|
+
const payload = text.slice(1).join('\n').trim();
|
|
396
395
|
|
|
397
396
|
let cue = null;
|
|
398
397
|
if (styles.has('global')) {
|
|
@@ -450,7 +449,7 @@ shaka.text.VttTextParser = class {
|
|
|
450
449
|
const childNode = childNodes[0];
|
|
451
450
|
if (childNode.nodeType == Node.TEXT_NODE ||
|
|
452
451
|
childNode.nodeType == Node.CDATA_SECTION_NODE) {
|
|
453
|
-
rootCue.payload = payload;
|
|
452
|
+
rootCue.payload = VttTextParser.htmlUnescape_(payload);
|
|
454
453
|
return;
|
|
455
454
|
}
|
|
456
455
|
}
|
|
@@ -461,7 +460,7 @@ shaka.text.VttTextParser = class {
|
|
|
461
460
|
rootCue.nestedCues = cues;
|
|
462
461
|
} else {
|
|
463
462
|
shaka.log.warning('The cue\'s markup could not be parsed: ', payload);
|
|
464
|
-
rootCue.payload = payload;
|
|
463
|
+
rootCue.payload = VttTextParser.htmlUnescape_(payload);
|
|
465
464
|
}
|
|
466
465
|
}
|
|
467
466
|
|
|
@@ -755,7 +754,7 @@ shaka.text.VttTextParser = class {
|
|
|
755
754
|
}
|
|
756
755
|
if (text.length > 0) {
|
|
757
756
|
const textCue = nestedCue.clone();
|
|
758
|
-
textCue.payload = text;
|
|
757
|
+
textCue.payload = VttTextParser.htmlUnescape_(text);
|
|
759
758
|
cues.push(textCue);
|
|
760
759
|
}
|
|
761
760
|
isFirst = false;
|
|
@@ -185,8 +185,7 @@ shaka.util.ManifestParserUtils.AUDIO_CODEC_REGEXPS_ = [
|
|
|
185
185
|
/^mp4a/,
|
|
186
186
|
/^[ae]c-3$/,
|
|
187
187
|
/^ac-4$/,
|
|
188
|
-
/^dts[
|
|
189
|
-
/^[du]dts$/, // DTS Surround Sound
|
|
188
|
+
/^dts[cex]$/, // DTS Digital Surround (dtsc), DTS Express (dtse), DTS:X (dtsx)
|
|
190
189
|
];
|
|
191
190
|
|
|
192
191
|
|
package/lib/util/mime_utils.js
CHANGED
|
@@ -52,7 +52,7 @@ shaka.util.MimeUtils = class {
|
|
|
52
52
|
return shaka.media.Transmuxer.convertTsCodecs(
|
|
53
53
|
contentType, fullMimeType);
|
|
54
54
|
}
|
|
55
|
-
} else if (contentType == ContentType.AUDIO) {
|
|
55
|
+
} else if (mimeType != 'video/mp2t' && contentType == ContentType.AUDIO) {
|
|
56
56
|
// video/mp2t is the correct mime type for TS audio, so only replace the
|
|
57
57
|
// word "video" with "audio" for non-TS audio content.
|
|
58
58
|
return fullMimeType.replace('video', 'audio');
|
|
@@ -196,6 +196,17 @@ shaka.util.MimeUtils = class {
|
|
|
196
196
|
return value;
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Checks if the given MIME type is HLS MIME type.
|
|
201
|
+
*
|
|
202
|
+
* @param {string} mimeType
|
|
203
|
+
* @return {boolean}
|
|
204
|
+
*/
|
|
205
|
+
static isHlsType(mimeType) {
|
|
206
|
+
return mimeType === 'application/x-mpegurl' ||
|
|
207
|
+
mimeType === 'application/vnd.apple.mpegurl';
|
|
208
|
+
}
|
|
209
|
+
|
|
199
210
|
/**
|
|
200
211
|
* Get the base and profile of a codec string. Where [0] will be the codec
|
|
201
212
|
* base and [1] will be the profile.
|
package/lib/util/periods.js
CHANGED
|
@@ -671,11 +671,11 @@ shaka.util.PeriodCombiner = class {
|
|
|
671
671
|
// Also checks if the segmentIndex is still valid after the async
|
|
672
672
|
// operations, to make sure we stop if the active stream has changed.
|
|
673
673
|
if (outputStream.segmentIndex instanceof shaka.media.MetaSegmentIndex) {
|
|
674
|
-
for (let i =
|
|
674
|
+
for (let i = firstNewPeriodIndex; i < streams.length; i++) {
|
|
675
675
|
const match = streams[i];
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
676
|
+
goog.asserts.assert(match.segmentIndex,
|
|
677
|
+
'stream should have a segmentIndex.');
|
|
678
|
+
if (match.segmentIndex) {
|
|
679
679
|
outputStream.segmentIndex.appendSegmentIndex(match.segmentIndex);
|
|
680
680
|
}
|
|
681
681
|
}
|
|
@@ -728,7 +728,7 @@ shaka.util.PeriodCombiner = class {
|
|
|
728
728
|
// in advance. concat() will add them to the output's MetaSegmentIndex.
|
|
729
729
|
}
|
|
730
730
|
|
|
731
|
-
if (!outputStream.matchedStreams) {
|
|
731
|
+
if (!outputStream.matchedStreams || !outputStream.matchedStreams.length) {
|
|
732
732
|
// This is not a stream we can build output from, but it may become part
|
|
733
733
|
// of another output based on another period's stream.
|
|
734
734
|
return null;
|