shaka-player 4.13.0 → 4.13.2
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/CONTRIBUTORS +1 -0
- package/dist/deps.js +4 -4
- package/dist/locales.js +36 -36
- 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 +2 -9
- package/dist/shaka-player.compiled.debug.js +491 -478
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +2 -9
- package/dist/shaka-player.compiled.js +1455 -1443
- package/dist/shaka-player.compiled.map +4 -4
- 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 +2 -9
- package/dist/shaka-player.ui.debug.js +544 -530
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +2 -9
- package/dist/shaka-player.ui.js +1627 -1613
- package/dist/shaka-player.ui.map +4 -4
- package/eslint.config.mjs +14 -1
- package/lib/cast/cast_proxy.js +50 -12
- package/lib/cast/cast_receiver.js +6 -9
- package/lib/cast/cast_sender.js +14 -11
- package/lib/cast/cast_utils.js +43 -46
- package/lib/cea/mp4_cea_parser.js +23 -23
- package/lib/dash/content_protection.js +1 -1
- package/lib/dash/dash_parser.js +39 -29
- package/lib/dash/mpd_utils.js +8 -9
- package/lib/dash/segment_list.js +2 -2
- package/lib/dash/segment_template.js +9 -9
- package/lib/debug/log.js +16 -17
- package/lib/hls/hls_parser.js +122 -99
- package/lib/media/closed_caption_parser.js +5 -5
- package/lib/media/content_workarounds.js +78 -0
- package/lib/media/example_based_criteria.js +4 -3
- package/lib/media/manifest_parser.js +8 -12
- package/lib/media/media_source_engine.js +136 -119
- package/lib/media/preload_manager.js +10 -5
- package/lib/media/region_observer.js +9 -8
- package/lib/media/region_timeline.js +10 -8
- package/lib/media/streaming_engine.js +25 -9
- package/lib/mss/mss_parser.js +6 -7
- package/lib/net/http_plugin_utils.js +1 -1
- package/lib/net/networking_engine.js +7 -7
- package/lib/net/networking_utils.js +38 -39
- package/lib/offline/stream_bandwidth_estimator.js +8 -8
- package/lib/player.js +98 -47
- package/lib/polyfill/media_capabilities.js +8 -9
- package/lib/text/cue.js +6 -2
- package/lib/text/text_engine.js +7 -7
- package/lib/text/ttml_text_parser.js +14 -16
- package/lib/text/ui_text_displayer.js +25 -4
- package/lib/transmuxer/ac3_transmuxer.js +6 -1
- package/lib/transmuxer/transmuxer_engine.js +6 -6
- package/lib/util/manifest_parser_utils.js +7 -8
- package/lib/util/mp4_generator.js +23 -8
- package/lib/util/mp4_parser.js +10 -10
- package/lib/util/multi_map.js +22 -20
- package/lib/util/platform.js +40 -9
- package/lib/util/player_configuration.js +1 -1
- package/lib/util/stream_utils.js +35 -17
- package/package.json +2 -2
- package/project-words.txt +1 -0
- package/third_party/closure-uri/uri.js +13 -13
- package/ui/ad_statistics_button.js +17 -16
- package/ui/audio_language_selection.js +6 -1
- package/ui/statistics_button.js +36 -35
package/lib/player.js
CHANGED
|
@@ -54,6 +54,7 @@ goog.require('shaka.util.Functional');
|
|
|
54
54
|
goog.require('shaka.util.IDestroyable');
|
|
55
55
|
goog.require('shaka.util.LanguageUtils');
|
|
56
56
|
goog.require('shaka.util.ManifestParserUtils');
|
|
57
|
+
goog.require('shaka.util.MapUtils');
|
|
57
58
|
goog.require('shaka.util.MediaReadyState');
|
|
58
59
|
goog.require('shaka.util.MimeUtils');
|
|
59
60
|
goog.require('shaka.util.Mutex');
|
|
@@ -698,7 +699,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
698
699
|
/** @private {shaka.media.BufferingObserver} */
|
|
699
700
|
this.bufferObserver_ = null;
|
|
700
701
|
|
|
701
|
-
/**
|
|
702
|
+
/**
|
|
703
|
+
* @private {shaka.media.RegionTimeline<
|
|
704
|
+
* shaka.extern.TimelineRegionInfo>}
|
|
705
|
+
*/
|
|
702
706
|
this.regionTimeline_ = null;
|
|
703
707
|
|
|
704
708
|
/** @private {shaka.util.CmcdManager} */
|
|
@@ -1083,7 +1087,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1083
1087
|
* @export
|
|
1084
1088
|
*/
|
|
1085
1089
|
static registerSupportPlugin(name, callback) {
|
|
1086
|
-
shaka.Player.supportPlugins_
|
|
1090
|
+
shaka.Player.supportPlugins_.set(name, callback);
|
|
1087
1091
|
}
|
|
1088
1092
|
|
|
1089
1093
|
/**
|
|
@@ -1172,9 +1176,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1172
1176
|
};
|
|
1173
1177
|
|
|
1174
1178
|
const plugins = shaka.Player.supportPlugins_;
|
|
1175
|
-
|
|
1176
|
-
ret[
|
|
1177
|
-
}
|
|
1179
|
+
plugins.forEach((value, key) => {
|
|
1180
|
+
ret[key] = value();
|
|
1181
|
+
});
|
|
1178
1182
|
|
|
1179
1183
|
return ret;
|
|
1180
1184
|
}
|
|
@@ -3591,6 +3595,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3591
3595
|
|
|
3592
3596
|
// Create the region observer. This will allow us to notify the app when we
|
|
3593
3597
|
// move in and out of timeline regions.
|
|
3598
|
+
/** @type {!shaka.media.RegionObserver<shaka.extern.TimelineRegionInfo>} */
|
|
3594
3599
|
const regionObserver = new shaka.media.RegionObserver(
|
|
3595
3600
|
this.regionTimeline_, startsPastZero);
|
|
3596
3601
|
|
|
@@ -3808,10 +3813,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3808
3813
|
netEngine: this.networkingEngine_,
|
|
3809
3814
|
onError: (error) => this.onError_(error),
|
|
3810
3815
|
onEvent: (event) => this.dispatchEvent(event),
|
|
3811
|
-
onSegmentAppended: (reference, stream) => {
|
|
3816
|
+
onSegmentAppended: (reference, stream, isMuxed) => {
|
|
3812
3817
|
this.onSegmentAppended_(
|
|
3813
|
-
reference.startTime, reference.endTime, stream.type,
|
|
3814
|
-
stream.codecs.includes(','));
|
|
3818
|
+
reference.startTime, reference.endTime, stream.type, isMuxed);
|
|
3815
3819
|
},
|
|
3816
3820
|
onInitSegmentAppended: (position, initSegment) => {
|
|
3817
3821
|
const mediaQuality = initSegment.getMediaQuality();
|
|
@@ -4723,7 +4727,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4723
4727
|
shaka.log.alwaysWarn('A trick play rate of 0 is unsupported!');
|
|
4724
4728
|
return;
|
|
4725
4729
|
}
|
|
4726
|
-
this.trickPlayEventManager_.removeAll();
|
|
4727
4730
|
|
|
4728
4731
|
this.playRateController_.set(rate);
|
|
4729
4732
|
|
|
@@ -4732,26 +4735,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4732
4735
|
this.streamingEngine_.setTrickPlay(
|
|
4733
4736
|
useTrickPlayTrack && Math.abs(rate) > 1);
|
|
4734
4737
|
}
|
|
4735
|
-
|
|
4736
|
-
this.trickPlayEventManager_.listen(this.video_, 'timeupdate', () => {
|
|
4737
|
-
const currentTime = this.video_.currentTime;
|
|
4738
|
-
const seekRange = this.seekRange();
|
|
4739
|
-
const safeSeekOffset = this.config_.streaming.safeSeekOffset;
|
|
4740
|
-
|
|
4741
|
-
// Cancel trick play if we hit the beginning or end of the seekable
|
|
4742
|
-
// (Sub-second accuracy not required here)
|
|
4743
|
-
if (rate > 0) {
|
|
4744
|
-
if (Math.floor(currentTime) >= Math.floor(seekRange.end)) {
|
|
4745
|
-
this.cancelTrickPlay();
|
|
4746
|
-
}
|
|
4747
|
-
} else {
|
|
4748
|
-
if (Math.floor(currentTime) <=
|
|
4749
|
-
Math.floor(seekRange.start + safeSeekOffset)) {
|
|
4750
|
-
this.cancelTrickPlay();
|
|
4751
|
-
}
|
|
4752
|
-
}
|
|
4753
|
-
});
|
|
4754
|
-
}
|
|
4738
|
+
this.setupTrickPlayEventListeners_(rate);
|
|
4755
4739
|
}
|
|
4756
4740
|
|
|
4757
4741
|
/**
|
|
@@ -5187,7 +5171,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5187
5171
|
if (this.config_.abr.enabled && (active.video != variant.video ||
|
|
5188
5172
|
(active.audio && variant.audio &&
|
|
5189
5173
|
active.audio.language == variant.audio.language &&
|
|
5190
|
-
active.audio.channelsCount == variant.audio.channelsCount
|
|
5174
|
+
active.audio.channelsCount == variant.audio.channelsCount &&
|
|
5175
|
+
active.audio.label == variant.audio.label))) {
|
|
5191
5176
|
shaka.log.alwaysWarn('Changing tracks while abr manager is enabled ' +
|
|
5192
5177
|
'will likely result in the selected track ' +
|
|
5193
5178
|
'being overridden. Consider disabling abr ' +
|
|
@@ -5296,10 +5281,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5296
5281
|
* @param {number=} safeMargin
|
|
5297
5282
|
* @param {string=} codec
|
|
5298
5283
|
* @param {boolean=} spatialAudio
|
|
5284
|
+
* @param {string=} label
|
|
5299
5285
|
* @export
|
|
5300
5286
|
*/
|
|
5301
5287
|
selectAudioLanguage(language, role, channelsCount = 0, safeMargin = 0,
|
|
5302
|
-
codec = '', spatialAudio = false) {
|
|
5288
|
+
codec = '', spatialAudio = false, label = '') {
|
|
5303
5289
|
const selectMediaSourceMode = () => {
|
|
5304
5290
|
this.currentAdaptationSetCriteria_ =
|
|
5305
5291
|
this.config_.adaptationSetCriteriaFactory();
|
|
@@ -5310,7 +5296,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5310
5296
|
hdrLevel: '',
|
|
5311
5297
|
spatialAudio: spatialAudio || false,
|
|
5312
5298
|
videoLayout: '',
|
|
5313
|
-
audioLabel: '',
|
|
5299
|
+
audioLabel: label || '',
|
|
5314
5300
|
videoLabel: '',
|
|
5315
5301
|
codecSwitchingStrategy:
|
|
5316
5302
|
this.config_.mediaSource.codecSwitchingStrategy,
|
|
@@ -5823,12 +5809,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5823
5809
|
* is playing.
|
|
5824
5810
|
*/
|
|
5825
5811
|
getLiveLatency() {
|
|
5826
|
-
if (!this.video_) {
|
|
5812
|
+
if (!this.video_ || !this.video_.currentTime) {
|
|
5827
5813
|
return null;
|
|
5828
5814
|
}
|
|
5829
5815
|
const now = this.getPresentationStartTimeAsDate().getTime() +
|
|
5830
5816
|
this.video_.currentTime * 1000;
|
|
5831
|
-
|
|
5817
|
+
|
|
5818
|
+
return Math.floor(Date.now() - now);
|
|
5832
5819
|
}
|
|
5833
5820
|
|
|
5834
5821
|
/**
|
|
@@ -6865,6 +6852,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6865
6852
|
// The playback rate has changed. This could be us or someone else.
|
|
6866
6853
|
// If this was us, setting the rate again will be a no-op.
|
|
6867
6854
|
this.playRateController_.set(newRate);
|
|
6855
|
+
|
|
6856
|
+
if (this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE) {
|
|
6857
|
+
this.abrManager_.playbackRateChanged(newRate);
|
|
6858
|
+
}
|
|
6859
|
+
this.setupTrickPlayEventListeners_(newRate);
|
|
6868
6860
|
}
|
|
6869
6861
|
|
|
6870
6862
|
const event = shaka.Player.makeEvent_(
|
|
@@ -6872,6 +6864,36 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6872
6864
|
this.dispatchEvent(event);
|
|
6873
6865
|
}
|
|
6874
6866
|
|
|
6867
|
+
/**
|
|
6868
|
+
* Configures all the necessary listeners when trick play is being performed.
|
|
6869
|
+
*
|
|
6870
|
+
* @param {number} rate
|
|
6871
|
+
* @private
|
|
6872
|
+
*/
|
|
6873
|
+
setupTrickPlayEventListeners_(rate) {
|
|
6874
|
+
this.trickPlayEventManager_.removeAll();
|
|
6875
|
+
if (this.isLive()) {
|
|
6876
|
+
this.trickPlayEventManager_.listen(this.video_, 'timeupdate', () => {
|
|
6877
|
+
const currentTime = this.video_.currentTime;
|
|
6878
|
+
const seekRange = this.seekRange();
|
|
6879
|
+
const safeSeekOffset = this.config_.streaming.safeSeekOffset;
|
|
6880
|
+
|
|
6881
|
+
// Cancel trick play if we hit the beginning or end of the seekable
|
|
6882
|
+
// (Sub-second accuracy not required here)
|
|
6883
|
+
if (rate > 0) {
|
|
6884
|
+
if (Math.floor(currentTime) >= Math.floor(seekRange.end)) {
|
|
6885
|
+
this.cancelTrickPlay();
|
|
6886
|
+
}
|
|
6887
|
+
} else {
|
|
6888
|
+
if (Math.floor(currentTime) <=
|
|
6889
|
+
Math.floor(seekRange.start + safeSeekOffset)) {
|
|
6890
|
+
this.cancelTrickPlay();
|
|
6891
|
+
}
|
|
6892
|
+
}
|
|
6893
|
+
});
|
|
6894
|
+
}
|
|
6895
|
+
}
|
|
6896
|
+
|
|
6875
6897
|
/**
|
|
6876
6898
|
* Try updating the state history. If the player has not finished
|
|
6877
6899
|
* initializing, this will be a no-op.
|
|
@@ -8040,6 +8062,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
8040
8062
|
|
|
8041
8063
|
// Only filter tracks for keys if we have some key statuses to look at.
|
|
8042
8064
|
if (keyIds.length) {
|
|
8065
|
+
const currentKeySystem = this.keySystem();
|
|
8066
|
+
const clearKeys = shaka.util.MapUtils.asMap(this.config_.drm.clearKeys);
|
|
8067
|
+
|
|
8043
8068
|
for (const variant of this.manifest_.variants) {
|
|
8044
8069
|
const streams = shaka.util.StreamUtils.getVariantStreams(variant);
|
|
8045
8070
|
|
|
@@ -8049,16 +8074,42 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
8049
8074
|
// Only update if we have key IDs for the stream. If the keys aren't
|
|
8050
8075
|
// all present, then the track should be restricted.
|
|
8051
8076
|
if (stream.keyIds.size) {
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8077
|
+
// If we are not using clearkeys, and the stream has drmInfos we
|
|
8078
|
+
// only want to check the keyIds of the keySystem we are using.
|
|
8079
|
+
// Other keySystems might have other keyIds that might not be
|
|
8080
|
+
// valid in this case. This can happen in HLS if the manifest
|
|
8081
|
+
// has Widevine with keyIds and PlayReady without keyIds and we are
|
|
8082
|
+
// using PlayReady.
|
|
8083
|
+
if (stream.drmInfos.length && !clearKeys.size) {
|
|
8084
|
+
for (const drmInfo of stream.drmInfos) {
|
|
8085
|
+
if (drmInfo.keyIds.size &&
|
|
8086
|
+
drmInfo.keySystem == currentKeySystem) {
|
|
8087
|
+
variant.allowedByKeySystem = true;
|
|
8088
|
+
|
|
8089
|
+
for (const keyId of drmInfo.keyIds) {
|
|
8090
|
+
const keyStatus =
|
|
8091
|
+
keyStatusMap[isGlobalStatus ? '00' : keyId];
|
|
8092
|
+
if (keyStatus || this.drmEngine_.hasManifestInitData()) {
|
|
8093
|
+
variant.allowedByKeySystem =
|
|
8094
|
+
variant.allowedByKeySystem &&
|
|
8095
|
+
!!keyStatus &&
|
|
8096
|
+
!restrictedStatuses.includes(keyStatus);
|
|
8097
|
+
} // if (keyStatus || this.drmEngine_.hasManifestInitData())
|
|
8098
|
+
} // for (const keyId of drmInfo.keyIds)
|
|
8099
|
+
} // if (drmInfo.keyIds.size && ...
|
|
8100
|
+
} // for (const drmInfo of stream.drmInfos
|
|
8101
|
+
} else {
|
|
8102
|
+
variant.allowedByKeySystem = true;
|
|
8103
|
+
|
|
8104
|
+
for (const keyId of stream.keyIds) {
|
|
8105
|
+
const keyStatus = keyStatusMap[isGlobalStatus ? '00' : keyId];
|
|
8106
|
+
if (keyStatus || this.drmEngine_.hasManifestInitData()) {
|
|
8107
|
+
variant.allowedByKeySystem = variant.allowedByKeySystem &&
|
|
8108
|
+
!!keyStatus && !restrictedStatuses.includes(keyStatus);
|
|
8109
|
+
}
|
|
8110
|
+
} // for (const keyId of stream.keyIds)
|
|
8111
|
+
} // if (stream.drmInfos.length && !clearKeys.size)
|
|
8112
|
+
} // if (stream.keyIds.size)
|
|
8062
8113
|
|
|
8063
8114
|
if (originalAllowed != variant.allowedByKeySystem) {
|
|
8064
8115
|
tracksChanged = true;
|
|
@@ -8384,15 +8435,15 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
8384
8435
|
* @export
|
|
8385
8436
|
*/
|
|
8386
8437
|
// eslint-disable-next-line no-useless-concat
|
|
8387
|
-
shaka.Player.version = 'v4.13.
|
|
8438
|
+
shaka.Player.version = 'v4.13.2' + '-uncompiled'; // x-release-please-version
|
|
8388
8439
|
|
|
8389
8440
|
// Initialize the deprecation system using the version string we just set
|
|
8390
8441
|
// on the player.
|
|
8391
8442
|
shaka.Deprecate.init(shaka.Player.version);
|
|
8392
8443
|
|
|
8393
8444
|
|
|
8394
|
-
/** @private {!
|
|
8395
|
-
shaka.Player.supportPlugins_ =
|
|
8445
|
+
/** @private {!Map<string, function(): *>} */
|
|
8446
|
+
shaka.Player.supportPlugins_ = new Map();
|
|
8396
8447
|
|
|
8397
8448
|
|
|
8398
8449
|
/** @private {?shaka.extern.IAdManager.Factory} */
|
|
@@ -378,14 +378,13 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
378
378
|
}
|
|
379
379
|
|
|
380
380
|
let result = false;
|
|
381
|
-
|
|
382
|
-
.memoizedCanDisplayTypeRequests_
|
|
383
|
-
|
|
384
|
-
|
|
381
|
+
const memoizedCanDisplayTypeRequests =
|
|
382
|
+
shaka.polyfill.MediaCapabilities.memoizedCanDisplayTypeRequests_;
|
|
383
|
+
if (memoizedCanDisplayTypeRequests.has(displayType)) {
|
|
384
|
+
result = memoizedCanDisplayTypeRequests.get(displayType);
|
|
385
385
|
} else {
|
|
386
386
|
result = await cast.__platform__.canDisplayType(displayType);
|
|
387
|
-
|
|
388
|
-
.memoizedCanDisplayTypeRequests_[displayType] = result;
|
|
387
|
+
memoizedCanDisplayTypeRequests.set(displayType, result);
|
|
389
388
|
}
|
|
390
389
|
return result;
|
|
391
390
|
}
|
|
@@ -405,10 +404,10 @@ shaka.polyfill.MediaCapabilities.originalMcap = null;
|
|
|
405
404
|
* A cache that stores the canDisplayType result of calling
|
|
406
405
|
* `cast.__platform__.canDisplayType`.
|
|
407
406
|
*
|
|
408
|
-
* @type {
|
|
409
|
-
* @
|
|
407
|
+
* @type {Map<string, boolean>}
|
|
408
|
+
* @private
|
|
410
409
|
*/
|
|
411
|
-
shaka.polyfill.MediaCapabilities.memoizedCanDisplayTypeRequests_ =
|
|
410
|
+
shaka.polyfill.MediaCapabilities.memoizedCanDisplayTypeRequests_ = new Map();
|
|
412
411
|
|
|
413
412
|
// Install at a lower priority than MediaSource polyfill, so that we have
|
|
414
413
|
// MediaSource available first.
|
package/lib/text/cue.js
CHANGED
|
@@ -371,8 +371,12 @@ shaka.text.Cue = class {
|
|
|
371
371
|
// performance optimization. We can avoid the more expensive recursive
|
|
372
372
|
// checks if the top-level properties don't match.
|
|
373
373
|
// See: https://github.com/shaka-project/shaka-player/issues/3018
|
|
374
|
-
if (cue1.
|
|
375
|
-
|
|
374
|
+
if (cue1.payload != cue2.payload) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
const isDiffNegligible = (a, b) => Math.abs(a - b) < 0.001;
|
|
378
|
+
if (!isDiffNegligible(cue1.startTime, cue2.startTime) ||
|
|
379
|
+
!isDiffNegligible(cue1.endTime, cue2.endTime)) {
|
|
376
380
|
return false;
|
|
377
381
|
}
|
|
378
382
|
for (const k in cue1) {
|
package/lib/text/text_engine.js
CHANGED
|
@@ -74,7 +74,7 @@ shaka.text.TextEngine = class {
|
|
|
74
74
|
* @export
|
|
75
75
|
*/
|
|
76
76
|
static registerParser(mimeType, plugin) {
|
|
77
|
-
shaka.text.TextEngine.parserMap_
|
|
77
|
+
shaka.text.TextEngine.parserMap_.set(mimeType, plugin);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
@@ -82,7 +82,7 @@ shaka.text.TextEngine = class {
|
|
|
82
82
|
* @export
|
|
83
83
|
*/
|
|
84
84
|
static unregisterParser(mimeType) {
|
|
85
|
-
|
|
85
|
+
shaka.text.TextEngine.parserMap_.delete(mimeType);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
/**
|
|
@@ -90,7 +90,7 @@ shaka.text.TextEngine = class {
|
|
|
90
90
|
* @export
|
|
91
91
|
*/
|
|
92
92
|
static findParser(mimeType) {
|
|
93
|
-
return shaka.text.TextEngine.parserMap_
|
|
93
|
+
return shaka.text.TextEngine.parserMap_.get(mimeType);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
@@ -98,7 +98,7 @@ shaka.text.TextEngine = class {
|
|
|
98
98
|
* @return {boolean}
|
|
99
99
|
*/
|
|
100
100
|
static isTypeSupported(mimeType) {
|
|
101
|
-
if (shaka.text.TextEngine.parserMap_
|
|
101
|
+
if (shaka.text.TextEngine.parserMap_.has(mimeType)) {
|
|
102
102
|
// An actual parser is available.
|
|
103
103
|
return true;
|
|
104
104
|
}
|
|
@@ -147,7 +147,7 @@ shaka.text.TextEngine = class {
|
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
const factory = shaka.text.TextEngine.parserMap_
|
|
150
|
+
const factory = shaka.text.TextEngine.parserMap_.get(mimeType);
|
|
151
151
|
goog.asserts.assert(
|
|
152
152
|
factory, 'Text type negotiation should have happened already');
|
|
153
153
|
this.parser_ = factory();
|
|
@@ -469,5 +469,5 @@ shaka.text.TextEngine = class {
|
|
|
469
469
|
}
|
|
470
470
|
};
|
|
471
471
|
|
|
472
|
-
/** @private {!
|
|
473
|
-
shaka.text.TextEngine.parserMap_ =
|
|
472
|
+
/** @private {!Map<string, !shaka.extern.TextParserPlugin>} */
|
|
473
|
+
shaka.text.TextEngine.parserMap_ = new Map();
|
|
@@ -589,8 +589,8 @@ shaka.text.TtmlTextParser = class {
|
|
|
589
589
|
const align = TtmlTextParser.getStyleAttribute_(
|
|
590
590
|
cueElement, region, styles, 'textAlign', true);
|
|
591
591
|
if (align) {
|
|
592
|
-
cue.positionAlign = TtmlTextParser.textAlignToPositionAlign_
|
|
593
|
-
cue.lineAlign = TtmlTextParser.textAlignToLineAlign_
|
|
592
|
+
cue.positionAlign = TtmlTextParser.textAlignToPositionAlign_.get(align);
|
|
593
|
+
cue.lineAlign = TtmlTextParser.textAlignToLineAlign_.get(align);
|
|
594
594
|
|
|
595
595
|
goog.asserts.assert(align.toUpperCase() in Cue.textAlign,
|
|
596
596
|
align.toUpperCase() + ' Should be in Cue.textAlign values!');
|
|
@@ -1402,25 +1402,23 @@ shaka.text.TtmlTextParser.timeHMSFormat_ =
|
|
|
1402
1402
|
|
|
1403
1403
|
/**
|
|
1404
1404
|
* @const
|
|
1405
|
-
* @private {!
|
|
1405
|
+
* @private {!Map<string, shaka.text.Cue.lineAlign>}
|
|
1406
1406
|
*/
|
|
1407
|
-
shaka.text.TtmlTextParser.textAlignToLineAlign_ =
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
};
|
|
1407
|
+
shaka.text.TtmlTextParser.textAlignToLineAlign_ = new Map()
|
|
1408
|
+
.set('left', shaka.text.Cue.lineAlign.START)
|
|
1409
|
+
.set('center', shaka.text.Cue.lineAlign.CENTER)
|
|
1410
|
+
.set('right', shaka.text.Cue.lineAlign.END)
|
|
1411
|
+
.set('start', shaka.text.Cue.lineAlign.START)
|
|
1412
|
+
.set('end', shaka.text.Cue.lineAlign.END);
|
|
1414
1413
|
|
|
1415
1414
|
/**
|
|
1416
1415
|
* @const
|
|
1417
|
-
* @private {!
|
|
1416
|
+
* @private {!Map<string, shaka.text.Cue.positionAlign>}
|
|
1418
1417
|
*/
|
|
1419
|
-
shaka.text.TtmlTextParser.textAlignToPositionAlign_ =
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
};
|
|
1418
|
+
shaka.text.TtmlTextParser.textAlignToPositionAlign_ = new Map()
|
|
1419
|
+
.set('left', shaka.text.Cue.positionAlign.LEFT)
|
|
1420
|
+
.set('center', shaka.text.Cue.positionAlign.CENTER)
|
|
1421
|
+
.set('right', shaka.text.Cue.positionAlign.RIGHT);
|
|
1424
1422
|
|
|
1425
1423
|
/**
|
|
1426
1424
|
* The namespace URL for TTML parameters. Can be assigned any name in the TTML
|
|
@@ -55,6 +55,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
55
55
|
/** @private {?number} */
|
|
56
56
|
this.aspectRatio_ = null;
|
|
57
57
|
|
|
58
|
+
/** @private {?shaka.extern.TextDisplayerConfiguration} */
|
|
59
|
+
this.config_ = null;
|
|
60
|
+
|
|
58
61
|
/** @type {HTMLElement} */
|
|
59
62
|
this.textContainer_ = shaka.util.Dom.createHTMLElement('div');
|
|
60
63
|
this.textContainer_.classList.add('shaka-text-container');
|
|
@@ -77,7 +80,8 @@ shaka.text.UITextDisplayer = class {
|
|
|
77
80
|
if (!this.video_.paused) {
|
|
78
81
|
this.updateCaptions_();
|
|
79
82
|
}
|
|
80
|
-
})
|
|
83
|
+
});
|
|
84
|
+
this.configureCaptionsTimer_();
|
|
81
85
|
|
|
82
86
|
/**
|
|
83
87
|
* Maps cues to cue elements. Specifically points out the wrapper element of
|
|
@@ -101,6 +105,10 @@ shaka.text.UITextDisplayer = class {
|
|
|
101
105
|
this.updateCaptions_(/* forceUpdate= */ true);
|
|
102
106
|
});
|
|
103
107
|
|
|
108
|
+
this.eventManager_.listen(this.video_, 'ratechange', () => {
|
|
109
|
+
this.configureCaptionsTimer_();
|
|
110
|
+
});
|
|
111
|
+
|
|
104
112
|
// From: https://html.spec.whatwg.org/multipage/media.html#dom-video-videowidth
|
|
105
113
|
// Whenever the natural width or natural height of the video changes
|
|
106
114
|
// (including, for example, because the selected video track was changed),
|
|
@@ -137,9 +145,8 @@ shaka.text.UITextDisplayer = class {
|
|
|
137
145
|
* @export
|
|
138
146
|
*/
|
|
139
147
|
configure(config) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
148
|
+
this.config_ = config;
|
|
149
|
+
this.configureCaptionsTimer_();
|
|
143
150
|
}
|
|
144
151
|
|
|
145
152
|
|
|
@@ -186,6 +193,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
186
193
|
this.cues_ = [];
|
|
187
194
|
if (this.captionsTimer_) {
|
|
188
195
|
this.captionsTimer_.stop();
|
|
196
|
+
this.captionsTimer_ = null;
|
|
189
197
|
}
|
|
190
198
|
|
|
191
199
|
this.currentCuesMap_.clear();
|
|
@@ -265,6 +273,19 @@ shaka.text.UITextDisplayer = class {
|
|
|
265
273
|
enableTextDisplayer() {
|
|
266
274
|
}
|
|
267
275
|
|
|
276
|
+
/**
|
|
277
|
+
* @private
|
|
278
|
+
*/
|
|
279
|
+
configureCaptionsTimer_() {
|
|
280
|
+
if (this.captionsTimer_) {
|
|
281
|
+
const captionsUpdatePeriod = this.config_ ?
|
|
282
|
+
this.config_.captionsUpdatePeriod : 0.25;
|
|
283
|
+
const updateTime = captionsUpdatePeriod /
|
|
284
|
+
Math.max(1, Math.abs(this.video_.playbackRate));
|
|
285
|
+
this.captionsTimer_.tickEvery(updateTime);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
268
289
|
/**
|
|
269
290
|
* @private
|
|
270
291
|
*/
|
|
@@ -14,6 +14,7 @@ goog.require('shaka.util.Error');
|
|
|
14
14
|
goog.require('shaka.util.Id3Utils');
|
|
15
15
|
goog.require('shaka.util.ManifestParserUtils');
|
|
16
16
|
goog.require('shaka.util.Mp4Generator');
|
|
17
|
+
goog.require('shaka.util.Platform');
|
|
17
18
|
goog.require('shaka.util.Uint8ArrayUtils');
|
|
18
19
|
|
|
19
20
|
|
|
@@ -86,7 +87,11 @@ shaka.transmuxer.Ac3Transmuxer = class {
|
|
|
86
87
|
*/
|
|
87
88
|
convertCodecs(contentType, mimeType) {
|
|
88
89
|
if (this.isAc3Container_(mimeType)) {
|
|
89
|
-
|
|
90
|
+
if (shaka.util.Platform.requiresEC3InitSegments()) {
|
|
91
|
+
return 'audio/mp4; codecs="ec-3"';
|
|
92
|
+
} else {
|
|
93
|
+
return 'audio/mp4; codecs="ac-3"';
|
|
94
|
+
}
|
|
90
95
|
}
|
|
91
96
|
return mimeType;
|
|
92
97
|
}
|
|
@@ -33,10 +33,10 @@ shaka.transmuxer.TransmuxerEngine = class {
|
|
|
33
33
|
const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
|
|
34
34
|
const normalizedMimetype = TransmuxerEngine.normalizeMimeType_(mimeType);
|
|
35
35
|
const key = normalizedMimetype + '-' + priority;
|
|
36
|
-
TransmuxerEngine.transmuxerMap_
|
|
36
|
+
TransmuxerEngine.transmuxerMap_.set(key, {
|
|
37
37
|
priority: priority,
|
|
38
38
|
plugin: plugin,
|
|
39
|
-
};
|
|
39
|
+
});
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -48,7 +48,7 @@ shaka.transmuxer.TransmuxerEngine = class {
|
|
|
48
48
|
const TransmuxerEngine = shaka.transmuxer.TransmuxerEngine;
|
|
49
49
|
const normalizedMimetype = TransmuxerEngine.normalizeMimeType_(mimeType);
|
|
50
50
|
const key = normalizedMimetype + '-' + priority;
|
|
51
|
-
|
|
51
|
+
TransmuxerEngine.transmuxerMap_.delete(key);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
@@ -68,7 +68,7 @@ shaka.transmuxer.TransmuxerEngine = class {
|
|
|
68
68
|
];
|
|
69
69
|
for (const priority of priorities) {
|
|
70
70
|
const key = normalizedMimetype + '-' + priority;
|
|
71
|
-
const object = TransmuxerEngine.transmuxerMap_
|
|
71
|
+
const object = TransmuxerEngine.transmuxerMap_.get(key);
|
|
72
72
|
if (object) {
|
|
73
73
|
const transmuxer = object.plugin();
|
|
74
74
|
const isSupported = transmuxer.isSupported(mimeType, contentType);
|
|
@@ -139,9 +139,9 @@ shaka.transmuxer.TransmuxerEngine.PluginObject;
|
|
|
139
139
|
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
|
-
* @private {!
|
|
142
|
+
* @private {!Map<string, !shaka.transmuxer.TransmuxerEngine.PluginObject>}
|
|
143
143
|
*/
|
|
144
|
-
shaka.transmuxer.TransmuxerEngine.transmuxerMap_ =
|
|
144
|
+
shaka.transmuxer.TransmuxerEngine.transmuxerMap_ = new Map();
|
|
145
145
|
|
|
146
146
|
|
|
147
147
|
/**
|
|
@@ -202,7 +202,7 @@ shaka.util.ManifestParserUtils = class {
|
|
|
202
202
|
*/
|
|
203
203
|
static guessCodecsSafe(contentType, codecs) {
|
|
204
204
|
const formats = shaka.util.ManifestParserUtils
|
|
205
|
-
.CODEC_REGEXPS_BY_CONTENT_TYPE_
|
|
205
|
+
.CODEC_REGEXPS_BY_CONTENT_TYPE_.get(contentType);
|
|
206
206
|
for (const format of formats) {
|
|
207
207
|
for (const codec of codecs) {
|
|
208
208
|
if (format.test(codec.trim())) {
|
|
@@ -231,7 +231,7 @@ shaka.util.ManifestParserUtils = class {
|
|
|
231
231
|
static guessAllCodecsSafe(contentType, codecs) {
|
|
232
232
|
const allCodecs = [];
|
|
233
233
|
const formats = shaka.util.ManifestParserUtils
|
|
234
|
-
.CODEC_REGEXPS_BY_CONTENT_TYPE_
|
|
234
|
+
.CODEC_REGEXPS_BY_CONTENT_TYPE_.get(contentType);
|
|
235
235
|
for (const format of formats) {
|
|
236
236
|
for (const codec of codecs) {
|
|
237
237
|
if (format.test(codec.trim())) {
|
|
@@ -332,10 +332,9 @@ shaka.util.ManifestParserUtils.TEXT_CODEC_REGEXPS_ = [
|
|
|
332
332
|
|
|
333
333
|
|
|
334
334
|
/**
|
|
335
|
-
* @const {!
|
|
335
|
+
* @const {!Map<string, !Array<!RegExp>>}
|
|
336
336
|
*/
|
|
337
|
-
shaka.util.ManifestParserUtils.CODEC_REGEXPS_BY_CONTENT_TYPE_ =
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
};
|
|
337
|
+
shaka.util.ManifestParserUtils.CODEC_REGEXPS_BY_CONTENT_TYPE_ = new Map()
|
|
338
|
+
.set('audio', shaka.util.ManifestParserUtils.AUDIO_CODEC_REGEXPS_)
|
|
339
|
+
.set('video', shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_)
|
|
340
|
+
.set('text', shaka.util.ManifestParserUtils.TEXT_CODEC_REGEXPS_);
|
|
@@ -8,6 +8,7 @@ goog.provide('shaka.util.Mp4Generator');
|
|
|
8
8
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
10
|
goog.require('shaka.util.ManifestParserUtils');
|
|
11
|
+
goog.require('shaka.util.Platform');
|
|
11
12
|
goog.require('shaka.util.Uint8ArrayUtils');
|
|
12
13
|
|
|
13
14
|
|
|
@@ -298,6 +299,20 @@ shaka.util.Mp4Generator = class {
|
|
|
298
299
|
stsd_(streamInfo) {
|
|
299
300
|
const Mp4Generator = shaka.util.Mp4Generator;
|
|
300
301
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
302
|
+
let audioCodec = 'aac';
|
|
303
|
+
if (streamInfo.codecs.includes('mp3')) {
|
|
304
|
+
audioCodec = 'mp3';
|
|
305
|
+
} else if (streamInfo.codecs.includes('ac-3')) {
|
|
306
|
+
if (shaka.util.Platform.requiresEC3InitSegments()) {
|
|
307
|
+
audioCodec = 'ec-3';
|
|
308
|
+
} else {
|
|
309
|
+
audioCodec = 'ac-3';
|
|
310
|
+
}
|
|
311
|
+
} else if (streamInfo.codecs.includes('ec-3')) {
|
|
312
|
+
audioCodec = 'ec-3';
|
|
313
|
+
} else if (streamInfo.codecs.includes('opus')) {
|
|
314
|
+
audioCodec = 'opus';
|
|
315
|
+
}
|
|
301
316
|
let bytes = new Uint8Array([]);
|
|
302
317
|
switch (streamInfo.type) {
|
|
303
318
|
case ContentType.VIDEO:
|
|
@@ -308,13 +323,13 @@ shaka.util.Mp4Generator = class {
|
|
|
308
323
|
}
|
|
309
324
|
break;
|
|
310
325
|
case ContentType.AUDIO:
|
|
311
|
-
if (
|
|
326
|
+
if (audioCodec == 'mp3') {
|
|
312
327
|
bytes = this.mp3_(streamInfo);
|
|
313
|
-
} else if (
|
|
328
|
+
} else if (audioCodec == 'ac-3') {
|
|
314
329
|
bytes = this.ac3_(streamInfo);
|
|
315
|
-
} else if (
|
|
330
|
+
} else if (audioCodec == 'ec-3') {
|
|
316
331
|
bytes = this.ec3_(streamInfo);
|
|
317
|
-
} else if (
|
|
332
|
+
} else if (audioCodec == 'opus') {
|
|
318
333
|
bytes = this.opus_(streamInfo);
|
|
319
334
|
} else {
|
|
320
335
|
bytes = this.mp4a_(streamInfo);
|
|
@@ -1167,7 +1182,7 @@ shaka.util.Mp4Generator = class {
|
|
|
1167
1182
|
* @return {!Uint8Array}
|
|
1168
1183
|
*/
|
|
1169
1184
|
static box(boxName, ...payload) {
|
|
1170
|
-
let type = shaka.util.Mp4Generator.BOX_TYPES_
|
|
1185
|
+
let type = shaka.util.Mp4Generator.BOX_TYPES_.get(boxName);
|
|
1171
1186
|
if (!type) {
|
|
1172
1187
|
type = [
|
|
1173
1188
|
boxName.charCodeAt(0),
|
|
@@ -1175,7 +1190,7 @@ shaka.util.Mp4Generator = class {
|
|
|
1175
1190
|
boxName.charCodeAt(2),
|
|
1176
1191
|
boxName.charCodeAt(3),
|
|
1177
1192
|
];
|
|
1178
|
-
shaka.util.Mp4Generator.BOX_TYPES_
|
|
1193
|
+
shaka.util.Mp4Generator.BOX_TYPES_.set(boxName, type);
|
|
1179
1194
|
}
|
|
1180
1195
|
// make the header for the box
|
|
1181
1196
|
let size = 8;
|
|
@@ -1212,9 +1227,9 @@ shaka.util.Mp4Generator.initialized_ = false;
|
|
|
1212
1227
|
shaka.util.Mp4Generator.UINT32_MAX_ = Math.pow(2, 32) - 1;
|
|
1213
1228
|
|
|
1214
1229
|
/**
|
|
1215
|
-
* @private {!
|
|
1230
|
+
* @private {!Map<string, !Array<number>>}
|
|
1216
1231
|
*/
|
|
1217
|
-
shaka.util.Mp4Generator.BOX_TYPES_ =
|
|
1232
|
+
shaka.util.Mp4Generator.BOX_TYPES_ = new Map();
|
|
1218
1233
|
|
|
1219
1234
|
/**
|
|
1220
1235
|
* @private {{video: !Uint8Array, audio: !Uint8Array}}
|