shaka-player 3.3.18 → 3.3.19
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/selenium-lab-tests.yaml +23 -1
- package/AUTHORS +2 -1
- package/CHANGELOG.md +22 -0
- package/CONTRIBUTORS +4 -1
- package/dist/controls.css +1 -1
- package/dist/locales.js +29 -29
- package/dist/shaka-player.compiled.d.ts +6 -2
- package/dist/shaka-player.compiled.debug.d.ts +6 -2
- package/dist/shaka-player.compiled.debug.js +132 -130
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.js +299 -297
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +6 -2
- package/dist/shaka-player.ui.debug.d.ts +6 -2
- package/dist/shaka-player.ui.debug.js +196 -194
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.js +482 -480
- package/dist/shaka-player.ui.map +3 -3
- package/docs/tutorials/fairplay.md +2 -2
- package/externs/shaka/net.js +1 -0
- package/externs/shaka/player.js +13 -7
- package/lib/ads/client_side_ad_manager.js +6 -3
- package/lib/dash/dash_parser.js +49 -48
- package/lib/media/media_source_engine.js +9 -4
- package/lib/media/streaming_engine.js +8 -3
- package/lib/net/http_fetch_plugin.js +79 -57
- package/lib/net/networking_engine.js +1 -0
- package/lib/player.js +17 -2
- package/lib/polyfill/media_capabilities.js +13 -0
- package/lib/text/mp4_vtt_parser.js +4 -0
- package/lib/text/text_engine.js +1 -0
- package/lib/util/manifest_parser_utils.js +1 -2
- package/lib/util/mime_utils.js +11 -0
- package/lib/util/periods.js +5 -5
- package/lib/util/stream_utils.js +2 -2
- 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 +54 -0
- package/test/polyfill/media_capabilities_unit.js +0 -1
- 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 +3 -0
- package/test/test/util/test_scheme.js +9 -14
- package/test/text/mp4_vtt_parser_unit.js +8 -0
- package/test/util/mime_utils_unit.js +10 -0
|
@@ -25,14 +25,14 @@ is used by the browser to generate the license request. If you don't use the
|
|
|
25
25
|
default content ID derivation, you need to specify a custom init data transform:
|
|
26
26
|
|
|
27
27
|
```js
|
|
28
|
-
player.configure('drm.initDataTransform', (initData, initDataType) => {
|
|
28
|
+
player.configure('drm.initDataTransform', (initData, initDataType, drmInfo) => {
|
|
29
29
|
if (initDataType != 'skd')
|
|
30
30
|
return initData;
|
|
31
31
|
|
|
32
32
|
// 'initData' is a buffer containing an 'skd://' URL as a UTF-8 string.
|
|
33
33
|
const skdUri = shaka.util.StringUtils.fromBytesAutoDetect(initData);
|
|
34
34
|
const contentId = getMyContentId(skdUri);
|
|
35
|
-
const cert =
|
|
35
|
+
const cert = drmInfo.serverCertificate;
|
|
36
36
|
return shaka.util.FairPlayUtils.initDataTransform(initData, contentId, cert);
|
|
37
37
|
});
|
|
38
38
|
```
|
package/externs/shaka/net.js
CHANGED
package/externs/shaka/player.js
CHANGED
|
@@ -613,9 +613,7 @@ shaka.extern.AdvancedDrmConfiguration;
|
|
|
613
613
|
* clearKeys: !Object.<string, string>,
|
|
614
614
|
* delayLicenseRequestUntilPlayed: boolean,
|
|
615
615
|
* advanced: Object.<string, shaka.extern.AdvancedDrmConfiguration>,
|
|
616
|
-
* initDataTransform:
|
|
617
|
-
* ((function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array)|
|
|
618
|
-
* undefined),
|
|
616
|
+
* initDataTransform:(shaka.extern.InitDataTransform|undefined),
|
|
619
617
|
* logLicenseExchange: boolean,
|
|
620
618
|
* updateExpirationTime: number,
|
|
621
619
|
* preferredKeySystems: !Array.<string>
|
|
@@ -639,10 +637,7 @@ shaka.extern.AdvancedDrmConfiguration;
|
|
|
639
637
|
* <i>Optional.</i> <br>
|
|
640
638
|
* A dictionary which maps key system IDs to advanced DRM configuration for
|
|
641
639
|
* those key systems.
|
|
642
|
-
* @property
|
|
643
|
-
* {((function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array)|
|
|
644
|
-
* undefined)}
|
|
645
|
-
* initDataTransform
|
|
640
|
+
* @property {shaka.extern.InitDataTransform|undefined} initDataTransform
|
|
646
641
|
* <i>Optional.</i><br>
|
|
647
642
|
* If given, this function is called with the init data from the
|
|
648
643
|
* manifest/media and should return the (possibly transformed) init data to
|
|
@@ -664,6 +659,17 @@ shaka.extern.AdvancedDrmConfiguration;
|
|
|
664
659
|
*/
|
|
665
660
|
shaka.extern.DrmConfiguration;
|
|
666
661
|
|
|
662
|
+
/**
|
|
663
|
+
* @typedef {function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array}
|
|
664
|
+
*
|
|
665
|
+
* @description
|
|
666
|
+
* A callback function to handle custom content ID signaling for FairPlay
|
|
667
|
+
* content.
|
|
668
|
+
*
|
|
669
|
+
* @exportDoc
|
|
670
|
+
*/
|
|
671
|
+
shaka.extern.InitDataTransform;
|
|
672
|
+
|
|
667
673
|
|
|
668
674
|
/**
|
|
669
675
|
* @typedef {{
|
|
@@ -212,9 +212,9 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
212
212
|
// seen the ads actually play until requestAds() is called.
|
|
213
213
|
// Note: We listen for a play event to avoid autoplay issues that might
|
|
214
214
|
// crash IMA.
|
|
215
|
-
this.
|
|
215
|
+
this.eventManager_.listenOnce(this.video_, 'play', () => {
|
|
216
216
|
this.imaAdsManager_.start();
|
|
217
|
-
}
|
|
217
|
+
});
|
|
218
218
|
} catch (adError) {
|
|
219
219
|
// If there was a problem with the VAST response,
|
|
220
220
|
// we we won't be getting an ad. Hide ad UI if we showed it already
|
|
@@ -402,7 +402,10 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
402
402
|
if (this.ad_.isLinear()) {
|
|
403
403
|
this.adContainer_.setAttribute('ad-active', 'true');
|
|
404
404
|
this.video_.pause();
|
|
405
|
-
this.ad_.setVolume(this.video_.
|
|
405
|
+
this.ad_.setVolume(this.video_.volume);
|
|
406
|
+
if (this.video_.muted) {
|
|
407
|
+
this.ad_.setMuted(true);
|
|
408
|
+
}
|
|
406
409
|
}
|
|
407
410
|
}
|
|
408
411
|
|
package/lib/dash/dash_parser.js
CHANGED
|
@@ -1278,55 +1278,56 @@ shaka.dash.DashParser = class {
|
|
|
1278
1278
|
context.period.id + ',' + context.representation.id : '';
|
|
1279
1279
|
|
|
1280
1280
|
/** @type {shaka.extern.Stream} */
|
|
1281
|
-
|
|
1282
|
-
id: this.globalId_++,
|
|
1283
|
-
originalId: context.representation.id,
|
|
1284
|
-
createSegmentIndex: async () => {
|
|
1285
|
-
// If we have a stream with the same context id stored in the map
|
|
1286
|
-
// that has no segmentIndex, we should set the segmentIndex for it.
|
|
1287
|
-
const storedInMap = contextId && context.dynamic &&
|
|
1288
|
-
this.streamMap_[contextId];
|
|
1289
|
-
|
|
1290
|
-
const currentStream = storedInMap ? this.streamMap_[contextId] : stream;
|
|
1291
|
-
if (!currentStream.segmentIndex) {
|
|
1292
|
-
currentStream.segmentIndex = await streamInfo.generateSegmentIndex();
|
|
1293
|
-
}
|
|
1294
|
-
},
|
|
1281
|
+
let stream;
|
|
1295
1282
|
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1283
|
+
if (contextId && this.streamMap_[contextId]) {
|
|
1284
|
+
stream = this.streamMap_[contextId];
|
|
1285
|
+
} else {
|
|
1286
|
+
stream = {
|
|
1287
|
+
id: this.globalId_++,
|
|
1288
|
+
originalId: context.representation.id,
|
|
1289
|
+
createSegmentIndex: () => Promise.resolve(),
|
|
1290
|
+
closeSegmentIndex: () => {
|
|
1291
|
+
if (stream.segmentIndex) {
|
|
1292
|
+
stream.segmentIndex.release();
|
|
1293
|
+
stream.segmentIndex = null;
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1296
|
+
segmentIndex: null,
|
|
1297
|
+
mimeType: context.representation.mimeType,
|
|
1298
|
+
codecs: context.representation.codecs,
|
|
1299
|
+
frameRate: context.representation.frameRate,
|
|
1300
|
+
pixelAspectRatio: context.representation.pixelAspectRatio,
|
|
1301
|
+
bandwidth: context.bandwidth,
|
|
1302
|
+
width: context.representation.width,
|
|
1303
|
+
height: context.representation.height,
|
|
1304
|
+
kind,
|
|
1305
|
+
encrypted: contentProtection.drmInfos.length > 0,
|
|
1306
|
+
drmInfos: contentProtection.drmInfos,
|
|
1307
|
+
keyIds,
|
|
1308
|
+
language,
|
|
1309
|
+
label,
|
|
1310
|
+
type: context.adaptationSet.contentType,
|
|
1311
|
+
primary: isPrimary,
|
|
1312
|
+
trickModeVideo: null,
|
|
1313
|
+
emsgSchemeIdUris:
|
|
1314
|
+
context.representation.emsgSchemeIdUris,
|
|
1315
|
+
roles,
|
|
1316
|
+
forced,
|
|
1317
|
+
channelsCount: context.representation.numChannels,
|
|
1318
|
+
audioSamplingRate: context.representation.audioSamplingRate,
|
|
1319
|
+
spatialAudio,
|
|
1320
|
+
closedCaptions,
|
|
1321
|
+
hdr,
|
|
1322
|
+
tilesLayout,
|
|
1323
|
+
matchedStreams: [],
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
stream.createSegmentIndex = async () => {
|
|
1328
|
+
if (!stream.segmentIndex) {
|
|
1329
|
+
stream.segmentIndex = await streamInfo.generateSegmentIndex();
|
|
1330
|
+
}
|
|
1330
1331
|
};
|
|
1331
1332
|
|
|
1332
1333
|
if (contextId && context.dynamic && !this.streamMap_[contextId]) {
|
|
@@ -182,6 +182,9 @@ shaka.media.MediaSourceEngine = class {
|
|
|
182
182
|
'audio/mp4; codecs="ec-3"',
|
|
183
183
|
'audio/mp4; codecs="opus"',
|
|
184
184
|
'audio/mp4; codecs="flac"',
|
|
185
|
+
'audio/mp4; codecs="dtsc"', // DTS Digital Surround
|
|
186
|
+
'audio/mp4; codecs="dtse"', // DTS Express
|
|
187
|
+
'audio/mp4; codecs="dtsx"', // DTS:X
|
|
185
188
|
// WebM types
|
|
186
189
|
'video/webm; codecs="vp8"',
|
|
187
190
|
'video/webm; codecs="vp9"',
|
|
@@ -515,7 +518,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
515
518
|
// For HLS CEA-608/708 CLOSED-CAPTIONS, text data is embedded in
|
|
516
519
|
// the video stream, so textEngine may not have been initialized.
|
|
517
520
|
if (!this.textEngine_) {
|
|
518
|
-
this.reinitText(
|
|
521
|
+
this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE);
|
|
519
522
|
}
|
|
520
523
|
|
|
521
524
|
if (transmuxedData.metadata) {
|
|
@@ -542,7 +545,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
542
545
|
contentType, () => this.append_(contentType, transmuxedSegment));
|
|
543
546
|
} else if (hasClosedCaptions) {
|
|
544
547
|
if (!this.textEngine_) {
|
|
545
|
-
this.reinitText(
|
|
548
|
+
this.reinitText(shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE);
|
|
546
549
|
}
|
|
547
550
|
// If it is the init segment for closed captions, initialize the closed
|
|
548
551
|
// caption parser.
|
|
@@ -711,8 +714,10 @@ shaka.media.MediaSourceEngine = class {
|
|
|
711
714
|
async endOfStream(reason) {
|
|
712
715
|
await this.enqueueBlockingOperation_(() => {
|
|
713
716
|
// If endOfStream() has already been called on the media source,
|
|
714
|
-
// don't call it again.
|
|
715
|
-
|
|
717
|
+
// don't call it again. Also do not call if readyState is
|
|
718
|
+
// 'closed' (not attached to video element) since it is not a
|
|
719
|
+
// valid operation.
|
|
720
|
+
if (this.ended() || this.mediaSource_.readyState === 'closed') {
|
|
716
721
|
return;
|
|
717
722
|
}
|
|
718
723
|
// 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
|
|
@@ -241,7 +246,7 @@ shaka.media.StreamingEngine = class {
|
|
|
241
246
|
const streamText =
|
|
242
247
|
textDisplayer.isTextVisible() || this.config_.alwaysStreamText;
|
|
243
248
|
|
|
244
|
-
if (streamText) {
|
|
249
|
+
if (streamText && (this.textStreamSequenceId_ == currentSequenceId)) {
|
|
245
250
|
const state = this.createMediaState_(stream);
|
|
246
251
|
this.mediaStates_.set(ContentType.TEXT, state);
|
|
247
252
|
this.scheduleUpdate_(state, 0);
|
|
@@ -1488,10 +1493,10 @@ shaka.media.StreamingEngine = class {
|
|
|
1488
1493
|
'waiting for another stream to recover...');
|
|
1489
1494
|
}
|
|
1490
1495
|
|
|
1491
|
-
// QuotaExceededError gets thrown if
|
|
1496
|
+
// QuotaExceededError gets thrown if eviction didn't help to make room
|
|
1492
1497
|
// for a segment. We want to wait for a while (4 seconds is just an
|
|
1493
1498
|
// arbitrary number) before updating to give the playhead a chance to
|
|
1494
|
-
// advance, so we don't
|
|
1499
|
+
// advance, so we don't immediately throw again.
|
|
1495
1500
|
this.scheduleUpdate_(mediaState, 4);
|
|
1496
1501
|
}
|
|
1497
1502
|
|
|
@@ -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
|
};
|
|
@@ -309,6 +309,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
309
309
|
if (!responseAndGotProgress.gotProgress &&
|
|
310
310
|
this.onProgressUpdated_ &&
|
|
311
311
|
!response.fromCache &&
|
|
312
|
+
request.method != 'HEAD' &&
|
|
312
313
|
type == shaka.net.NetworkingEngine.RequestType.SEGMENT) {
|
|
313
314
|
this.onProgressUpdated_(response.timeMs, response.data.byteLength);
|
|
314
315
|
}
|
package/lib/player.js
CHANGED
|
@@ -1225,6 +1225,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1225
1225
|
*/
|
|
1226
1226
|
shouldUseSrcEquals_(payload) {
|
|
1227
1227
|
const Platform = shaka.util.Platform;
|
|
1228
|
+
const MimeUtils = shaka.util.MimeUtils;
|
|
1228
1229
|
|
|
1229
1230
|
// If we are using a platform that does not support media source, we will
|
|
1230
1231
|
// fall back to src= to handle all playback.
|
|
@@ -1279,7 +1280,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1279
1280
|
// version there.
|
|
1280
1281
|
|
|
1281
1282
|
// Native HLS can be preferred on any platform via this flag:
|
|
1282
|
-
if (
|
|
1283
|
+
if (MimeUtils.isHlsType(mimeType) &&
|
|
1284
|
+
this.config_.streaming.preferNativeHls) {
|
|
1283
1285
|
return true;
|
|
1284
1286
|
}
|
|
1285
1287
|
|
|
@@ -4062,6 +4064,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4062
4064
|
firstVariantWithLabel.language, '', 0, label);
|
|
4063
4065
|
|
|
4064
4066
|
this.chooseVariantAndSwitch_();
|
|
4067
|
+
} else if (this.video_ && this.video_.audioTracks) {
|
|
4068
|
+
const audioTracks = Array.from(this.video_.audioTracks);
|
|
4069
|
+
|
|
4070
|
+
let trackMatch = null;
|
|
4071
|
+
|
|
4072
|
+
for (const audioTrack of audioTracks) {
|
|
4073
|
+
if (audioTrack.label == label) {
|
|
4074
|
+
trackMatch = audioTrack;
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
if (trackMatch) {
|
|
4078
|
+
this.switchHtml5Track_(trackMatch);
|
|
4079
|
+
}
|
|
4065
4080
|
}
|
|
4066
4081
|
}
|
|
4067
4082
|
|
|
@@ -6665,7 +6680,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6665
6680
|
* @define {string} A version number taken from git at compile time.
|
|
6666
6681
|
* @export
|
|
6667
6682
|
*/
|
|
6668
|
-
shaka.Player.version = 'v3.3.
|
|
6683
|
+
shaka.Player.version = 'v3.3.19-uncompiled';
|
|
6669
6684
|
|
|
6670
6685
|
// Initialize the deprecation system using the version string we just set
|
|
6671
6686
|
// on the player.
|
|
@@ -101,6 +101,19 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
101
101
|
if (shaka.util.Platform.isChromecast()) {
|
|
102
102
|
isSupported =
|
|
103
103
|
shaka.polyfill.MediaCapabilities.canCastDisplayType_(videoConfig);
|
|
104
|
+
} else if (shaka.util.Platform.isTizen()) {
|
|
105
|
+
let extendedType = videoConfig.contentType;
|
|
106
|
+
if (videoConfig.width && videoConfig.height) {
|
|
107
|
+
extendedType += `; width=${videoConfig.width}`;
|
|
108
|
+
extendedType += `; height=${videoConfig.height}`;
|
|
109
|
+
}
|
|
110
|
+
if (videoConfig.framerate) {
|
|
111
|
+
extendedType += `; framerate=${videoConfig.framerate}`;
|
|
112
|
+
}
|
|
113
|
+
if (videoConfig.bitrate) {
|
|
114
|
+
extendedType += `; bitrate=${videoConfig.bitrate}`;
|
|
115
|
+
}
|
|
116
|
+
isSupported = MediaSource.isTypeSupported(extendedType);
|
|
104
117
|
} else {
|
|
105
118
|
isSupported = MediaSource.isTypeSupported(videoConfig.contentType);
|
|
106
119
|
}
|
package/lib/text/text_engine.js
CHANGED
|
@@ -134,6 +134,7 @@ shaka.text.TextEngine = class {
|
|
|
134
134
|
// into TextEngine and TextDisplayer.
|
|
135
135
|
if (mimeType == shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE ||
|
|
136
136
|
mimeType == shaka.util.MimeUtils.CEA708_CLOSED_CAPTION_MIMETYPE) {
|
|
137
|
+
this.parser_ = null;
|
|
137
138
|
return;
|
|
138
139
|
}
|
|
139
140
|
|
|
@@ -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
|
@@ -194,6 +194,17 @@ shaka.util.MimeUtils = class {
|
|
|
194
194
|
return value;
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Checks if the given MIME type is HLS MIME type.
|
|
199
|
+
*
|
|
200
|
+
* @param {string} mimeType
|
|
201
|
+
* @return {boolean}
|
|
202
|
+
*/
|
|
203
|
+
static isHlsType(mimeType) {
|
|
204
|
+
return mimeType === 'application/x-mpegurl' ||
|
|
205
|
+
mimeType === 'application/vnd.apple.mpegurl';
|
|
206
|
+
}
|
|
207
|
+
|
|
197
208
|
/**
|
|
198
209
|
* Get the base and profile of a codec string. Where [0] will be the codec
|
|
199
210
|
* 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;
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -821,7 +821,7 @@ shaka.util.StreamUtils = class {
|
|
|
821
821
|
const video = variant.video;
|
|
822
822
|
if (audio && currentVariant && currentVariant.audio) {
|
|
823
823
|
if (!StreamUtils.areStreamsCompatible_(audio, currentVariant.audio)) {
|
|
824
|
-
shaka.log.debug('
|
|
824
|
+
shaka.log.debug('Dropping variant - not compatible with active audio',
|
|
825
825
|
'active audio',
|
|
826
826
|
StreamUtils.getStreamSummaryString_(currentVariant.audio),
|
|
827
827
|
'variant.audio',
|
|
@@ -832,7 +832,7 @@ shaka.util.StreamUtils = class {
|
|
|
832
832
|
|
|
833
833
|
if (video && currentVariant && currentVariant.video) {
|
|
834
834
|
if (!StreamUtils.areStreamsCompatible_(video, currentVariant.video)) {
|
|
835
|
-
shaka.log.debug('
|
|
835
|
+
shaka.log.debug('Dropping variant - not compatible with active video',
|
|
836
836
|
'active video',
|
|
837
837
|
StreamUtils.getStreamSummaryString_(currentVariant.video),
|
|
838
838
|
'variant.video',
|
package/maintained-branches.md
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
We are currently maintaining the following branches of Shaka Player:
|
|
4
4
|
|
|
5
|
-
- v4.
|
|
6
|
-
- v4.
|
|
7
|
-
-
|
|
8
|
-
- v3.2 (in use by the Cast Application Framework)
|
|
5
|
+
- v4.3 (latest, also in use by the Cast Application Framework)
|
|
6
|
+
- v4.2 (previous)
|
|
7
|
+
- No active LTS branches at this time
|
|
9
8
|
|
|
10
9
|
Other branches are no longer receiving bug fixes, and we recommend you upgrade
|
|
11
10
|
to a maintained branch. For details on upgrading, please see our
|