shaka-player 3.1.4 → 3.3.0
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/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
- package/.github/ISSUE_TEMPLATE/question.md +1 -1
- package/AUTHORS +6 -5
- package/CHANGELOG.md +363 -0
- package/CONTRIBUTORS +4 -2
- package/README.md +4 -4
- package/build/types/core +3 -0
- package/build/types/offline +1 -0
- package/build/types/polyfill +2 -0
- package/build/types/ui +2 -0
- package/demo/common/asset.js +9 -7
- package/demo/common/assets.js +28 -1
- package/demo/common/message_ids.js +15 -2
- package/demo/config.js +35 -2
- package/demo/index.html +1 -1
- package/demo/locales/en.json +14 -1
- package/demo/locales/source.json +56 -4
- package/demo/main.js +2 -1
- package/dist/controls.css +26 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +24 -21
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +24 -21
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +33 -25
- package/dist/locales.js +31 -29
- package/dist/receiver.compiled.debug.js +14 -12
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +14 -12
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +258 -20
- package/dist/shaka-player.compiled.debug.d.ts +258 -20
- package/dist/shaka-player.compiled.debug.externs.js +1005 -695
- package/dist/shaka-player.compiled.debug.js +720 -640
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +1005 -695
- package/dist/shaka-player.compiled.js +850 -817
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +274 -21
- package/dist/shaka-player.ui.debug.d.ts +274 -21
- package/dist/shaka-player.ui.debug.externs.js +766 -429
- package/dist/shaka-player.ui.debug.js +863 -765
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +766 -429
- package/dist/shaka-player.ui.js +1042 -996
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/bg-fetch-after.gv +29 -0
- package/docs/design/bg-fetch-after.gv.png +0 -0
- package/docs/design/bg-fetch-before.gv +18 -0
- package/docs/design/bg-fetch-before.gv.png +0 -0
- package/docs/design/bg-fetch.md +134 -0
- package/docs/tutorials/blob-url.md +15 -0
- package/docs/tutorials/index.json +1 -0
- package/docs/tutorials/ui-customization.md +66 -0
- package/docs/tutorials/ui.md +5 -5
- package/externs/aria.js +6 -0
- package/externs/ima.js +3 -0
- package/externs/shaka/ads.js +29 -1
- package/externs/shaka/manifest.js +18 -2
- package/externs/shaka/manifest_parser.js +10 -1
- package/externs/shaka/net.js +20 -4
- package/externs/shaka/offline.js +36 -2
- package/externs/shaka/player.js +182 -13
- package/externs/shaka/text.js +24 -10
- package/externs/texttrack.js +3 -0
- package/externs/webcrypto.js +18 -0
- package/lib/abr/ewma.js +14 -0
- package/lib/abr/ewma_bandwidth_estimator.js +16 -2
- package/lib/abr/simple_abr_manager.js +6 -0
- package/lib/ads/ad_manager.js +16 -15
- package/lib/ads/client_side_ad.js +8 -0
- package/lib/ads/client_side_ad_manager.js +14 -6
- package/lib/ads/server_side_ad.js +7 -0
- package/lib/ads/server_side_ad_manager.js +19 -2
- package/lib/cast/cast_proxy.js +8 -0
- package/lib/cast/cast_utils.js +7 -1
- package/lib/cea/cea608_data_channel.js +56 -56
- package/lib/cea/cea608_memory.js +33 -33
- package/lib/cea/cea708_service.js +43 -43
- package/lib/cea/cea708_window.js +38 -38
- package/lib/cea/cea_decoder.js +13 -14
- package/lib/cea/cea_utils.js +23 -23
- package/lib/cea/dtvcc_packet_builder.js +7 -7
- package/lib/cea/i_caption_decoder.js +2 -3
- package/lib/cea/i_cea_parser.js +2 -2
- package/lib/cea/mp4_cea_parser.js +6 -6
- package/lib/dash/dash_parser.js +53 -14
- package/lib/dash/segment_base.js +25 -1
- package/lib/dash/segment_list.js +18 -9
- package/lib/dash/segment_template.js +23 -10
- package/lib/hls/hls_parser.js +210 -30
- package/lib/media/closed_caption_parser.js +1 -2
- package/lib/media/drm_engine.js +162 -192
- package/lib/media/quality_observer.js +291 -0
- package/lib/media/segment_index.js +9 -1
- package/lib/media/segment_reference.js +37 -5
- package/lib/media/streaming_engine.js +48 -10
- package/lib/net/http_fetch_plugin.js +31 -11
- package/lib/net/http_plugin_utils.js +1 -0
- package/lib/net/http_xhr_plugin.js +40 -14
- package/lib/net/networking_engine.js +82 -18
- package/lib/offline/download_info.js +68 -0
- package/lib/offline/download_manager.js +14 -7
- package/lib/offline/indexeddb/base_storage_cell.js +35 -0
- package/lib/offline/indexeddb/storage_mechanism.js +1 -1
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
- package/lib/offline/manifest_converter.js +3 -1
- package/lib/offline/storage.js +404 -297
- package/lib/offline/stored_content_utils.js +2 -0
- package/lib/player.js +622 -153
- package/lib/polyfill/aria.js +82 -0
- package/lib/polyfill/random_uuid.js +52 -0
- package/lib/routing/walker.js +2 -3
- package/lib/text/cue.js +12 -0
- package/lib/text/srt_text_parser.js +4 -4
- package/lib/text/text_engine.js +2 -2
- package/lib/text/ttml_text_parser.js +25 -2
- package/lib/text/ui_text_displayer.js +2 -2
- package/lib/text/vtt_text_parser.js +2 -2
- package/lib/text/web_vtt_generator.js +55 -12
- package/lib/util/cmcd_manager.js +826 -0
- package/lib/util/error.js +12 -0
- package/lib/util/language_utils.js +12 -0
- package/lib/util/manifest_parser_utils.js +1 -0
- package/lib/util/mp4_box_parsers.js +20 -20
- package/lib/util/multi_map.js +8 -0
- package/lib/util/mutex.js +52 -0
- package/lib/util/periods.js +98 -64
- package/lib/util/player_configuration.js +31 -1
- package/lib/util/stream_utils.js +140 -85
- package/lib/util/xml_utils.js +31 -12
- package/package.json +1 -1
- package/roadmap.md +24 -22
- package/shaka-player.uncompiled.js +4 -0
- package/test/cast/cast_utils_unit.js +1 -0
- package/test/cea/cea608_memory_unit.js +1 -1
- package/test/cea/cea708_service_unit.js +8 -8
- package/test/cea/cea708_window_unit.js +6 -6
- package/test/cea/cea_decoder_unit.js +1 -1
- package/test/cea/dtvcc_packet_builder_unit.js +2 -2
- package/test/dash/dash_parser_content_protection_unit.js +2 -0
- package/test/dash/dash_parser_live_unit.js +101 -1
- package/test/dash/dash_parser_manifest_unit.js +78 -2
- package/test/dash/dash_parser_segment_base_unit.js +2 -0
- package/test/dash/dash_parser_segment_list_unit.js +2 -1
- package/test/dash/dash_parser_segment_template_unit.js +2 -1
- package/test/hls/hls_live_unit.js +2 -0
- package/test/hls/hls_parser_unit.js +252 -0
- package/test/media/drm_engine_integration.js +86 -12
- package/test/media/drm_engine_unit.js +310 -450
- package/test/media/quality_observer_unit.js +139 -0
- package/test/media/segment_reference_unit.js +16 -1
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +2 -0
- package/test/net/http_plugin_unit.js +20 -15
- package/test/offline/manifest_convert_unit.js +2 -0
- package/test/offline/storage_integration.js +117 -23
- package/test/player_integration.js +151 -0
- package/test/player_src_equals_integration.js +52 -0
- package/test/player_unit.js +11 -33
- package/test/test/assets/chapters.srt +11 -0
- package/test/test/assets/chapters.vtt +10 -0
- package/test/test/util/cea_utils.js +12 -12
- package/test/test/util/dash_parser_util.js +4 -0
- package/test/test/util/fake_ad.js +7 -0
- package/test/test/util/fake_ad_manager.js +7 -0
- package/test/test/util/fake_drm_engine.js +9 -2
- package/test/test/util/manifest_generator.js +5 -2
- package/test/test/util/manifest_parser_util.js +3 -1
- package/test/test/util/test_scheme.js +14 -1
- package/test/text/ttml_text_parser_unit.js +47 -0
- package/test/text/web_vtt_generator_unit.js +81 -7
- package/test/ui/ui_unit.js +250 -0
- package/test/util/cmcd_manager_unit.js +277 -0
- package/test/util/stream_utils_unit.js +118 -10
- package/ui/airplay_button.js +2 -1
- package/ui/audio_language_selection.js +9 -5
- package/ui/cast_button.js +5 -6
- package/ui/constants.js +0 -3
- package/ui/context_menu.js +109 -0
- package/ui/controls.js +41 -7
- package/ui/controls.less +1 -0
- package/ui/enums.js +4 -0
- package/ui/externs/ui.js +28 -1
- package/ui/fast_forward_button.js +16 -8
- package/ui/fullscreen_button.js +2 -3
- package/ui/language_utils.js +1 -1
- package/ui/less/buttons.less +0 -4
- package/ui/less/containers.less +74 -0
- package/ui/less/overflow_menu.less +0 -10
- package/ui/less/tooltip.less +97 -0
- package/ui/locales/en.json +1 -0
- package/ui/locales/source.json +4 -0
- package/ui/loop_button.js +6 -3
- package/ui/mute_button.js +3 -4
- package/ui/overflow_menu.js +7 -28
- package/ui/pip_button.js +10 -7
- package/ui/play_button.js +2 -2
- package/ui/playback_rate_selection.js +11 -15
- package/ui/presentation_time.js +1 -3
- package/ui/resolution_selection.js +11 -7
- package/ui/rewind_button.js +17 -9
- package/ui/seek_bar.js +6 -6
- package/ui/settings_menu.js +36 -4
- package/ui/small_play_button.js +1 -0
- package/ui/spacer.js +1 -1
- package/ui/statistics_button.js +253 -0
- package/ui/text_selection.js +19 -15
- package/ui/ui.js +30 -0
- package/ui/ui_utils.js +1 -1
- package/ui/volume_bar.js +2 -4
package/lib/player.js
CHANGED
|
@@ -22,6 +22,7 @@ goog.require('shaka.media.PlayRateController');
|
|
|
22
22
|
goog.require('shaka.media.Playhead');
|
|
23
23
|
goog.require('shaka.media.PlayheadObserverManager');
|
|
24
24
|
goog.require('shaka.media.PreferenceBasedCriteria');
|
|
25
|
+
goog.require('shaka.media.QualityObserver');
|
|
25
26
|
goog.require('shaka.media.RegionObserver');
|
|
26
27
|
goog.require('shaka.media.RegionTimeline');
|
|
27
28
|
goog.require('shaka.media.SegmentIndex');
|
|
@@ -36,6 +37,7 @@ goog.require('shaka.text.UITextDisplayer');
|
|
|
36
37
|
goog.require('shaka.text.WebVttGenerator');
|
|
37
38
|
goog.require('shaka.util.AbortableOperation');
|
|
38
39
|
goog.require('shaka.util.BufferUtils');
|
|
40
|
+
goog.require('shaka.util.CmcdManager');
|
|
39
41
|
goog.require('shaka.util.ConfigUtils');
|
|
40
42
|
goog.require('shaka.util.Error');
|
|
41
43
|
goog.require('shaka.util.EventManager');
|
|
@@ -106,6 +108,32 @@ goog.requireType('shaka.routing.Payload');
|
|
|
106
108
|
*/
|
|
107
109
|
|
|
108
110
|
|
|
111
|
+
/**
|
|
112
|
+
* @event shaka.Player.DownloadFailed
|
|
113
|
+
* @description Fired when a download has failed, for any reason.
|
|
114
|
+
* 'downloadfailed'
|
|
115
|
+
* @property {!shaka.extern.Request} request
|
|
116
|
+
* @property {?shaka.util.Error} error
|
|
117
|
+
* @param {number} httpResponseCode
|
|
118
|
+
* @param {boolean} aborted
|
|
119
|
+
* @exportDoc
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @event shaka.Player.DownloadHeadersReceived
|
|
125
|
+
* @description Fired when the networking engine has received the headers for
|
|
126
|
+
* a download, but before the body has been downloaded.
|
|
127
|
+
* If the HTTP plugin being used does not track this information, this event
|
|
128
|
+
* will default to being fired when the body is received, instead.
|
|
129
|
+
* @property {!Object.<string, string>} headers
|
|
130
|
+
* @property {!shaka.extern.Request} request
|
|
131
|
+
* @property {!shaka.net.NetworkingEngine.RequestType} type
|
|
132
|
+
* 'downloadheadersreceived'
|
|
133
|
+
* @exportDoc
|
|
134
|
+
*/
|
|
135
|
+
|
|
136
|
+
|
|
109
137
|
/**
|
|
110
138
|
* @event shaka.Player.DrmSessionUpdateEvent
|
|
111
139
|
* @description Fired when the CDM has accepted the license response.
|
|
@@ -147,6 +175,21 @@ goog.requireType('shaka.routing.Payload');
|
|
|
147
175
|
* @exportDoc
|
|
148
176
|
*/
|
|
149
177
|
|
|
178
|
+
/**
|
|
179
|
+
* @event shaka.Player.MediaQualityChangedEvent
|
|
180
|
+
* @description Fired when the media quality changes at the playhead.
|
|
181
|
+
* That may be caused by an adaptation change or a DASH period transition.
|
|
182
|
+
* Separate events are emitted for audio and video contentTypes.
|
|
183
|
+
* This is supported for only DASH streams at this time.
|
|
184
|
+
* @property {string} type
|
|
185
|
+
* 'mediaqualitychanged'
|
|
186
|
+
* @property {shaka.extern.MediaQualityInfo} mediaQuality
|
|
187
|
+
* Information about media quality at the playhead position.
|
|
188
|
+
* @property {number} position
|
|
189
|
+
* The playhead position.
|
|
190
|
+
* @exportDoc
|
|
191
|
+
*/
|
|
192
|
+
|
|
150
193
|
|
|
151
194
|
/**
|
|
152
195
|
* @event shaka.Player.BufferingEvent
|
|
@@ -217,6 +260,8 @@ goog.requireType('shaka.routing.Payload');
|
|
|
217
260
|
* <code>selectAudioLanguage()</code>, or <code>selectTextLanguage()</code>.
|
|
218
261
|
* @property {string} type
|
|
219
262
|
* 'adaptation'
|
|
263
|
+
* @property {shaka.extern.Track} oldTrack
|
|
264
|
+
* @property {shaka.extern.Track} newTrack
|
|
220
265
|
* @exportDoc
|
|
221
266
|
*/
|
|
222
267
|
|
|
@@ -439,6 +484,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
439
484
|
/** @private {shaka.media.RegionTimeline} */
|
|
440
485
|
this.regionTimeline_ = null;
|
|
441
486
|
|
|
487
|
+
/** @private {shaka.util.CmcdManager} */
|
|
488
|
+
this.cmcdManager_ = null;
|
|
489
|
+
|
|
490
|
+
/** @private {shaka.media.QualityObserver} */
|
|
491
|
+
this.qualityObserver_ = null;
|
|
492
|
+
|
|
442
493
|
/** @private {shaka.media.StreamingEngine} */
|
|
443
494
|
this.streamingEngine_ = null;
|
|
444
495
|
|
|
@@ -505,6 +556,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
505
556
|
/** @private {!Array.<function():(!Promise|undefined)>} */
|
|
506
557
|
this.cleanupOnUnload_ = [];
|
|
507
558
|
|
|
559
|
+
/**
|
|
560
|
+
* This playback start position will be used when
|
|
561
|
+
* <code>updateStartTime()</code> has been called to provide an updated
|
|
562
|
+
* start position during the media loading process.
|
|
563
|
+
*
|
|
564
|
+
* @private {?number}
|
|
565
|
+
*/
|
|
566
|
+
this.updatedStartTime_ = null;
|
|
567
|
+
|
|
508
568
|
if (dependencyInjector) {
|
|
509
569
|
dependencyInjector(this);
|
|
510
570
|
}
|
|
@@ -721,6 +781,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
721
781
|
this.config_ = null;
|
|
722
782
|
this.stats_ = null;
|
|
723
783
|
this.videoContainer_ = null;
|
|
784
|
+
this.cmcdManager_ = null;
|
|
724
785
|
|
|
725
786
|
if (this.networkingEngine_) {
|
|
726
787
|
await this.networkingEngine_.destroy();
|
|
@@ -1018,6 +1079,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1018
1079
|
return this.wrapWalkerListenersWithPromise_(events);
|
|
1019
1080
|
}
|
|
1020
1081
|
|
|
1082
|
+
/**
|
|
1083
|
+
* Provides a way to update the stream start position during the media loading
|
|
1084
|
+
* process. Can for example be called from the <code>manifestparsed</code>
|
|
1085
|
+
* event handler to update the start position based on information in the
|
|
1086
|
+
* manifest.
|
|
1087
|
+
*
|
|
1088
|
+
* @param {number} startTime
|
|
1089
|
+
* @export
|
|
1090
|
+
*/
|
|
1091
|
+
updateStartTime(startTime) {
|
|
1092
|
+
this.updatedStartTime_ = startTime;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1021
1095
|
/**
|
|
1022
1096
|
* Tell the player to load the content at <code>assetUri</code> and start
|
|
1023
1097
|
* playback at <code>startTime</code>. Before calling <code>load</code>,
|
|
@@ -1038,6 +1112,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1038
1112
|
* @export
|
|
1039
1113
|
*/
|
|
1040
1114
|
load(assetUri, startTime, mimeType) {
|
|
1115
|
+
this.updatedStartTime_ = null;
|
|
1116
|
+
|
|
1041
1117
|
// Do not allow the player to be used after |destroy| is called.
|
|
1042
1118
|
if (this.loadMode_ == shaka.Player.LoadMode.DESTROYED) {
|
|
1043
1119
|
return Promise.reject(this.createAbortLoadError_());
|
|
@@ -1091,6 +1167,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1091
1167
|
// before we allow calls to |updateStateHistory|.
|
|
1092
1168
|
this.stats_ = new shaka.util.Stats();
|
|
1093
1169
|
|
|
1170
|
+
// Create the CMCD manager so client data can be attached to all requests
|
|
1171
|
+
this.cmcdManager_ = this.createCmcd_();
|
|
1172
|
+
|
|
1094
1173
|
// Load's request is a little different, so we can't use our normal
|
|
1095
1174
|
// listeners-to-promise method. It is the only request where we may skip the
|
|
1096
1175
|
// request, so we need to set the on skip callback to reject with a specific
|
|
@@ -1142,25 +1221,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1142
1221
|
if (!mimeType) {
|
|
1143
1222
|
// Try using the uri extension.
|
|
1144
1223
|
const extension = shaka.media.ManifestParser.getExtension(uri);
|
|
1145
|
-
mimeType =
|
|
1146
|
-
'mp4': 'video/mp4',
|
|
1147
|
-
'm4v': 'video/mp4',
|
|
1148
|
-
'm4a': 'audio/mp4',
|
|
1149
|
-
'webm': 'video/webm',
|
|
1150
|
-
'weba': 'audio/webm',
|
|
1151
|
-
'mkv': 'video/webm', // Chromium browsers supports it.
|
|
1152
|
-
'ts': 'video/mp2t',
|
|
1153
|
-
'ogv': 'video/ogg',
|
|
1154
|
-
'ogg': 'audio/ogg',
|
|
1155
|
-
'mpg': 'video/mpeg',
|
|
1156
|
-
'mpeg': 'video/mpeg',
|
|
1157
|
-
'm3u8': 'application/x-mpegurl',
|
|
1158
|
-
'mpd': 'application/dash+xml',
|
|
1159
|
-
'mp3': 'audio/mpeg',
|
|
1160
|
-
'aac': 'audio/aac',
|
|
1161
|
-
'flac': 'audio/flac',
|
|
1162
|
-
'wav': 'audio/wav',
|
|
1163
|
-
}[extension];
|
|
1224
|
+
mimeType = shaka.Player.SRC_EQUAL_EXTENSIONS_TO_MIME_TYPES_[extension];
|
|
1164
1225
|
}
|
|
1165
1226
|
|
|
1166
1227
|
// TODO: The load graph system has a design limitation that requires routing
|
|
@@ -1610,6 +1671,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1610
1671
|
goog.asserts.assert(
|
|
1611
1672
|
this.networkingEngine_,
|
|
1612
1673
|
'Need networking engine to parse manifest.');
|
|
1674
|
+
goog.asserts.assert(
|
|
1675
|
+
this.cmcdManager_,
|
|
1676
|
+
'Need CMCD manager to populate manifest request data.');
|
|
1613
1677
|
goog.asserts.assert(
|
|
1614
1678
|
this.config_,
|
|
1615
1679
|
'Need player config to parse manifest.');
|
|
@@ -1633,12 +1697,24 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1633
1697
|
this.adManager_.onDashTimedMetadata(region);
|
|
1634
1698
|
}
|
|
1635
1699
|
});
|
|
1700
|
+
this.qualityObserver_ = null;
|
|
1701
|
+
if (this.config_.streaming.observeQualityChanges) {
|
|
1702
|
+
this.qualityObserver_ = new shaka.media.QualityObserver(
|
|
1703
|
+
() => this.getBufferedInfo());
|
|
1704
|
+
this.qualityObserver_.setListeners((mediaQualityInfo, position) => {
|
|
1705
|
+
this.onMediaQualityChange_(mediaQualityInfo, position);
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1636
1708
|
|
|
1637
|
-
// TODO (#1391): Once filterManifest_ is async, remove this eslint disable.
|
|
1638
|
-
/* eslint-disable require-await */
|
|
1639
1709
|
const playerInterface = {
|
|
1640
1710
|
networkingEngine: networkingEngine,
|
|
1641
|
-
|
|
1711
|
+
modifyManifestRequest: (request, manifestInfo) => {
|
|
1712
|
+
this.cmcdManager_.applyManifestData(request, manifestInfo);
|
|
1713
|
+
},
|
|
1714
|
+
modifySegmentRequest: (request, segmentInfo) => {
|
|
1715
|
+
this.cmcdManager_.applySegmentData(request, segmentInfo);
|
|
1716
|
+
},
|
|
1717
|
+
filter: (manifest) => this.filterManifest_(manifest),
|
|
1642
1718
|
makeTextStreamsForClosedCaptions: (manifest) => {
|
|
1643
1719
|
return this.makeTextStreamsForClosedCaptions_(manifest);
|
|
1644
1720
|
},
|
|
@@ -1656,7 +1732,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1656
1732
|
this.configure('streaming.lowLatencyMode', true);
|
|
1657
1733
|
},
|
|
1658
1734
|
};
|
|
1659
|
-
/* eslint-enable require-await */
|
|
1660
1735
|
|
|
1661
1736
|
const startTime = Date.now() / 1000;
|
|
1662
1737
|
|
|
@@ -1748,17 +1823,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1748
1823
|
},
|
|
1749
1824
|
});
|
|
1750
1825
|
|
|
1751
|
-
// TODO: remove once MediaCap implementation is complete.
|
|
1752
|
-
if (!this.config_.useMediaCapabilities) {
|
|
1753
|
-
shaka.util.StreamUtils.filterManifestByMediaSource(this.manifest_);
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
1826
|
this.drmEngine_.configure(this.config_.drm);
|
|
1757
1827
|
|
|
1758
1828
|
await this.drmEngine_.initForPlayback(
|
|
1759
1829
|
this.manifest_.variants,
|
|
1760
|
-
this.manifest_.offlineSessionIds
|
|
1761
|
-
this.config_.useMediaCapabilities);
|
|
1830
|
+
this.manifest_.offlineSessionIds);
|
|
1762
1831
|
|
|
1763
1832
|
await this.drmEngine_.attach(has.mediaElement);
|
|
1764
1833
|
|
|
@@ -1805,6 +1874,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1805
1874
|
// something we are now depending on.
|
|
1806
1875
|
has.startTime = wants.startTime;
|
|
1807
1876
|
|
|
1877
|
+
// If updateStartTime() has been called since load() was invoked use the
|
|
1878
|
+
// requested startTime
|
|
1879
|
+
if (this.updatedStartTime_ != null) {
|
|
1880
|
+
has.startTime = this.updatedStartTime_;
|
|
1881
|
+
this.updatedStartTime_ = null;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1808
1884
|
// Store a reference to values in |has| after asserting so that closure will
|
|
1809
1885
|
// know that they will still be non-null between calls to await.
|
|
1810
1886
|
const mediaElement = has.mediaElement;
|
|
@@ -1873,7 +1949,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1873
1949
|
// If the content is multi-codec and the browser can play more than one of
|
|
1874
1950
|
// them, choose codecs now before we initialize streaming.
|
|
1875
1951
|
shaka.util.StreamUtils.chooseCodecsAndFilterManifest(
|
|
1876
|
-
this.manifest_,
|
|
1952
|
+
this.manifest_,
|
|
1953
|
+
this.config_.preferredVideoCodecs,
|
|
1954
|
+
this.config_.preferredAudioCodecs,
|
|
1955
|
+
this.config_.preferredAudioChannelCount,
|
|
1956
|
+
this.config_.preferredDecodingAttributes);
|
|
1877
1957
|
|
|
1878
1958
|
this.streamingEngine_ = this.createStreamingEngine();
|
|
1879
1959
|
this.streamingEngine_.configure(this.config_.streaming);
|
|
@@ -1912,7 +1992,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1912
1992
|
}
|
|
1913
1993
|
|
|
1914
1994
|
// Since the first streams just became active, send an adaptation event.
|
|
1915
|
-
this.onAdaptation_(
|
|
1995
|
+
this.onAdaptation_(null,
|
|
1996
|
+
shaka.util.StreamUtils.variantToTrack(initialVariant));
|
|
1916
1997
|
}
|
|
1917
1998
|
|
|
1918
1999
|
// Decide if text should be shown automatically.
|
|
@@ -2025,6 +2106,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2025
2106
|
|
|
2026
2107
|
this.drmEngine_.configure(this.config_.drm);
|
|
2027
2108
|
|
|
2109
|
+
const uri = wants.uri || '';
|
|
2110
|
+
const extension = shaka.media.ManifestParser.getExtension(uri);
|
|
2111
|
+
let mimeType = shaka.Player.SRC_EQUAL_EXTENSIONS_TO_MIME_TYPES_[extension];
|
|
2112
|
+
if (mimeType == 'application/x-mpegurl' && shaka.util.Platform.isApple()) {
|
|
2113
|
+
mimeType = 'application/vnd.apple.mpegurl';
|
|
2114
|
+
}
|
|
2115
|
+
if (!mimeType) {
|
|
2116
|
+
mimeType = 'video/mp4';
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2028
2119
|
// TODO: Instead of feeding DrmEngine with Variants, we should refactor
|
|
2029
2120
|
// DrmEngine so that it takes a minimal config derived from Variants. In
|
|
2030
2121
|
// cases like this one or in removal of stored content, the details are
|
|
@@ -2045,7 +2136,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2045
2136
|
createSegmentIndex: () => Promise.resolve(),
|
|
2046
2137
|
segmentIndex: null,
|
|
2047
2138
|
mimeType: wants.mimeType ?
|
|
2048
|
-
shaka.util.MimeUtils.getBasicType(wants.mimeType) :
|
|
2139
|
+
shaka.util.MimeUtils.getBasicType(wants.mimeType) : mimeType,
|
|
2049
2140
|
codecs: wants.mimeType ?
|
|
2050
2141
|
shaka.util.MimeUtils.getCodecs(wants.mimeType) : '',
|
|
2051
2142
|
encrypted: true,
|
|
@@ -2164,8 +2255,22 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2164
2255
|
}
|
|
2165
2256
|
if (this.video_.textTracks) {
|
|
2166
2257
|
this.eventManager_.listen(this.video_.textTracks, 'addtrack', (e) => {
|
|
2167
|
-
|
|
2168
|
-
|
|
2258
|
+
const trackEvent = /** @type {!TrackEvent} */(e);
|
|
2259
|
+
if (trackEvent.track) {
|
|
2260
|
+
const track = trackEvent.track;
|
|
2261
|
+
goog.asserts.assert(track instanceof TextTrack, 'Wrong track type!');
|
|
2262
|
+
switch (track.kind) {
|
|
2263
|
+
case 'metadata':
|
|
2264
|
+
this.processTimedMetadataSrcEqls_(track);
|
|
2265
|
+
break;
|
|
2266
|
+
case 'chapters':
|
|
2267
|
+
this.processChaptersTrack_(track);
|
|
2268
|
+
break;
|
|
2269
|
+
default:
|
|
2270
|
+
this.onTracksChanged_();
|
|
2271
|
+
break;
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2169
2274
|
});
|
|
2170
2275
|
this.eventManager_.listen(
|
|
2171
2276
|
this.video_.textTracks, 'removetrack', () => this.onTracksChanged_());
|
|
@@ -2173,9 +2278,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2173
2278
|
this.video_.textTracks, 'change', () => this.onTracksChanged_());
|
|
2174
2279
|
}
|
|
2175
2280
|
|
|
2281
|
+
const extension = shaka.media.ManifestParser.getExtension(has.uri);
|
|
2282
|
+
const mimeType =
|
|
2283
|
+
shaka.Player.SRC_EQUAL_EXTENSIONS_TO_MIME_TYPES_[extension];
|
|
2284
|
+
|
|
2176
2285
|
// By setting |src| we are done "loading" with src=. We don't need to set
|
|
2177
2286
|
// the current time because |playhead| will do that for us.
|
|
2178
|
-
has.mediaElement.src = has.uri;
|
|
2287
|
+
has.mediaElement.src = this.cmcdManager_.appendSrcData(has.uri, mimeType);
|
|
2179
2288
|
|
|
2180
2289
|
// Tizen 3 / WebOS won't load anything unless you call load() explicitly,
|
|
2181
2290
|
// no matter the value of the preload attribute. This is harmful on some
|
|
@@ -2338,13 +2447,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2338
2447
|
* We're looking for metadata tracks to process id3 tags. One of the uses is
|
|
2339
2448
|
* for ad info on LIVE streams
|
|
2340
2449
|
*
|
|
2341
|
-
* @param {!
|
|
2450
|
+
* @param {!TextTrack} track
|
|
2342
2451
|
* @private
|
|
2343
2452
|
*/
|
|
2344
|
-
processTimedMetadataSrcEqls_(
|
|
2345
|
-
const track = event.track;
|
|
2346
|
-
goog.asserts.assert(track instanceof TextTrack, 'Wrong track type!');
|
|
2347
|
-
|
|
2453
|
+
processTimedMetadataSrcEqls_(track) {
|
|
2348
2454
|
if (track.kind != 'metadata') {
|
|
2349
2455
|
return;
|
|
2350
2456
|
}
|
|
@@ -2424,6 +2530,32 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2424
2530
|
this.dispatchEvent(this.makeEvent_(eventName, data));
|
|
2425
2531
|
}
|
|
2426
2532
|
|
|
2533
|
+
/**
|
|
2534
|
+
* We're looking for chapters tracks to process the chapters.
|
|
2535
|
+
*
|
|
2536
|
+
* @param {?TextTrack} track
|
|
2537
|
+
* @private
|
|
2538
|
+
*/
|
|
2539
|
+
processChaptersTrack_(track) {
|
|
2540
|
+
if (!track || track.kind != 'chapters') {
|
|
2541
|
+
return;
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2544
|
+
// Hidden mode is required for the cuechange event to launch correctly and
|
|
2545
|
+
// get the cues and the activeCues
|
|
2546
|
+
track.mode = 'hidden';
|
|
2547
|
+
|
|
2548
|
+
// In Safari the initial assignment does not always work, so we schedule
|
|
2549
|
+
// this process to be repeated several times to ensure that it has been put
|
|
2550
|
+
// in the correct mode.
|
|
2551
|
+
new shaka.util.Timer(() => {
|
|
2552
|
+
const chaptersTracks = this.getChaptersTracks_();
|
|
2553
|
+
for (const chaptersTrack of chaptersTracks) {
|
|
2554
|
+
chaptersTrack.mode = 'hidden';
|
|
2555
|
+
}
|
|
2556
|
+
}).tickNow().tickAfter(/* seconds= */ 0.5);
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2427
2559
|
/**
|
|
2428
2560
|
* Take a series of variants and ensure that they only contain one type of
|
|
2429
2561
|
* variant. The different options are:
|
|
@@ -2482,7 +2614,30 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2482
2614
|
this.abrManager_.segmentDownloaded(deltaTimeMs, bytesDownloaded);
|
|
2483
2615
|
}
|
|
2484
2616
|
};
|
|
2485
|
-
|
|
2617
|
+
/** @type {shaka.net.NetworkingEngine.OnHeadersReceived} */
|
|
2618
|
+
const onHeadersReceived_ = (headers, request, requestType) => {
|
|
2619
|
+
// Release a 'downloadheadersreceived' event.
|
|
2620
|
+
const name = shaka.Player.EventName.DownloadHeadersReceived;
|
|
2621
|
+
const data = new Map()
|
|
2622
|
+
.set('headers', headers)
|
|
2623
|
+
.set('request', request)
|
|
2624
|
+
.set('requestType', requestType);
|
|
2625
|
+
this.dispatchEvent(this.makeEvent_(name, data));
|
|
2626
|
+
};
|
|
2627
|
+
/** @type {shaka.net.NetworkingEngine.OnDownloadFailed} */
|
|
2628
|
+
const onDownloadFailed_ = (request, error, httpResponseCode, aborted) => {
|
|
2629
|
+
// Release a 'downloadfailed' event.
|
|
2630
|
+
const name = shaka.Player.EventName.DownloadFailed;
|
|
2631
|
+
const data = new Map()
|
|
2632
|
+
.set('request', request)
|
|
2633
|
+
.set('error', error)
|
|
2634
|
+
.set('httpResponseCode', httpResponseCode)
|
|
2635
|
+
.set('aborted', aborted);
|
|
2636
|
+
this.dispatchEvent(this.makeEvent_(name, data));
|
|
2637
|
+
};
|
|
2638
|
+
|
|
2639
|
+
return new shaka.net.NetworkingEngine(
|
|
2640
|
+
onProgressUpdated_, onHeadersReceived_, onDownloadFailed_);
|
|
2486
2641
|
}
|
|
2487
2642
|
|
|
2488
2643
|
/**
|
|
@@ -2538,7 +2693,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2538
2693
|
// Now that we have all our observers, create a manager for them.
|
|
2539
2694
|
const manager = new shaka.media.PlayheadObserverManager(this.video_);
|
|
2540
2695
|
manager.manage(regionObserver);
|
|
2541
|
-
|
|
2696
|
+
if (this.qualityObserver_) {
|
|
2697
|
+
manager.manage(this.qualityObserver_);
|
|
2698
|
+
}
|
|
2542
2699
|
return manager;
|
|
2543
2700
|
}
|
|
2544
2701
|
|
|
@@ -2654,6 +2811,27 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2654
2811
|
mediaElement, closedCaptionsParser, textDisplayer, onMetadata);
|
|
2655
2812
|
}
|
|
2656
2813
|
|
|
2814
|
+
/**
|
|
2815
|
+
* Create a new CMCD manager.
|
|
2816
|
+
*
|
|
2817
|
+
* @private
|
|
2818
|
+
*/
|
|
2819
|
+
createCmcd_() {
|
|
2820
|
+
const abr = this.abrManager_;
|
|
2821
|
+
|
|
2822
|
+
/** @type {shaka.util.CmcdManager.PlayerInterface} */
|
|
2823
|
+
const playerInterface = {
|
|
2824
|
+
getBandwidthEstimate: () => abr ? abr.getBandwidthEstimate() : NaN,
|
|
2825
|
+
getBufferedInfo: () => this.getBufferedInfo(),
|
|
2826
|
+
getCurrentTime: () => this.video_ ? this.video_.currentTime : 0,
|
|
2827
|
+
getManifest: () => this.manifest_,
|
|
2828
|
+
getPlaybackRate: () => this.getPlaybackRate(),
|
|
2829
|
+
isLive: () => this.isLive(),
|
|
2830
|
+
};
|
|
2831
|
+
|
|
2832
|
+
return new shaka.util.CmcdManager(playerInterface, this.config_.cmcd);
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2657
2835
|
/**
|
|
2658
2836
|
* Creates a new instance of StreamingEngine. This can be replaced by tests
|
|
2659
2837
|
* to create fake instances instead.
|
|
@@ -2663,19 +2841,28 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2663
2841
|
createStreamingEngine() {
|
|
2664
2842
|
goog.asserts.assert(
|
|
2665
2843
|
this.playhead_ && this.abrManager_ && this.mediaSourceEngine_ &&
|
|
2666
|
-
this.manifest_,
|
|
2844
|
+
this.cmcdManager_ && this.manifest_,
|
|
2667
2845
|
'Must not be destroyed');
|
|
2668
2846
|
|
|
2669
2847
|
/** @type {shaka.media.StreamingEngine.PlayerInterface} */
|
|
2670
2848
|
const playerInterface = {
|
|
2671
2849
|
getPresentationTime: () => this.playhead_.getTime(),
|
|
2672
2850
|
getBandwidthEstimate: () => this.abrManager_.getBandwidthEstimate(),
|
|
2851
|
+
modifySegmentRequest: (request, segmentInfo) => {
|
|
2852
|
+
this.cmcdManager_.applySegmentData(request, segmentInfo);
|
|
2853
|
+
},
|
|
2673
2854
|
mediaSourceEngine: this.mediaSourceEngine_,
|
|
2674
2855
|
netEngine: this.networkingEngine_,
|
|
2675
2856
|
onError: (error) => this.onError_(error),
|
|
2676
2857
|
onEvent: (event) => this.dispatchEvent(event),
|
|
2677
2858
|
onManifestUpdate: () => this.onManifestUpdate_(),
|
|
2678
2859
|
onSegmentAppended: () => this.onSegmentAppended_(),
|
|
2860
|
+
onInitSegmentAppended: (position, initSegment) => {
|
|
2861
|
+
const mediaQuality = initSegment.getMediaQuality();
|
|
2862
|
+
if (mediaQuality && this.qualityObserver_) {
|
|
2863
|
+
this.qualityObserver_.addMediaQualityChange(mediaQuality, position);
|
|
2864
|
+
}
|
|
2865
|
+
},
|
|
2679
2866
|
};
|
|
2680
2867
|
|
|
2681
2868
|
return new shaka.media.StreamingEngine(this.manifest_, playerInterface);
|
|
@@ -2868,6 +3055,32 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2868
3055
|
return this.config_;
|
|
2869
3056
|
}
|
|
2870
3057
|
|
|
3058
|
+
/**
|
|
3059
|
+
* Returns the ratio of video length buffered compared to buffering Goal
|
|
3060
|
+
* @return {number}
|
|
3061
|
+
* @export
|
|
3062
|
+
*/
|
|
3063
|
+
getBufferFullness() {
|
|
3064
|
+
if (this.video_) {
|
|
3065
|
+
const bufferedLength = this.video_.buffered.length;
|
|
3066
|
+
const bufferedEnd =
|
|
3067
|
+
bufferedLength ? this.video_.buffered.end(bufferedLength - 1) : 0;
|
|
3068
|
+
const bufferingGoal = this.getConfiguration().streaming.bufferingGoal;
|
|
3069
|
+
const lengthToBeBuffered = Math.min(this.video_.currentTime +
|
|
3070
|
+
bufferingGoal, this.seekRange().end);
|
|
3071
|
+
|
|
3072
|
+
if (bufferedEnd >= lengthToBeBuffered) {
|
|
3073
|
+
return 1;
|
|
3074
|
+
} else if (bufferedEnd <= this.video_.currentTime) {
|
|
3075
|
+
return 0;
|
|
3076
|
+
} else if (bufferedEnd < lengthToBeBuffered) {
|
|
3077
|
+
return ((bufferedEnd - this.video_.currentTime) /
|
|
3078
|
+
(lengthToBeBuffered - this.video_.currentTime));
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
return 0;
|
|
3082
|
+
}
|
|
3083
|
+
|
|
2871
3084
|
/**
|
|
2872
3085
|
* Reset configuration to default.
|
|
2873
3086
|
* @export
|
|
@@ -3078,6 +3291,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3078
3291
|
return {'start': 0, 'end': 0};
|
|
3079
3292
|
}
|
|
3080
3293
|
|
|
3294
|
+
/**
|
|
3295
|
+
* Go to live in a live stream.
|
|
3296
|
+
*
|
|
3297
|
+
* @export
|
|
3298
|
+
*/
|
|
3299
|
+
goToLive() {
|
|
3300
|
+
if (this.isLive()) {
|
|
3301
|
+
this.video_.currentTime = this.seekRange().end;
|
|
3302
|
+
} else {
|
|
3303
|
+
shaka.log.warning('goToLive is for live streams!');
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
|
|
3081
3307
|
/**
|
|
3082
3308
|
* Get the key system currently used by EME. If EME is not being used, this
|
|
3083
3309
|
* will return an empty string. If the player has not loaded content, this
|
|
@@ -3102,6 +3328,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3102
3328
|
return this.drmEngine_ ? this.drmEngine_.getDrmInfo() : null;
|
|
3103
3329
|
}
|
|
3104
3330
|
|
|
3331
|
+
|
|
3332
|
+
/**
|
|
3333
|
+
* Get the drm engine.
|
|
3334
|
+
* This method should only be used for testing. Applications SHOULD NOT
|
|
3335
|
+
* use this in production.
|
|
3336
|
+
*
|
|
3337
|
+
* @return {?shaka.media.DrmEngine}
|
|
3338
|
+
*/
|
|
3339
|
+
getDrmEngine() {
|
|
3340
|
+
return this.drmEngine_;
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
|
|
3105
3344
|
/**
|
|
3106
3345
|
* Get the next known expiration time for any EME session. If the session
|
|
3107
3346
|
* never expires, this will return <code>Infinity</code>. If there are no EME
|
|
@@ -3356,9 +3595,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3356
3595
|
return null;
|
|
3357
3596
|
}
|
|
3358
3597
|
const reference = imageStream.segmentIndex.get(referencePosition);
|
|
3598
|
+
const tilesLayout =
|
|
3599
|
+
reference.getTilesLayout() || imageStream.tilesLayout;
|
|
3359
3600
|
// This expression is used to detect one or more numbers (0-9) followed
|
|
3360
3601
|
// by an x and after one or more numbers (0-9)
|
|
3361
|
-
const match = /(\d+)x(\d+)/.exec(
|
|
3602
|
+
const match = /(\d+)x(\d+)/.exec(tilesLayout);
|
|
3362
3603
|
if (!match) {
|
|
3363
3604
|
shaka.log.warning('Tiles layout does not contain a valid format ' +
|
|
3364
3605
|
' (columns x rows)');
|
|
@@ -3598,36 +3839,34 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3598
3839
|
new shaka.media.PreferenceBasedCriteria(language, role || '',
|
|
3599
3840
|
/* channelCount= */ 0, /* label= */ '');
|
|
3600
3841
|
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
return Math.abs((a.video.height || 0) - (b.video.height || 0)) +
|
|
3842
|
+
const diff = (a, b) => {
|
|
3843
|
+
if (!a.video && !b.video) {
|
|
3844
|
+
return 0;
|
|
3845
|
+
} else if (!a.video || !b.video) {
|
|
3846
|
+
return Infinity;
|
|
3847
|
+
} else {
|
|
3848
|
+
return Math.abs((a.video.height || 0) - (b.video.height || 0)) +
|
|
3609
3849
|
Math.abs((a.video.width || 0) - (b.video.width || 0));
|
|
3610
|
-
|
|
3611
|
-
|
|
3850
|
+
}
|
|
3851
|
+
};
|
|
3612
3852
|
// Find the variant whose size is closest to the active variant. This
|
|
3613
3853
|
// ensures we stay at about the same resolution when just changing the
|
|
3614
3854
|
// language/role.
|
|
3615
|
-
|
|
3616
|
-
|
|
3855
|
+
const active = this.streamingEngine_.getCurrentVariant();
|
|
3856
|
+
const set =
|
|
3617
3857
|
this.currentAdaptationSetCriteria_.create(this.manifest_.variants);
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3858
|
+
let bestVariant = null;
|
|
3859
|
+
for (const curVariant of set.values()) {
|
|
3860
|
+
if (!bestVariant ||
|
|
3621
3861
|
diff(bestVariant, active) > diff(curVariant, active)) {
|
|
3622
|
-
|
|
3623
|
-
}
|
|
3624
|
-
}
|
|
3625
|
-
if (bestVariant) {
|
|
3626
|
-
const track = shaka.util.StreamUtils.variantToTrack(bestVariant);
|
|
3627
|
-
this.selectVariantTrack(track, /* clearBuffer= */ true);
|
|
3628
|
-
return;
|
|
3862
|
+
bestVariant = curVariant;
|
|
3629
3863
|
}
|
|
3630
3864
|
}
|
|
3865
|
+
if (bestVariant) {
|
|
3866
|
+
const track = shaka.util.StreamUtils.variantToTrack(bestVariant);
|
|
3867
|
+
this.selectVariantTrack(track, /* clearBuffer= */ true);
|
|
3868
|
+
return;
|
|
3869
|
+
}
|
|
3631
3870
|
|
|
3632
3871
|
// If we haven't switched yet, just use ABR to find a new track.
|
|
3633
3872
|
this.chooseVariantAndSwitch_();
|
|
@@ -3771,6 +4010,51 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3771
4010
|
return expected;
|
|
3772
4011
|
}
|
|
3773
4012
|
|
|
4013
|
+
/**
|
|
4014
|
+
* Return a list of chapters tracks.
|
|
4015
|
+
*
|
|
4016
|
+
* @return {!Array.<shaka.extern.Track>}
|
|
4017
|
+
* @export
|
|
4018
|
+
*/
|
|
4019
|
+
getChaptersTracks() {
|
|
4020
|
+
if (this.video_ && this.video_.src && this.video_.textTracks) {
|
|
4021
|
+
const textTracks = this.getChaptersTracks_();
|
|
4022
|
+
const StreamUtils = shaka.util.StreamUtils;
|
|
4023
|
+
return textTracks.map((text) => StreamUtils.html5TextTrackToTrack(text));
|
|
4024
|
+
} else {
|
|
4025
|
+
return [];
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
|
|
4029
|
+
/**
|
|
4030
|
+
* This returns the list of chapters.
|
|
4031
|
+
*
|
|
4032
|
+
* @param {string} language
|
|
4033
|
+
* @return {!Array.<shaka.extern.Chapter>}
|
|
4034
|
+
* @export
|
|
4035
|
+
*/
|
|
4036
|
+
getChapters(language) {
|
|
4037
|
+
const LanguageUtils = shaka.util.LanguageUtils;
|
|
4038
|
+
const inputlanguage = LanguageUtils.normalize(language);
|
|
4039
|
+
const chaptersTracks = this.getChaptersTracks_();
|
|
4040
|
+
const chaptersTrack = chaptersTracks
|
|
4041
|
+
.find((t) => LanguageUtils.normalize(t.language) == inputlanguage);
|
|
4042
|
+
if (!chaptersTrack || !chaptersTrack.cues) {
|
|
4043
|
+
return [];
|
|
4044
|
+
}
|
|
4045
|
+
const chapters = [];
|
|
4046
|
+
for (const cue of chaptersTrack.cues) {
|
|
4047
|
+
/** @type {shaka.extern.Chapter} */
|
|
4048
|
+
const chapter = {
|
|
4049
|
+
title: cue.text,
|
|
4050
|
+
startTime: cue.startTime,
|
|
4051
|
+
endTime: cue.endTime,
|
|
4052
|
+
};
|
|
4053
|
+
chapters.push(chapter);
|
|
4054
|
+
}
|
|
4055
|
+
return chapters;
|
|
4056
|
+
}
|
|
4057
|
+
|
|
3774
4058
|
/**
|
|
3775
4059
|
* Ignore the TextTracks with the 'metadata' or 'chapters' kind, or the one
|
|
3776
4060
|
* generated by the SimpleTextDisplayer.
|
|
@@ -3799,6 +4083,19 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3799
4083
|
.filter((t) => t.kind == 'metadata');
|
|
3800
4084
|
}
|
|
3801
4085
|
|
|
4086
|
+
/**
|
|
4087
|
+
* Get the TextTracks with the 'chapters' kind.
|
|
4088
|
+
*
|
|
4089
|
+
* @return {!Array.<TextTrack>}
|
|
4090
|
+
* @private
|
|
4091
|
+
*/
|
|
4092
|
+
getChaptersTracks_() {
|
|
4093
|
+
goog.asserts.assert(this.video_.textTracks,
|
|
4094
|
+
'TextTracks should be valid.');
|
|
4095
|
+
return Array.from(this.video_.textTracks)
|
|
4096
|
+
.filter((t) => t.kind == 'chapters');
|
|
4097
|
+
}
|
|
4098
|
+
|
|
3802
4099
|
/**
|
|
3803
4100
|
* Enable or disable the text displayer. If the player is in an unloaded
|
|
3804
4101
|
* state, the request will be applied next time content is loaded.
|
|
@@ -4100,16 +4397,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4100
4397
|
if (!mimeType) {
|
|
4101
4398
|
// Try using the uri extension.
|
|
4102
4399
|
const extension = shaka.media.ManifestParser.getExtension(uri);
|
|
4103
|
-
mimeType =
|
|
4104
|
-
'sbv': 'text/x-subviewer',
|
|
4105
|
-
'srt': 'text/srt',
|
|
4106
|
-
'vtt': 'text/vtt',
|
|
4107
|
-
'webvtt': 'text/vtt',
|
|
4108
|
-
'ttml': 'application/ttml+xml',
|
|
4109
|
-
'lrc': 'application/x-subtitle-lrc',
|
|
4110
|
-
'ssa': 'text/x-ssa',
|
|
4111
|
-
'ass': 'text/x-ssa',
|
|
4112
|
-
}[extension];
|
|
4400
|
+
mimeType = shaka.Player.TEXT_EXTENSIONS_TO_MIME_TYPES_[extension];
|
|
4113
4401
|
|
|
4114
4402
|
if (!mimeType) {
|
|
4115
4403
|
shaka.log.error(
|
|
@@ -4138,7 +4426,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4138
4426
|
}
|
|
4139
4427
|
const trackElement =
|
|
4140
4428
|
/** @type {!HTMLTrackElement} */(document.createElement('track'));
|
|
4141
|
-
trackElement.src = uri;
|
|
4429
|
+
trackElement.src = this.cmcdManager_.appendTextTrackData(uri);
|
|
4142
4430
|
trackElement.label = label || '';
|
|
4143
4431
|
trackElement.kind = kind;
|
|
4144
4432
|
trackElement.srclang = language;
|
|
@@ -4257,74 +4545,23 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4257
4545
|
}
|
|
4258
4546
|
|
|
4259
4547
|
if (!mimeType) {
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
mimeType = {
|
|
4263
|
-
'sbv': 'text/x-subviewer',
|
|
4264
|
-
'srt': 'text/srt',
|
|
4265
|
-
'vtt': 'text/vtt',
|
|
4266
|
-
'webvtt': 'text/vtt',
|
|
4267
|
-
'ttml': 'application/ttml+xml',
|
|
4268
|
-
'lrc': 'application/x-subtitle-lrc',
|
|
4269
|
-
'ssa': 'text/x-ssa',
|
|
4270
|
-
'ass': 'text/x-ssa',
|
|
4271
|
-
}[extension];
|
|
4272
|
-
|
|
4273
|
-
if (!mimeType) {
|
|
4274
|
-
try {
|
|
4275
|
-
goog.asserts.assert(
|
|
4276
|
-
this.networkingEngine_, 'Need networking engine.');
|
|
4277
|
-
// eslint-disable-next-line require-atomic-updates
|
|
4278
|
-
mimeType = await shaka.media.ManifestParser.getMimeType(uri,
|
|
4279
|
-
this.networkingEngine_,
|
|
4280
|
-
this.config_.streaming.retryParameters);
|
|
4281
|
-
} catch (error) {}
|
|
4282
|
-
}
|
|
4548
|
+
mimeType = await this.getTextMimetype_(uri);
|
|
4549
|
+
}
|
|
4283
4550
|
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
shaka.util.Error.Severity.RECOVERABLE,
|
|
4290
|
-
shaka.util.Error.Category.TEXT,
|
|
4291
|
-
shaka.util.Error.Code.TEXT_COULD_NOT_GUESS_MIME_TYPE,
|
|
4292
|
-
extension);
|
|
4293
|
-
}
|
|
4551
|
+
let adCuePoints = [];
|
|
4552
|
+
if (this.adManager_) {
|
|
4553
|
+
try {
|
|
4554
|
+
adCuePoints = this.adManager_.getServerSideCuePoints();
|
|
4555
|
+
} catch (error) {}
|
|
4294
4556
|
}
|
|
4295
4557
|
|
|
4296
4558
|
if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
|
|
4297
|
-
if (mimeType != 'text/vtt') {
|
|
4298
|
-
goog.asserts.assert(
|
|
4299
|
-
this.networkingEngine_, 'Need networking engine.');
|
|
4300
|
-
const data = await this.getTextData_(uri,
|
|
4301
|
-
this.networkingEngine_,
|
|
4302
|
-
this.config_.streaming.retryParameters);
|
|
4303
|
-
const vvtText = this.convertToWebVTT_(data, mimeType);
|
|
4304
|
-
const blob = new Blob([vvtText], {type: 'text/vtt'});
|
|
4305
|
-
uri = shaka.media.MediaSourceEngine.createObjectURL(blob);
|
|
4306
|
-
mimeType = 'text/vtt';
|
|
4307
|
-
}
|
|
4308
4559
|
if (forced) {
|
|
4309
4560
|
// See: https://github.com/whatwg/html/issues/4472
|
|
4310
4561
|
kind = 'forced';
|
|
4311
4562
|
}
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
trackElement.src = uri;
|
|
4315
|
-
trackElement.label = label || '';
|
|
4316
|
-
trackElement.kind = kind;
|
|
4317
|
-
trackElement.srclang = language;
|
|
4318
|
-
// Because we're pulling in the text track file via Javascript, the
|
|
4319
|
-
// same-origin policy applies. If you'd like to have a player served
|
|
4320
|
-
// from one domain, but the text track served from another, you'll
|
|
4321
|
-
// need to enable CORS in order to do so. In addition to enabling CORS
|
|
4322
|
-
// on the server serving the text tracks, you will need to add the
|
|
4323
|
-
// crossorigin attribute to the video element itself.
|
|
4324
|
-
if (!this.video_.getAttribute('crossorigin')) {
|
|
4325
|
-
this.video_.setAttribute('crossorigin', 'anonymous');
|
|
4326
|
-
}
|
|
4327
|
-
this.video_.appendChild(trackElement);
|
|
4563
|
+
await this.addSrcTrackElement_(uri, language, kind, mimeType, label || '',
|
|
4564
|
+
adCuePoints);
|
|
4328
4565
|
const textTracks = this.getTextTracks();
|
|
4329
4566
|
const srcTrack = textTracks.find((t) => {
|
|
4330
4567
|
return t.language == language &&
|
|
@@ -4354,6 +4591,18 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4354
4591
|
shaka.util.Error.Code.CANNOT_ADD_EXTERNAL_TEXT_TO_LIVE_STREAM);
|
|
4355
4592
|
}
|
|
4356
4593
|
|
|
4594
|
+
if (adCuePoints.length) {
|
|
4595
|
+
goog.asserts.assert(
|
|
4596
|
+
this.networkingEngine_, 'Need networking engine.');
|
|
4597
|
+
const data = await this.getTextData_(uri,
|
|
4598
|
+
this.networkingEngine_,
|
|
4599
|
+
this.config_.streaming.retryParameters);
|
|
4600
|
+
const vvtText = this.convertToWebVTT_(data, mimeType, adCuePoints);
|
|
4601
|
+
const blob = new Blob([vvtText], {type: 'text/vtt'});
|
|
4602
|
+
uri = shaka.media.MediaSourceEngine.createObjectURL(blob);
|
|
4603
|
+
mimeType = 'text/vtt';
|
|
4604
|
+
}
|
|
4605
|
+
|
|
4357
4606
|
/** @type {shaka.extern.Stream} */
|
|
4358
4607
|
const stream = {
|
|
4359
4608
|
id: this.nextExternalStreamId_++,
|
|
@@ -4399,6 +4648,138 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4399
4648
|
return shaka.util.StreamUtils.textStreamToTrack(stream);
|
|
4400
4649
|
}
|
|
4401
4650
|
|
|
4651
|
+
/**
|
|
4652
|
+
* Adds the given chapters track to the loaded manifest. <code>load()</code>
|
|
4653
|
+
* must resolve before calling. The presentation must have a duration.
|
|
4654
|
+
*
|
|
4655
|
+
* This returns the created track.
|
|
4656
|
+
*
|
|
4657
|
+
* @param {string} uri
|
|
4658
|
+
* @param {string} language
|
|
4659
|
+
* @param {string=} mimeType
|
|
4660
|
+
* @return {!Promise.<shaka.extern.Track>}
|
|
4661
|
+
* @export
|
|
4662
|
+
*/
|
|
4663
|
+
async addChaptersTrack(uri, language, mimeType) {
|
|
4664
|
+
if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE &&
|
|
4665
|
+
this.loadMode_ != shaka.Player.LoadMode.SRC_EQUALS) {
|
|
4666
|
+
shaka.log.error(
|
|
4667
|
+
'Must call load() and wait for it to resolve before adding ' +
|
|
4668
|
+
'chapters tracks.');
|
|
4669
|
+
throw new shaka.util.Error(
|
|
4670
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
4671
|
+
shaka.util.Error.Category.PLAYER,
|
|
4672
|
+
shaka.util.Error.Code.CONTENT_NOT_LOADED);
|
|
4673
|
+
}
|
|
4674
|
+
|
|
4675
|
+
if (!mimeType) {
|
|
4676
|
+
mimeType = await this.getTextMimetype_(uri);
|
|
4677
|
+
}
|
|
4678
|
+
let adCuePoints = [];
|
|
4679
|
+
if (this.adManager_) {
|
|
4680
|
+
try {
|
|
4681
|
+
adCuePoints = this.adManager_.getServerSideCuePoints();
|
|
4682
|
+
} catch (error) {}
|
|
4683
|
+
}
|
|
4684
|
+
await this.addSrcTrackElement_(uri, language, /* kind= */ 'chapters',
|
|
4685
|
+
mimeType, /* label= */ '', adCuePoints);
|
|
4686
|
+
const chaptersTracks = this.getChaptersTracks();
|
|
4687
|
+
const chaptersTrack = chaptersTracks.find((t) => {
|
|
4688
|
+
return t.language == language;
|
|
4689
|
+
});
|
|
4690
|
+
if (chaptersTrack) {
|
|
4691
|
+
const html5ChaptersTracks = this.getChaptersTracks_();
|
|
4692
|
+
for (const html5ChaptersTrack of html5ChaptersTracks) {
|
|
4693
|
+
this.processChaptersTrack_(html5ChaptersTrack);
|
|
4694
|
+
}
|
|
4695
|
+
return chaptersTrack;
|
|
4696
|
+
}
|
|
4697
|
+
// This should not happen, but there are browser implementations that may
|
|
4698
|
+
// not support the Track element.
|
|
4699
|
+
shaka.log.error('Cannot add this text when loaded with src=');
|
|
4700
|
+
throw new shaka.util.Error(
|
|
4701
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
4702
|
+
shaka.util.Error.Category.TEXT,
|
|
4703
|
+
shaka.util.Error.Code.CANNOT_ADD_EXTERNAL_TEXT_TO_SRC_EQUALS);
|
|
4704
|
+
}
|
|
4705
|
+
|
|
4706
|
+
/**
|
|
4707
|
+
* @param {string} uri
|
|
4708
|
+
* @return {!Promise.<string>}
|
|
4709
|
+
* @private
|
|
4710
|
+
*/
|
|
4711
|
+
async getTextMimetype_(uri) {
|
|
4712
|
+
// Try using the uri extension.
|
|
4713
|
+
const extension = shaka.media.ManifestParser.getExtension(uri);
|
|
4714
|
+
let mimeType = shaka.Player.TEXT_EXTENSIONS_TO_MIME_TYPES_[extension];
|
|
4715
|
+
|
|
4716
|
+
if (mimeType) {
|
|
4717
|
+
return mimeType;
|
|
4718
|
+
}
|
|
4719
|
+
|
|
4720
|
+
try {
|
|
4721
|
+
goog.asserts.assert(
|
|
4722
|
+
this.networkingEngine_, 'Need networking engine.');
|
|
4723
|
+
// eslint-disable-next-line require-atomic-updates
|
|
4724
|
+
mimeType = await shaka.media.ManifestParser.getMimeType(uri,
|
|
4725
|
+
this.networkingEngine_,
|
|
4726
|
+
this.config_.streaming.retryParameters);
|
|
4727
|
+
} catch (error) {}
|
|
4728
|
+
|
|
4729
|
+
if (mimeType) {
|
|
4730
|
+
return mimeType;
|
|
4731
|
+
}
|
|
4732
|
+
|
|
4733
|
+
shaka.log.error(
|
|
4734
|
+
'The mimeType has not been provided and it could not be deduced ' +
|
|
4735
|
+
'from its extension.');
|
|
4736
|
+
throw new shaka.util.Error(
|
|
4737
|
+
shaka.util.Error.Severity.RECOVERABLE,
|
|
4738
|
+
shaka.util.Error.Category.TEXT,
|
|
4739
|
+
shaka.util.Error.Code.TEXT_COULD_NOT_GUESS_MIME_TYPE,
|
|
4740
|
+
extension);
|
|
4741
|
+
}
|
|
4742
|
+
|
|
4743
|
+
/**
|
|
4744
|
+
* @param {string} uri
|
|
4745
|
+
* @param {string} language
|
|
4746
|
+
* @param {string} kind
|
|
4747
|
+
* @param {string} mimeType
|
|
4748
|
+
* @param {string} label
|
|
4749
|
+
* @param {!Array.<!shaka.extern.AdCuePoint>} adCuePoints
|
|
4750
|
+
* @private
|
|
4751
|
+
*/
|
|
4752
|
+
async addSrcTrackElement_(uri, language, kind, mimeType, label,
|
|
4753
|
+
adCuePoints) {
|
|
4754
|
+
if (mimeType != 'text/vtt' || adCuePoints.length) {
|
|
4755
|
+
goog.asserts.assert(
|
|
4756
|
+
this.networkingEngine_, 'Need networking engine.');
|
|
4757
|
+
const data = await this.getTextData_(uri,
|
|
4758
|
+
this.networkingEngine_,
|
|
4759
|
+
this.config_.streaming.retryParameters);
|
|
4760
|
+
const vvtText = this.convertToWebVTT_(data, mimeType, adCuePoints);
|
|
4761
|
+
const blob = new Blob([vvtText], {type: 'text/vtt'});
|
|
4762
|
+
uri = shaka.media.MediaSourceEngine.createObjectURL(blob);
|
|
4763
|
+
mimeType = 'text/vtt';
|
|
4764
|
+
}
|
|
4765
|
+
const trackElement =
|
|
4766
|
+
/** @type {!HTMLTrackElement} */(document.createElement('track'));
|
|
4767
|
+
trackElement.src = this.cmcdManager_.appendTextTrackData(uri);
|
|
4768
|
+
trackElement.label = label;
|
|
4769
|
+
trackElement.kind = kind;
|
|
4770
|
+
trackElement.srclang = language;
|
|
4771
|
+
// Because we're pulling in the text track file via Javascript, the
|
|
4772
|
+
// same-origin policy applies. If you'd like to have a player served
|
|
4773
|
+
// from one domain, but the text track served from another, you'll
|
|
4774
|
+
// need to enable CORS in order to do so. In addition to enabling CORS
|
|
4775
|
+
// on the server serving the text tracks, you will need to add the
|
|
4776
|
+
// crossorigin attribute to the video element itself.
|
|
4777
|
+
if (!this.video_.getAttribute('crossorigin')) {
|
|
4778
|
+
this.video_.setAttribute('crossorigin', 'anonymous');
|
|
4779
|
+
}
|
|
4780
|
+
this.video_.appendChild(trackElement);
|
|
4781
|
+
}
|
|
4782
|
+
|
|
4402
4783
|
/**
|
|
4403
4784
|
* @param {string} uri
|
|
4404
4785
|
* @param {!shaka.net.NetworkingEngine} netEngine
|
|
@@ -4412,6 +4793,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4412
4793
|
const request = shaka.net.NetworkingEngine.makeRequest([uri], retryParams);
|
|
4413
4794
|
request.method = 'GET';
|
|
4414
4795
|
|
|
4796
|
+
this.cmcdManager_.applyTextData(request);
|
|
4797
|
+
|
|
4415
4798
|
const response = await netEngine.request(type, request).promise;
|
|
4416
4799
|
|
|
4417
4800
|
return response.data;
|
|
@@ -4423,10 +4806,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4423
4806
|
*
|
|
4424
4807
|
* @param {BufferSource} buffer
|
|
4425
4808
|
* @param {string} mimeType
|
|
4809
|
+
* @param {!Array.<!shaka.extern.AdCuePoint>} adCuePoints
|
|
4426
4810
|
* @return {string}
|
|
4427
4811
|
* @private
|
|
4428
4812
|
*/
|
|
4429
|
-
convertToWebVTT_(buffer, mimeType) {
|
|
4813
|
+
convertToWebVTT_(buffer, mimeType, adCuePoints) {
|
|
4430
4814
|
const factory = shaka.text.TextEngine.findParser(mimeType);
|
|
4431
4815
|
if (factory) {
|
|
4432
4816
|
const obj = factory();
|
|
@@ -4437,7 +4821,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4437
4821
|
};
|
|
4438
4822
|
const data = shaka.util.BufferUtils.toUint8(buffer);
|
|
4439
4823
|
const cues = obj.parseMedia(data, time);
|
|
4440
|
-
return shaka.text.WebVttGenerator.convert(cues);
|
|
4824
|
+
return shaka.text.WebVttGenerator.convert(cues, adCuePoints);
|
|
4441
4825
|
}
|
|
4442
4826
|
throw new shaka.util.Error(
|
|
4443
4827
|
shaka.util.Error.Severity.CRITICAL,
|
|
@@ -4679,8 +5063,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4679
5063
|
this.streamingEngine_.getCurrentVariant() : null;
|
|
4680
5064
|
|
|
4681
5065
|
await shaka.util.StreamUtils.filterManifest(
|
|
4682
|
-
this.drmEngine_, currentVariant, manifest
|
|
4683
|
-
this.config_.useMediaCapabilities);
|
|
5066
|
+
this.drmEngine_, currentVariant, manifest);
|
|
4684
5067
|
this.checkPlayableVariants_(manifest);
|
|
4685
5068
|
}
|
|
4686
5069
|
|
|
@@ -4809,6 +5192,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4809
5192
|
|
|
4810
5193
|
if (loaded) {
|
|
4811
5194
|
this.playRateController_.setBuffering(isBuffering);
|
|
5195
|
+
if (this.cmcdManager_) {
|
|
5196
|
+
this.cmcdManager_.setBuffering(isBuffering);
|
|
5197
|
+
}
|
|
4812
5198
|
this.updateStateHistory_();
|
|
4813
5199
|
}
|
|
4814
5200
|
|
|
@@ -4984,9 +5370,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4984
5370
|
if (chosenVariant) {
|
|
4985
5371
|
this.switchVariant_(chosenVariant, /* fromAdaptation= */ true,
|
|
4986
5372
|
/* clearBuffers= */ true, /* safeMargin= */ 0);
|
|
4987
|
-
// Send an adaptation event so that the UI can show the new
|
|
4988
|
-
// language/tracks.
|
|
4989
|
-
this.onAdaptation_();
|
|
4990
5373
|
}
|
|
4991
5374
|
}
|
|
4992
5375
|
|
|
@@ -5017,8 +5400,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5017
5400
|
oldTrack = shaka.util.StreamUtils.variantToTrack(currentVariant);
|
|
5018
5401
|
}
|
|
5019
5402
|
const newTrack = shaka.util.StreamUtils.variantToTrack(variant);
|
|
5020
|
-
|
|
5021
|
-
|
|
5403
|
+
|
|
5404
|
+
if (fromAdaptation) {
|
|
5405
|
+
// Dispatch an 'adaptation' event
|
|
5406
|
+
this.onAdaptation_(oldTrack, newTrack);
|
|
5407
|
+
} else {
|
|
5408
|
+
// Dispatch a 'variantchanged' event
|
|
5409
|
+
this.onVariantChanged_(oldTrack, newTrack);
|
|
5410
|
+
}
|
|
5022
5411
|
}
|
|
5023
5412
|
|
|
5024
5413
|
/**
|
|
@@ -5162,19 +5551,23 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5162
5551
|
return;
|
|
5163
5552
|
}
|
|
5164
5553
|
|
|
5165
|
-
this.
|
|
5166
|
-
|
|
5167
|
-
this.onAdaptation_();
|
|
5554
|
+
this.switchVariant_(variant, /* fromAdaptation= */ true,
|
|
5555
|
+
clearBuffer, safeMargin);
|
|
5168
5556
|
}
|
|
5169
5557
|
|
|
5170
5558
|
/**
|
|
5171
5559
|
* Dispatches an 'adaptation' event.
|
|
5560
|
+
* @param {?shaka.extern.Track} from
|
|
5561
|
+
* @param {shaka.extern.Track} to
|
|
5172
5562
|
* @private
|
|
5173
5563
|
*/
|
|
5174
|
-
onAdaptation_() {
|
|
5564
|
+
onAdaptation_(from, to) {
|
|
5175
5565
|
// Delay the 'adaptation' event so that StreamingEngine has time to absorb
|
|
5176
5566
|
// the changes before the user tries to query it.
|
|
5177
|
-
const
|
|
5567
|
+
const data = new Map()
|
|
5568
|
+
.set('oldTrack', from)
|
|
5569
|
+
.set('newTrack', to);
|
|
5570
|
+
const event = this.makeEvent_(shaka.Player.EventName.Adaptation, data);
|
|
5178
5571
|
this.delayDispatchEvent_(event);
|
|
5179
5572
|
}
|
|
5180
5573
|
|
|
@@ -5276,6 +5669,38 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5276
5669
|
this.dispatchEvent(this.makeEvent_(eventName, data));
|
|
5277
5670
|
}
|
|
5278
5671
|
|
|
5672
|
+
/**
|
|
5673
|
+
* When notified of a media quality change we need to emit a
|
|
5674
|
+
* MediaQualityChange event to the app.
|
|
5675
|
+
*
|
|
5676
|
+
* @param {shaka.extern.MediaQualityInfo} mediaQuality
|
|
5677
|
+
* @param {number} position
|
|
5678
|
+
*
|
|
5679
|
+
* @private
|
|
5680
|
+
*/
|
|
5681
|
+
onMediaQualityChange_(mediaQuality, position) {
|
|
5682
|
+
// Always make a copy to avoid exposing our internal data to the app.
|
|
5683
|
+
const clone = {
|
|
5684
|
+
bandwidth: mediaQuality.bandwidth,
|
|
5685
|
+
audioSamplingRate: mediaQuality.audioSamplingRate,
|
|
5686
|
+
codecs: mediaQuality.codecs,
|
|
5687
|
+
contentType: mediaQuality.contentType,
|
|
5688
|
+
frameRate: mediaQuality.frameRate,
|
|
5689
|
+
height: mediaQuality.height,
|
|
5690
|
+
mimeType: mediaQuality.mimeType,
|
|
5691
|
+
channelsCount: mediaQuality.channelsCount,
|
|
5692
|
+
pixelAspectRatio: mediaQuality.pixelAspectRatio,
|
|
5693
|
+
width: mediaQuality.width,
|
|
5694
|
+
};
|
|
5695
|
+
|
|
5696
|
+
const data = new Map()
|
|
5697
|
+
.set('mediaQuality', clone)
|
|
5698
|
+
.set('position', position);
|
|
5699
|
+
|
|
5700
|
+
this.dispatchEvent(this.makeEvent_(
|
|
5701
|
+
shaka.Player.EventName.MediaQualityChanged, data));
|
|
5702
|
+
}
|
|
5703
|
+
|
|
5279
5704
|
/**
|
|
5280
5705
|
* Turn the media element's error object into a Shaka Player error object.
|
|
5281
5706
|
*
|
|
@@ -6039,6 +6464,8 @@ shaka.Player.EventName = {
|
|
|
6039
6464
|
AbrStatusChanged: 'abrstatuschanged',
|
|
6040
6465
|
Adaptation: 'adaptation',
|
|
6041
6466
|
Buffering: 'buffering',
|
|
6467
|
+
DownloadFailed: 'downloadfailed',
|
|
6468
|
+
DownloadHeadersReceived: 'downloadheadersreceived',
|
|
6042
6469
|
DrmSessionUpdate: 'drmsessionupdate',
|
|
6043
6470
|
Emsg: 'emsg',
|
|
6044
6471
|
Error: 'error',
|
|
@@ -6047,6 +6474,7 @@ shaka.Player.EventName = {
|
|
|
6047
6474
|
Loaded: 'loaded',
|
|
6048
6475
|
Loading: 'loading',
|
|
6049
6476
|
ManifestParsed: 'manifestparsed',
|
|
6477
|
+
MediaQualityChanged: 'mediaqualitychanged',
|
|
6050
6478
|
Metadata: 'metadata',
|
|
6051
6479
|
OnStateChange: 'onstatechange',
|
|
6052
6480
|
OnStateIdle: 'onstateidle',
|
|
@@ -6102,7 +6530,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6102
6530
|
* @define {string} A version number taken from git at compile time.
|
|
6103
6531
|
* @export
|
|
6104
6532
|
*/
|
|
6105
|
-
shaka.Player.version = 'v3.
|
|
6533
|
+
shaka.Player.version = 'v3.3.0-uncompiled';
|
|
6106
6534
|
|
|
6107
6535
|
// Initialize the deprecation system using the version string we just set
|
|
6108
6536
|
// on the player.
|
|
@@ -6129,6 +6557,47 @@ shaka.Player.supportPlugins_ = {};
|
|
|
6129
6557
|
shaka.Player.adManagerFactory_ = null;
|
|
6130
6558
|
|
|
6131
6559
|
|
|
6560
|
+
/**
|
|
6561
|
+
* @const {!Object.<string, string>}
|
|
6562
|
+
* @private
|
|
6563
|
+
*/
|
|
6564
|
+
shaka.Player.SRC_EQUAL_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
6565
|
+
'mp4': 'video/mp4',
|
|
6566
|
+
'm4v': 'video/mp4',
|
|
6567
|
+
'm4a': 'audio/mp4',
|
|
6568
|
+
'webm': 'video/webm',
|
|
6569
|
+
'weba': 'audio/webm',
|
|
6570
|
+
'mkv': 'video/webm', // Chromium browsers supports it.
|
|
6571
|
+
'ts': 'video/mp2t',
|
|
6572
|
+
'ogv': 'video/ogg',
|
|
6573
|
+
'ogg': 'audio/ogg',
|
|
6574
|
+
'mpg': 'video/mpeg',
|
|
6575
|
+
'mpeg': 'video/mpeg',
|
|
6576
|
+
'm3u8': 'application/x-mpegurl',
|
|
6577
|
+
'mpd': 'application/dash+xml',
|
|
6578
|
+
'mp3': 'audio/mpeg',
|
|
6579
|
+
'aac': 'audio/aac',
|
|
6580
|
+
'flac': 'audio/flac',
|
|
6581
|
+
'wav': 'audio/wav',
|
|
6582
|
+
};
|
|
6583
|
+
|
|
6584
|
+
|
|
6585
|
+
/**
|
|
6586
|
+
* @const {!Object.<string, string>}
|
|
6587
|
+
* @private
|
|
6588
|
+
*/
|
|
6589
|
+
shaka.Player.TEXT_EXTENSIONS_TO_MIME_TYPES_ = {
|
|
6590
|
+
'sbv': 'text/x-subviewer',
|
|
6591
|
+
'srt': 'text/srt',
|
|
6592
|
+
'vtt': 'text/vtt',
|
|
6593
|
+
'webvtt': 'text/vtt',
|
|
6594
|
+
'ttml': 'application/ttml+xml',
|
|
6595
|
+
'lrc': 'application/x-subtitle-lrc',
|
|
6596
|
+
'ssa': 'text/x-ssa',
|
|
6597
|
+
'ass': 'text/x-ssa',
|
|
6598
|
+
};
|
|
6599
|
+
|
|
6600
|
+
|
|
6132
6601
|
/**
|
|
6133
6602
|
* @const {string}
|
|
6134
6603
|
*/
|