shaka-player 4.10.11 → 4.10.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/deps.js +3 -3
- package/dist/locales.js +34 -34
- package/dist/shaka-player.compiled.d.ts +1 -1
- package/dist/shaka-player.compiled.debug.d.ts +1 -1
- package/dist/shaka-player.compiled.debug.externs.js +4 -3
- package/dist/shaka-player.compiled.debug.js +318 -316
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +4 -3
- package/dist/shaka-player.compiled.js +1187 -1185
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +1 -1
- package/dist/shaka-player.ui.debug.d.ts +1 -1
- package/dist/shaka-player.ui.debug.externs.js +4 -3
- package/dist/shaka-player.ui.debug.js +74 -72
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +4 -3
- package/dist/shaka-player.ui.js +1408 -1406
- package/dist/shaka-player.ui.map +3 -3
- package/lib/cea/mp4_cea_parser.js +10 -5
- package/lib/config/codec_switching_strategy.js +14 -10
- package/lib/dash/dash_parser.js +83 -48
- package/lib/hls/hls_parser.js +3 -0
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/segment_utils.js +6 -2
- package/lib/media/streaming_engine.js +30 -36
- package/lib/player.js +24 -8
- package/lib/util/mime_utils.js +6 -0
- package/lib/util/periods.js +16 -9
- package/lib/util/stream_utils.js +2 -0
- package/lib/util/tXml.js +66 -26
- package/package.json +1 -1
|
@@ -105,14 +105,16 @@ shaka.cea.Mp4CeaParser = class {
|
|
|
105
105
|
// These are the various boxes that signal a codec.
|
|
106
106
|
.box('avc1', codecBoxParser)
|
|
107
107
|
.box('avc3', codecBoxParser)
|
|
108
|
-
.box('hev1', codecBoxParser)
|
|
109
|
-
.box('hvc1', codecBoxParser)
|
|
110
108
|
.box('dvav', codecBoxParser)
|
|
111
109
|
.box('dva1', codecBoxParser)
|
|
110
|
+
.box('hev1', codecBoxParser)
|
|
111
|
+
.box('hvc1', codecBoxParser)
|
|
112
112
|
.box('dvh1', codecBoxParser)
|
|
113
113
|
.box('dvhe', codecBoxParser)
|
|
114
114
|
.box('vvc1', codecBoxParser)
|
|
115
115
|
.box('vvi1', codecBoxParser)
|
|
116
|
+
.box('dvc1', codecBoxParser)
|
|
117
|
+
.box('dvi1', codecBoxParser)
|
|
116
118
|
|
|
117
119
|
// This signals an encrypted sample, which we can go inside of to find
|
|
118
120
|
// the codec used.
|
|
@@ -406,18 +408,21 @@ shaka.cea.Mp4CeaParser.CodecBitstreamMap_ = {
|
|
|
406
408
|
// AVC
|
|
407
409
|
'avc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
|
|
408
410
|
'avc3': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
|
|
409
|
-
// HEVC
|
|
410
|
-
'hev1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
|
|
411
|
-
'hvc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
|
|
412
411
|
// Dolby Vision based in AVC
|
|
413
412
|
'dvav': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
|
|
414
413
|
'dva1': shaka.cea.Mp4CeaParser.BitstreamFormat.H264,
|
|
414
|
+
// HEVC
|
|
415
|
+
'hev1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
|
|
416
|
+
'hvc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
|
|
415
417
|
// Dolby Vision based in HEVC
|
|
416
418
|
'dvh1': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
|
|
417
419
|
'dvhe': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
|
|
418
420
|
// VVC
|
|
419
421
|
'vvc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H266,
|
|
420
422
|
'vvi1': shaka.cea.Mp4CeaParser.BitstreamFormat.H266,
|
|
423
|
+
// Dolby Vision based in VVC
|
|
424
|
+
'dvc1': shaka.cea.Mp4CeaParser.BitstreamFormat.H266,
|
|
425
|
+
'dvi1': shaka.cea.Mp4CeaParser.BitstreamFormat.H266,
|
|
421
426
|
};
|
|
422
427
|
|
|
423
428
|
/**
|
|
@@ -11,14 +11,18 @@ goog.provide('shaka.config.CodecSwitchingStrategy');
|
|
|
11
11
|
* @export
|
|
12
12
|
*/
|
|
13
13
|
shaka.config.CodecSwitchingStrategy = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Allow codec switching which will always involve reloading
|
|
16
|
+
* the <code<MediaSource</code>.
|
|
17
|
+
*/
|
|
18
|
+
'RELOAD': 'reload',
|
|
19
|
+
/**
|
|
20
|
+
* Allow codec switching; determine if <code>SourceBuffer.changeType</code>
|
|
21
|
+
* is available and attempt to use this first, but fall back to reloading
|
|
22
|
+
* <code>MediaSource</code> if not available.
|
|
23
|
+
* <br>
|
|
24
|
+
* Note: Some devices that support <code>SourceBuffer.changeType</code> can
|
|
25
|
+
* become stuck in a pause state.
|
|
26
|
+
*/
|
|
27
|
+
'SMOOTH': 'smooth',
|
|
24
28
|
};
|
package/lib/dash/dash_parser.js
CHANGED
|
@@ -30,6 +30,7 @@ goog.require('shaka.util.LanguageUtils');
|
|
|
30
30
|
goog.require('shaka.util.ManifestParserUtils');
|
|
31
31
|
goog.require('shaka.util.MimeUtils');
|
|
32
32
|
goog.require('shaka.util.Networking');
|
|
33
|
+
goog.require('shaka.util.ObjectUtils');
|
|
33
34
|
goog.require('shaka.util.OperationManager');
|
|
34
35
|
goog.require('shaka.util.PeriodCombiner');
|
|
35
36
|
goog.require('shaka.util.PlayerConfiguration');
|
|
@@ -93,12 +94,19 @@ shaka.dash.DashParser = class {
|
|
|
93
94
|
|
|
94
95
|
/**
|
|
95
96
|
* A map of IDs to Stream objects.
|
|
96
|
-
* ID: Period@id,
|
|
97
|
-
* e.g.: '1,
|
|
97
|
+
* ID: Period@id,Representation@id
|
|
98
|
+
* e.g.: '1,23'
|
|
98
99
|
* @private {!Object.<string, !shaka.extern.Stream>}
|
|
99
100
|
*/
|
|
100
101
|
this.streamMap_ = {};
|
|
101
102
|
|
|
103
|
+
/**
|
|
104
|
+
* A map of Period IDs to Stream Map IDs.
|
|
105
|
+
* Use to have direct access to streamMap key.
|
|
106
|
+
* @private {!Object.<string, !Array<string>>}
|
|
107
|
+
*/
|
|
108
|
+
this.indexStreamMap_ = {};
|
|
109
|
+
|
|
102
110
|
/**
|
|
103
111
|
* A map of period ids to their durations
|
|
104
112
|
* @private {!Object.<string, number>}
|
|
@@ -256,6 +264,7 @@ shaka.dash.DashParser = class {
|
|
|
256
264
|
this.manifestUris_ = [];
|
|
257
265
|
this.manifest_ = null;
|
|
258
266
|
this.streamMap_ = {};
|
|
267
|
+
this.indexStreamMap_ = {};
|
|
259
268
|
this.contextCache_.clear();
|
|
260
269
|
this.manifestPatchContext_ = {
|
|
261
270
|
mpdId: '',
|
|
@@ -653,7 +662,7 @@ shaka.dash.DashParser = class {
|
|
|
653
662
|
}
|
|
654
663
|
|
|
655
664
|
this.manifestPatchContext_.type = mpdType;
|
|
656
|
-
|
|
665
|
+
this.cleanStreamMap_();
|
|
657
666
|
/** @type {!shaka.media.PresentationTimeline} */
|
|
658
667
|
let presentationTimeline;
|
|
659
668
|
if (this.manifest_) {
|
|
@@ -734,7 +743,8 @@ shaka.dash.DashParser = class {
|
|
|
734
743
|
};
|
|
735
744
|
|
|
736
745
|
this.gapCount_ = 0;
|
|
737
|
-
const periodsAndDuration = this.parsePeriods_(
|
|
746
|
+
const periodsAndDuration = this.parsePeriods_(
|
|
747
|
+
context, getBaseUris, mpd, /* newPeriod= */ false);
|
|
738
748
|
const duration = periodsAndDuration.duration;
|
|
739
749
|
const periods = periodsAndDuration.periods;
|
|
740
750
|
|
|
@@ -1013,7 +1023,7 @@ shaka.dash.DashParser = class {
|
|
|
1013
1023
|
};
|
|
1014
1024
|
|
|
1015
1025
|
const periodsAndDuration = this.parsePeriods_(context,
|
|
1016
|
-
this.manifestPatchContext_.getBaseUris, periods);
|
|
1026
|
+
this.manifestPatchContext_.getBaseUris, periods, /* newPeriod= */ true);
|
|
1017
1027
|
|
|
1018
1028
|
return periodsAndDuration.periods;
|
|
1019
1029
|
}
|
|
@@ -1033,6 +1043,10 @@ shaka.dash.DashParser = class {
|
|
|
1033
1043
|
this.contextCache_.delete(contextId);
|
|
1034
1044
|
}
|
|
1035
1045
|
}
|
|
1046
|
+
const newPeriods = this.lastManifestUpdatePeriodIds_.filter((pID) => {
|
|
1047
|
+
return pID !== periodId;
|
|
1048
|
+
});
|
|
1049
|
+
this.lastManifestUpdatePeriodIds_ = newPeriods;
|
|
1036
1050
|
}
|
|
1037
1051
|
|
|
1038
1052
|
/**
|
|
@@ -1274,6 +1288,7 @@ shaka.dash.DashParser = class {
|
|
|
1274
1288
|
* @param {shaka.dash.DashParser.Context} context
|
|
1275
1289
|
* @param {function():!Array.<string>} getBaseUris
|
|
1276
1290
|
* @param {!shaka.extern.xml.Node} mpd
|
|
1291
|
+
* @param {!boolean} newPeriod
|
|
1277
1292
|
* @return {{
|
|
1278
1293
|
* periods: !Array.<shaka.extern.Period>,
|
|
1279
1294
|
* duration: ?number,
|
|
@@ -1281,7 +1296,7 @@ shaka.dash.DashParser = class {
|
|
|
1281
1296
|
* }}
|
|
1282
1297
|
* @private
|
|
1283
1298
|
*/
|
|
1284
|
-
parsePeriods_(context, getBaseUris, mpd) {
|
|
1299
|
+
parsePeriods_(context, getBaseUris, mpd, newPeriod) {
|
|
1285
1300
|
const TXml = shaka.util.TXml;
|
|
1286
1301
|
let presentationDuration = context.mediaPresentationDuration;
|
|
1287
1302
|
|
|
@@ -1413,8 +1428,18 @@ shaka.dash.DashParser = class {
|
|
|
1413
1428
|
prevEnd = start + periodDuration;
|
|
1414
1429
|
} // end of period parsing loop
|
|
1415
1430
|
|
|
1416
|
-
|
|
1417
|
-
|
|
1431
|
+
if (newPeriod) {
|
|
1432
|
+
// append new period from the patch manifest
|
|
1433
|
+
for (const el of periods) {
|
|
1434
|
+
const periodID = el.id;
|
|
1435
|
+
if (!this.lastManifestUpdatePeriodIds_.includes(periodID)) {
|
|
1436
|
+
this.lastManifestUpdatePeriodIds_.push(periodID);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
} else {
|
|
1440
|
+
// Replace previous seen periods with the current one.
|
|
1441
|
+
this.lastManifestUpdatePeriodIds_ = periods.map((el) => el.id);
|
|
1442
|
+
}
|
|
1418
1443
|
|
|
1419
1444
|
if (presentationDuration != null) {
|
|
1420
1445
|
if (prevEnd != null) {
|
|
@@ -1442,6 +1467,26 @@ shaka.dash.DashParser = class {
|
|
|
1442
1467
|
}
|
|
1443
1468
|
}
|
|
1444
1469
|
|
|
1470
|
+
/**
|
|
1471
|
+
* Clean StreamMap Object to remove reference of deleted Stream Object
|
|
1472
|
+
* @private
|
|
1473
|
+
*/
|
|
1474
|
+
cleanStreamMap_() {
|
|
1475
|
+
const oldPeriodIds = Object.keys(this.indexStreamMap_);
|
|
1476
|
+
if (oldPeriodIds.length === this.lastManifestUpdatePeriodIds_.length) {
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
const diffPeriodsIDs = oldPeriodIds.filter((pId) => {
|
|
1480
|
+
return !this.lastManifestUpdatePeriodIds_.includes(pId);
|
|
1481
|
+
});
|
|
1482
|
+
for (const pId of diffPeriodsIDs) {
|
|
1483
|
+
for (const contextId of this.indexStreamMap_[pId]) {
|
|
1484
|
+
delete this.streamMap_[contextId];
|
|
1485
|
+
}
|
|
1486
|
+
delete this.indexStreamMap_[pId];
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1445
1490
|
/**
|
|
1446
1491
|
* Parses a Period XML element. Unlike the other parse methods, this is not
|
|
1447
1492
|
* given the Node; it is given a PeriodInfo structure. Also, partial parsing
|
|
@@ -1458,6 +1503,7 @@ shaka.dash.DashParser = class {
|
|
|
1458
1503
|
const TXml = shaka.util.TXml;
|
|
1459
1504
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
1460
1505
|
|
|
1506
|
+
goog.asserts.assert(periodInfo.node, 'periodInfo.node should exist');
|
|
1461
1507
|
context.period = this.createFrame_(periodInfo.node, null, getBaseUris);
|
|
1462
1508
|
context.periodInfo = periodInfo;
|
|
1463
1509
|
context.period.availabilityTimeOffset = context.availabilityTimeOffset;
|
|
@@ -2221,7 +2267,12 @@ shaka.dash.DashParser = class {
|
|
|
2221
2267
|
};
|
|
2222
2268
|
|
|
2223
2269
|
if (contextId && context.dynamic && !this.streamMap_[contextId]) {
|
|
2270
|
+
const periodId = context.period.id || '';
|
|
2271
|
+
if (!this.indexStreamMap_[periodId]) {
|
|
2272
|
+
this.indexStreamMap_[periodId] = [];
|
|
2273
|
+
}
|
|
2224
2274
|
this.streamMap_[contextId] = stream;
|
|
2275
|
+
this.indexStreamMap_[periodId].push(contextId);
|
|
2225
2276
|
}
|
|
2226
2277
|
|
|
2227
2278
|
return stream;
|
|
@@ -2235,45 +2286,29 @@ shaka.dash.DashParser = class {
|
|
|
2235
2286
|
* @private
|
|
2236
2287
|
*/
|
|
2237
2288
|
cloneContext_(context) {
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
segmentBase: null,
|
|
2246
|
-
segmentList: null,
|
|
2247
|
-
segmentTemplate: frameRef.segmentTemplate,
|
|
2248
|
-
getBaseUris: frameRef.getBaseUris,
|
|
2249
|
-
width: frameRef.width,
|
|
2250
|
-
height: frameRef.height,
|
|
2251
|
-
contentType: frameRef.contentType,
|
|
2252
|
-
mimeType: frameRef.mimeType,
|
|
2253
|
-
language: frameRef.language,
|
|
2254
|
-
codecs: frameRef.codecs,
|
|
2255
|
-
frameRate: frameRef.frameRate,
|
|
2256
|
-
pixelAspectRatio: frameRef.pixelAspectRatio,
|
|
2257
|
-
emsgSchemeIdUris: frameRef.emsgSchemeIdUris,
|
|
2258
|
-
id: frameRef.id,
|
|
2259
|
-
position: frameRef.position,
|
|
2260
|
-
numChannels: frameRef.numChannels,
|
|
2261
|
-
audioSamplingRate: frameRef.audioSamplingRate,
|
|
2262
|
-
availabilityTimeOffset: frameRef.availabilityTimeOffset,
|
|
2263
|
-
initialization: frameRef.initialization,
|
|
2264
|
-
};
|
|
2265
|
-
} else if (k == 'periodInfo') {
|
|
2266
|
-
/** @type {shaka.dash.DashParser.PeriodInfo} */
|
|
2267
|
-
const frameRef = context[k];
|
|
2268
|
-
contextClone[k] = {
|
|
2269
|
-
start: frameRef.start,
|
|
2270
|
-
duration: frameRef.duration,
|
|
2271
|
-
node: null,
|
|
2272
|
-
isLastPeriod: frameRef.isLastPeriod,
|
|
2273
|
-
};
|
|
2274
|
-
} else {
|
|
2275
|
-
contextClone[k] = context[k];
|
|
2289
|
+
/**
|
|
2290
|
+
* @param {?shaka.dash.DashParser.InheritanceFrame} frame
|
|
2291
|
+
* @return {?shaka.dash.DashParser.InheritanceFrame}
|
|
2292
|
+
*/
|
|
2293
|
+
const cloneFrame = (frame) => {
|
|
2294
|
+
if (!frame) {
|
|
2295
|
+
return null;
|
|
2276
2296
|
}
|
|
2297
|
+
const clone = shaka.util.ObjectUtils.shallowCloneObject(frame);
|
|
2298
|
+
clone.segmentBase = null;
|
|
2299
|
+
clone.segmentList = null;
|
|
2300
|
+
clone.segmentTemplate = shaka.util.TXml.cloneNode(clone.segmentTemplate);
|
|
2301
|
+
return clone;
|
|
2302
|
+
};
|
|
2303
|
+
const contextClone = shaka.util.ObjectUtils.shallowCloneObject(context);
|
|
2304
|
+
contextClone.period = cloneFrame(contextClone.period);
|
|
2305
|
+
contextClone.adaptationSet = cloneFrame(contextClone.adaptationSet);
|
|
2306
|
+
contextClone.representation = cloneFrame(contextClone.representation);
|
|
2307
|
+
|
|
2308
|
+
if (contextClone.periodInfo) {
|
|
2309
|
+
contextClone.periodInfo =
|
|
2310
|
+
shaka.util.ObjectUtils.shallowCloneObject(contextClone.periodInfo);
|
|
2311
|
+
contextClone.periodInfo.node = null;
|
|
2277
2312
|
}
|
|
2278
2313
|
|
|
2279
2314
|
return contextClone;
|
|
@@ -3137,7 +3172,7 @@ shaka.dash.DashParser.Context;
|
|
|
3137
3172
|
* @typedef {{
|
|
3138
3173
|
* start: number,
|
|
3139
3174
|
* duration: ?number,
|
|
3140
|
-
* node:
|
|
3175
|
+
* node: ?shaka.extern.xml.Node,
|
|
3141
3176
|
* isLastPeriod: boolean
|
|
3142
3177
|
* }}
|
|
3143
3178
|
*
|
|
@@ -3149,7 +3184,7 @@ shaka.dash.DashParser.Context;
|
|
|
3149
3184
|
* @property {?number} duration
|
|
3150
3185
|
* The duration of the period; or null if the duration is not given. This
|
|
3151
3186
|
* will be non-null for all periods except the last.
|
|
3152
|
-
* @property {
|
|
3187
|
+
* @property {?shaka.extern.xml.Node} node
|
|
3153
3188
|
* The XML Node for the Period.
|
|
3154
3189
|
* @property {boolean} isLastPeriod
|
|
3155
3190
|
* Whether this Period is the last one in the manifest.
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -736,6 +736,9 @@ shaka.hls.HlsParser = class {
|
|
|
736
736
|
// also start 6 seconds apart in presentation time.
|
|
737
737
|
|
|
738
738
|
const segment0 = segmentIndex.earliestReference();
|
|
739
|
+
if (!segment0) {
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
739
742
|
if (segment0.syncTime == null) {
|
|
740
743
|
shaka.log.alwaysError('Missing EXT-X-PROGRAM-DATE-TIME for stream',
|
|
741
744
|
streamInfo.getUris(),
|
|
@@ -95,6 +95,18 @@ shaka.media.ContentWorkarounds = class {
|
|
|
95
95
|
newType: ContentWorkarounds.BOX_TYPE_ENCV_,
|
|
96
96
|
});
|
|
97
97
|
})
|
|
98
|
+
.fullBox('dvc1', (box) => {
|
|
99
|
+
boxesToModify.push({
|
|
100
|
+
box,
|
|
101
|
+
newType: ContentWorkarounds.BOX_TYPE_ENCV_,
|
|
102
|
+
});
|
|
103
|
+
})
|
|
104
|
+
.fullBox('dvi1', (box) => {
|
|
105
|
+
boxesToModify.push({
|
|
106
|
+
box,
|
|
107
|
+
newType: ContentWorkarounds.BOX_TYPE_ENCV_,
|
|
108
|
+
});
|
|
109
|
+
})
|
|
98
110
|
.fullBox('hev1', (box) => {
|
|
99
111
|
boxesToModify.push({
|
|
100
112
|
box,
|
|
@@ -493,9 +493,13 @@ shaka.media.SegmentUtils = class {
|
|
|
493
493
|
return codecs;
|
|
494
494
|
}
|
|
495
495
|
const dolbyVision = codecs.find((codec) => {
|
|
496
|
-
return codec.startsWith('
|
|
496
|
+
return codec.startsWith('dvav.') ||
|
|
497
|
+
codec.startsWith('dva1.') ||
|
|
498
|
+
codec.startsWith('dvh1.') ||
|
|
497
499
|
codec.startsWith('dvhe.') ||
|
|
498
|
-
codec.startsWith('dav1.')
|
|
500
|
+
codec.startsWith('dav1.') ||
|
|
501
|
+
codec.startsWith('dvc1.') ||
|
|
502
|
+
codec.startsWith('dvi1.');
|
|
499
503
|
});
|
|
500
504
|
if (!dolbyVision) {
|
|
501
505
|
return codecs;
|
|
@@ -15,10 +15,9 @@ goog.require('shaka.log');
|
|
|
15
15
|
goog.require('shaka.media.InitSegmentReference');
|
|
16
16
|
goog.require('shaka.media.ManifestParser');
|
|
17
17
|
goog.require('shaka.media.MediaSourceEngine');
|
|
18
|
-
goog.require('shaka.media.SegmentIndex');
|
|
19
18
|
goog.require('shaka.media.SegmentIterator');
|
|
20
|
-
goog.require('shaka.media.SegmentPrefetch');
|
|
21
19
|
goog.require('shaka.media.SegmentReference');
|
|
20
|
+
goog.require('shaka.media.SegmentPrefetch');
|
|
22
21
|
goog.require('shaka.media.SegmentUtils');
|
|
23
22
|
goog.require('shaka.net.Backoff');
|
|
24
23
|
goog.require('shaka.net.NetworkingEngine');
|
|
@@ -75,16 +74,9 @@ shaka.media.StreamingEngine = class {
|
|
|
75
74
|
this.config_ = null;
|
|
76
75
|
|
|
77
76
|
/**
|
|
78
|
-
* Retains
|
|
79
|
-
* away from during an ongoing
|
|
80
|
-
*
|
|
81
|
-
* a stream's content type occurs.
|
|
82
|
-
* This is because at that point, it is guaranteed no logic is actively
|
|
83
|
-
* using information for these old streams.
|
|
84
|
-
*
|
|
85
|
-
* Lastly, the operations are stored in arrays in the off chance multiple
|
|
86
|
-
* ABR switches happen during an ongoing fetchAndAppend_().
|
|
87
|
-
* @private {!Map.<string, !Array.<?shaka.media.SegmentIndex>>}
|
|
77
|
+
* Retains a reference to the function used to close SegmentIndex objects
|
|
78
|
+
* for streams which were switched away from during an ongoing update_().
|
|
79
|
+
* @private {!Map.<string, !function()>}
|
|
88
80
|
*/
|
|
89
81
|
this.deferredCloseSegmentIndex_ = new Map();
|
|
90
82
|
|
|
@@ -176,7 +168,6 @@ shaka.media.StreamingEngine = class {
|
|
|
176
168
|
state.segmentPrefetch.clearAll();
|
|
177
169
|
state.segmentPrefetch = null;
|
|
178
170
|
}
|
|
179
|
-
this.handleDeferredCloseSegmentIndexes_(state);
|
|
180
171
|
}
|
|
181
172
|
for (const prefetch of this.audioPrefetchMap_.values()) {
|
|
182
173
|
prefetch.clearAll();
|
|
@@ -186,7 +177,6 @@ shaka.media.StreamingEngine = class {
|
|
|
186
177
|
|
|
187
178
|
this.mediaStates_.clear();
|
|
188
179
|
this.audioPrefetchMap_.clear();
|
|
189
|
-
this.deferredCloseSegmentIndex_.clear();
|
|
190
180
|
|
|
191
181
|
this.playerInterface_ = null;
|
|
192
182
|
this.manifest_ = null;
|
|
@@ -530,14 +520,9 @@ shaka.media.StreamingEngine = class {
|
|
|
530
520
|
handleDeferredCloseSegmentIndexes_(mediaState) {
|
|
531
521
|
for (const [key, value] of this.deferredCloseSegmentIndex_.entries()) {
|
|
532
522
|
const streamId = /** @type {string} */ (key);
|
|
533
|
-
const
|
|
534
|
-
/** @type {!Array.<?shaka.media.SegmentIndex>} */ (value);
|
|
523
|
+
const closeSegmentIndex = /** @type {!function()} */ (value);
|
|
535
524
|
if (streamId.includes(mediaState.type)) {
|
|
536
|
-
|
|
537
|
-
if (segmentIndex) {
|
|
538
|
-
segmentIndex.release();
|
|
539
|
-
}
|
|
540
|
-
}
|
|
525
|
+
closeSegmentIndex();
|
|
541
526
|
this.deferredCloseSegmentIndex_.delete(streamId);
|
|
542
527
|
}
|
|
543
528
|
}
|
|
@@ -612,26 +597,23 @@ shaka.media.StreamingEngine = class {
|
|
|
612
597
|
fullMimeType, this.manifest_.sequenceMode, stream.external);
|
|
613
598
|
}
|
|
614
599
|
|
|
615
|
-
// Releases the segmentIndex of the old stream
|
|
600
|
+
// Releases the segmentIndex of the old stream.
|
|
616
601
|
// Do not close segment indexes we are prefetching.
|
|
617
602
|
if (!this.audioPrefetchMap_.has(mediaState.stream)) {
|
|
618
603
|
if (mediaState.stream.closeSegmentIndex) {
|
|
619
604
|
if (mediaState.performingUpdate) {
|
|
620
605
|
const oldStreamTag =
|
|
621
606
|
shaka.media.StreamingEngine.logPrefix_(mediaState);
|
|
622
|
-
// The ongoing update is still using the old stream's segment
|
|
623
|
-
// reference information.
|
|
624
|
-
// If we close the old stream now, the update will not complete
|
|
625
|
-
// correctly.
|
|
626
|
-
// onUpdate_() will resume the closeSegmentIndex() operation for the
|
|
627
|
-
// old stream when the update has finished.
|
|
628
607
|
if (!this.deferredCloseSegmentIndex_.has(oldStreamTag)) {
|
|
608
|
+
// The ongoing update is still using the old stream's segment
|
|
609
|
+
// reference information.
|
|
610
|
+
// If we close the old stream now, the update will not complete
|
|
611
|
+
// correctly.
|
|
612
|
+
// The next onUpdate_() for this content type will resume the
|
|
613
|
+
// closeSegmentIndex() operation for the old stream once the ongoing
|
|
614
|
+
// update has finished, then immediately create a new segment index.
|
|
629
615
|
this.deferredCloseSegmentIndex_.set(
|
|
630
|
-
oldStreamTag,
|
|
631
|
-
} else {
|
|
632
|
-
// We switched from stream A -> B -> back to A.
|
|
633
|
-
this.deferredCloseSegmentIndex_.get(oldStreamTag).push(
|
|
634
|
-
mediaState.stream.segmentIndex);
|
|
616
|
+
oldStreamTag, mediaState.stream.closeSegmentIndex);
|
|
635
617
|
}
|
|
636
618
|
} else {
|
|
637
619
|
mediaState.stream.closeSegmentIndex();
|
|
@@ -643,8 +625,8 @@ shaka.media.StreamingEngine = class {
|
|
|
643
625
|
mediaState.segmentIterator = null;
|
|
644
626
|
mediaState.adaptation = !!adaptation;
|
|
645
627
|
|
|
646
|
-
const
|
|
647
|
-
shaka.log.debug('switch: switching to Stream ' +
|
|
628
|
+
const streamTag = shaka.media.StreamingEngine.logPrefix_(mediaState);
|
|
629
|
+
shaka.log.debug('switch: switching to Stream ' + streamTag);
|
|
648
630
|
|
|
649
631
|
if (clearBuffer) {
|
|
650
632
|
if (mediaState.clearingBuffer) {
|
|
@@ -1228,6 +1210,11 @@ shaka.media.StreamingEngine = class {
|
|
|
1228
1210
|
return;
|
|
1229
1211
|
}
|
|
1230
1212
|
|
|
1213
|
+
// If stream switches happened during the previous update_() for this
|
|
1214
|
+
// content type, close out the old streams that were switched away from.
|
|
1215
|
+
// Even if we had switched away from the active stream 'A' during the
|
|
1216
|
+
// update_(), e.g. (A -> B -> A), closing 'A' is permissible here since we
|
|
1217
|
+
// will immediately re-create it in the logic below.
|
|
1231
1218
|
this.handleDeferredCloseSegmentIndexes_(mediaState);
|
|
1232
1219
|
|
|
1233
1220
|
// Make sure the segment index exists. If not, create the segment index.
|
|
@@ -1546,7 +1533,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1546
1533
|
// If there's positive drift then we need to adjust the lookup time, and
|
|
1547
1534
|
// may wind up requesting the previous segment to be safe.
|
|
1548
1535
|
// inaccurateManifestTolerance should be 0 for low latency streaming.
|
|
1549
|
-
const inaccurateTolerance = this.
|
|
1536
|
+
const inaccurateTolerance = this.manifest_.sequenceMode ?
|
|
1537
|
+
0 : this.config_.inaccurateManifestTolerance;
|
|
1550
1538
|
const lookupTime = Math.max(presentationTime - inaccurateTolerance, 0);
|
|
1551
1539
|
|
|
1552
1540
|
shaka.log.v1(logPrefix, 'looking up segment',
|
|
@@ -2086,6 +2074,8 @@ shaka.media.StreamingEngine = class {
|
|
|
2086
2074
|
.box('dva1', Mp4Parser.visualSampleEntry)
|
|
2087
2075
|
.box('dvh1', Mp4Parser.visualSampleEntry)
|
|
2088
2076
|
.box('dvhe', Mp4Parser.visualSampleEntry)
|
|
2077
|
+
.box('dvc1', Mp4Parser.visualSampleEntry)
|
|
2078
|
+
.box('dvi1', Mp4Parser.visualSampleEntry)
|
|
2089
2079
|
.box('vexu', Mp4Parser.children)
|
|
2090
2080
|
.box('proj', Mp4Parser.children)
|
|
2091
2081
|
.fullBox('prji', (box) => {
|
|
@@ -2810,6 +2800,10 @@ shaka.media.StreamingEngine = class {
|
|
|
2810
2800
|
this.destroyer_.ensureNotDestroyed();
|
|
2811
2801
|
// Try to recover from network errors
|
|
2812
2802
|
if (error.category === shaka.util.Error.Category.NETWORK) {
|
|
2803
|
+
if (mediaState.restoreStreamAfterTrickPlay) {
|
|
2804
|
+
this.setTrickPlay(/* on= */ false);
|
|
2805
|
+
return;
|
|
2806
|
+
}
|
|
2813
2807
|
const maxDisabledTime = this.getDisabledTime_(error);
|
|
2814
2808
|
error.handled = this.playerInterface_.disableStream(
|
|
2815
2809
|
mediaState.stream, maxDisabledTime);
|
package/lib/player.js
CHANGED
|
@@ -4454,9 +4454,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4454
4454
|
* beginning or end of the seekable range for the content.
|
|
4455
4455
|
*
|
|
4456
4456
|
* @param {number} rate
|
|
4457
|
+
* @param {boolean=} useTrickPlayTrack
|
|
4457
4458
|
* @export
|
|
4458
4459
|
*/
|
|
4459
|
-
trickPlay(rate) {
|
|
4460
|
+
trickPlay(rate, useTrickPlayTrack = true) {
|
|
4460
4461
|
// A playbackRate of 0 is used internally when we are in a buffering state,
|
|
4461
4462
|
// and doesn't make sense for trick play. If you set a rate of 0 for trick
|
|
4462
4463
|
// play, we will reject it and issue a warning. If it happens during a
|
|
@@ -4478,7 +4479,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4478
4479
|
|
|
4479
4480
|
if (this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE) {
|
|
4480
4481
|
this.abrManager_.playbackRateChanged(rate);
|
|
4481
|
-
this.streamingEngine_.setTrickPlay(
|
|
4482
|
+
this.streamingEngine_.setTrickPlay(
|
|
4483
|
+
useTrickPlayTrack && Math.abs(rate) > 1);
|
|
4482
4484
|
}
|
|
4483
4485
|
if (this.isLive()) {
|
|
4484
4486
|
this.trickPlayEventManager_.listen(this.video_, 'timeupdate', () => {
|
|
@@ -5561,6 +5563,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5561
5563
|
}
|
|
5562
5564
|
|
|
5563
5565
|
if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
|
|
5566
|
+
this.stats_.addBytesDownloaded(NaN);
|
|
5564
5567
|
this.stats_.setResolution(
|
|
5565
5568
|
/* width= */ element.videoWidth || NaN,
|
|
5566
5569
|
/* height= */ element.videoHeight || NaN);
|
|
@@ -6502,7 +6505,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6502
6505
|
shaka.log.debug('Buffer fullness ratio (' + bufferFullness + ') ' +
|
|
6503
6506
|
'is less than the vodDynamicPlaybackRateBufferRatio (' +
|
|
6504
6507
|
bufferThreshold + '). Updating playbackRate to ' + minPlaybackRate);
|
|
6505
|
-
this.trickPlay(minPlaybackRate);
|
|
6508
|
+
this.trickPlay(minPlaybackRate, /* useTrickPlayTrack= */ false);
|
|
6506
6509
|
}
|
|
6507
6510
|
} else if (bufferFullness == 1) {
|
|
6508
6511
|
if (playbackRate !== this.playRateController_.getDefaultRate()) {
|
|
@@ -6629,7 +6632,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6629
6632
|
timeSinceLastRebuffer + 's) ' +
|
|
6630
6633
|
'is less than the live sync panicThreshold (' + panicThreshold +
|
|
6631
6634
|
's). Updating playbackRate to ' + minPlaybackRate);
|
|
6632
|
-
this.trickPlay(minPlaybackRate);
|
|
6635
|
+
this.trickPlay(minPlaybackRate, /* useTrickPlayTrack= */ false);
|
|
6633
6636
|
}
|
|
6634
6637
|
} else if (maxLatency && maxPlaybackRate &&
|
|
6635
6638
|
(latency - offset) > maxLatency) {
|
|
@@ -6637,7 +6640,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6637
6640
|
shaka.log.debug('Latency (' + latency + 's) is greater than ' +
|
|
6638
6641
|
'live sync maxLatency (' + maxLatency + 's). ' +
|
|
6639
6642
|
'Updating playbackRate to ' + maxPlaybackRate);
|
|
6640
|
-
this.trickPlay(maxPlaybackRate);
|
|
6643
|
+
this.trickPlay(maxPlaybackRate, /* useTrickPlayTrack= */ false);
|
|
6641
6644
|
}
|
|
6642
6645
|
this.targetLatencyReached_ = null;
|
|
6643
6646
|
} else if (minLatency && minPlaybackRate &&
|
|
@@ -6646,7 +6649,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6646
6649
|
shaka.log.debug('Latency (' + latency + 's) is smaller than ' +
|
|
6647
6650
|
'live sync minLatency (' + minLatency + 's). ' +
|
|
6648
6651
|
'Updating playbackRate to ' + minPlaybackRate);
|
|
6649
|
-
this.trickPlay(minPlaybackRate);
|
|
6652
|
+
this.trickPlay(minPlaybackRate, /* useTrickPlayTrack= */ false);
|
|
6650
6653
|
}
|
|
6651
6654
|
this.targetLatencyReached_ = null;
|
|
6652
6655
|
} else if (playbackRate !== this.playRateController_.getDefaultRate()) {
|
|
@@ -7257,19 +7260,32 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
7257
7260
|
if (hasAltStream) {
|
|
7258
7261
|
let didDisableStream = false;
|
|
7259
7262
|
|
|
7263
|
+
let isTrickModeVideo = false;
|
|
7264
|
+
|
|
7260
7265
|
for (const variant of this.manifest_.variants) {
|
|
7261
7266
|
const candidate = variant[stream.type];
|
|
7262
7267
|
|
|
7263
|
-
if (candidate
|
|
7268
|
+
if (!candidate) {
|
|
7269
|
+
continue;
|
|
7270
|
+
}
|
|
7271
|
+
|
|
7272
|
+
if (candidate.id === stream.id) {
|
|
7264
7273
|
variant.disabledUntilTime = (Date.now() / 1000) + disableTime;
|
|
7265
7274
|
didDisableStream = true;
|
|
7266
7275
|
|
|
7267
7276
|
shaka.log.v2(
|
|
7268
7277
|
'Disabled stream ' + stream.type + ':' + stream.id +
|
|
7269
7278
|
' for ' + disableTime + ' seconds...');
|
|
7279
|
+
} else if (candidate.trickModeVideo &&
|
|
7280
|
+
candidate.trickModeVideo.id == stream.id) {
|
|
7281
|
+
isTrickModeVideo = true;
|
|
7270
7282
|
}
|
|
7271
7283
|
}
|
|
7272
7284
|
|
|
7285
|
+
if (!didDisableStream && isTrickModeVideo) {
|
|
7286
|
+
return false;
|
|
7287
|
+
}
|
|
7288
|
+
|
|
7273
7289
|
goog.asserts.assert(didDisableStream, 'Must have disabled stream');
|
|
7274
7290
|
|
|
7275
7291
|
this.checkVariantsTimer_.tickEvery(1);
|
|
@@ -7832,7 +7848,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
7832
7848
|
* @export
|
|
7833
7849
|
*/
|
|
7834
7850
|
// eslint-disable-next-line no-useless-concat
|
|
7835
|
-
shaka.Player.version = 'v4.10.
|
|
7851
|
+
shaka.Player.version = 'v4.10.13' + '-uncompiled'; // x-release-please-version
|
|
7836
7852
|
|
|
7837
7853
|
// Initialize the deprecation system using the version string we just set
|
|
7838
7854
|
// on the player.
|
package/lib/util/mime_utils.js
CHANGED
|
@@ -172,6 +172,9 @@ shaka.util.MimeUtils = class {
|
|
|
172
172
|
case base === 'hvc1':
|
|
173
173
|
case base === 'hev1':
|
|
174
174
|
return 'hevc'; // H265
|
|
175
|
+
case base === 'vvc1':
|
|
176
|
+
case base === 'vvi1':
|
|
177
|
+
return 'vvc'; // H266
|
|
175
178
|
case base === 'dvh1':
|
|
176
179
|
case base === 'dvhe':
|
|
177
180
|
return 'dovi-hevc'; // Dolby Vision based in HEVC
|
|
@@ -180,6 +183,9 @@ shaka.util.MimeUtils = class {
|
|
|
180
183
|
return 'dovi-avc'; // Dolby Vision based in AVC
|
|
181
184
|
case base === 'dav1':
|
|
182
185
|
return 'dovi-av1'; // Dolby Vision based in AV1
|
|
186
|
+
case base === 'dvc1':
|
|
187
|
+
case base === 'dvi1':
|
|
188
|
+
return 'dovi-vvc'; // Dolby Vision based in VVC
|
|
183
189
|
}
|
|
184
190
|
return base;
|
|
185
191
|
}
|