shaka-player 4.0.2 → 4.1.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/CHANGELOG.md +25 -18
- package/CONTRIBUTORS +1 -0
- package/demo/common/message_ids.js +1 -0
- package/demo/config.js +3 -1
- package/demo/locales/en.json +1 -0
- package/demo/locales/source.json +4 -0
- package/dist/demo.compiled.debug.js +4 -4
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +4 -4
- package/dist/demo.compiled.map +2 -2
- package/dist/deps.js +2 -2
- package/dist/locales.js +31 -31
- package/dist/receiver.compiled.debug.js +2 -2
- package/dist/receiver.compiled.debug.map +2 -2
- package/dist/receiver.compiled.js +2 -2
- package/dist/receiver.compiled.map +2 -2
- package/dist/shaka-player.compiled.d.ts +14 -8
- package/dist/shaka-player.compiled.debug.d.ts +14 -8
- package/dist/shaka-player.compiled.debug.externs.js +10 -2
- package/dist/shaka-player.compiled.debug.js +165 -157
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +10 -2
- package/dist/shaka-player.compiled.js +758 -754
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +15 -9
- package/dist/shaka-player.ui.debug.d.ts +15 -9
- package/dist/shaka-player.ui.debug.externs.js +10 -2
- package/dist/shaka-player.ui.debug.js +198 -186
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +10 -2
- package/dist/shaka-player.ui.js +931 -928
- package/dist/shaka-player.ui.map +3 -3
- package/docs/design/chromecast.md +3 -1
- package/docs/tutorials/drm-config.md +16 -0
- package/docs/tutorials/ui.md +26 -0
- package/externs/chromecast.js +9 -2
- package/externs/shaka/manifest.js +7 -0
- package/externs/shaka/player.js +21 -2
- package/externs/shaka/text.js +7 -0
- package/lib/cast/cast_proxy.js +19 -8
- package/lib/cast/cast_receiver.js +3 -3
- package/lib/cast/cast_sender.js +12 -2
- package/lib/hls/hls_parser.js +12 -1
- package/lib/media/drm_engine.js +17 -8
- package/lib/media/gap_jumping_controller.js +23 -1
- package/lib/media/playhead.js +47 -4
- package/lib/media/stall_detector.js +19 -2
- package/lib/media/streaming_engine.js +8 -1
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/player.js +175 -69
- package/lib/text/cue.js +6 -0
- package/lib/text/ui_text_displayer.js +1 -0
- package/lib/text/vtt_text_parser.js +4 -0
- package/lib/util/error.js +6 -0
- package/lib/util/fake_event.js +39 -0
- package/lib/util/player_configuration.js +2 -0
- package/lib/util/stats.js +28 -1
- package/lib/util/stream_utils.js +7 -0
- package/package.json +1 -1
- package/test/cast/cast_proxy_unit.js +3 -1
- package/test/cast/cast_sender_unit.js +21 -6
- package/test/demo/demo_unit.js +2 -1
- package/test/media/adaptation_set_unit.js +1 -0
- package/test/media/playhead_unit.js +114 -25
- package/test/media/stall_detector_unit.js +20 -2
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +1 -0
- package/test/player_integration.js +3 -0
- package/test/player_unit.js +142 -1
- package/test/test/util/manifest_generator.js +2 -0
- package/test/test/util/simple_fakes.js +14 -0
- package/test/test/util/streaming_engine_util.js +1 -0
- package/test/text/vtt_text_parser_unit.js +4 -0
- package/test/ui/ui_unit.js +18 -5
- package/test/util/stream_utils_unit.js +108 -0
- package/ui/controls.js +12 -8
- package/ui/externs/ui.js +10 -1
- package/ui/statistics_button.js +10 -0
- package/ui/ui.js +12 -1
|
@@ -26,11 +26,13 @@ the `Player` and `HTMLMediaElement` objects. Receiver apps only have to worry
|
|
|
26
26
|
about their UI, while the `CastReceiver` takes care of playback and
|
|
27
27
|
communication.
|
|
28
28
|
|
|
29
|
+
To enable an Android (TV) receiver apps set `androidReceiverCompatible` to true.
|
|
30
|
+
|
|
29
31
|
|
|
30
32
|
#### `CastProxy` API sketch
|
|
31
33
|
|
|
32
34
|
```js
|
|
33
|
-
new shaka.cast.CastProxy(video, player, receiverAppId)
|
|
35
|
+
new shaka.cast.CastProxy(video, player, receiverAppId, androidReceiverCompatible)
|
|
34
36
|
|
|
35
37
|
// Also destroys the underlying local Player object
|
|
36
38
|
shaka.cast.CastProxy.prototype.destroy() => Promise
|
|
@@ -75,6 +75,22 @@ player.configure({
|
|
|
75
75
|
If the browser supports it and you configured a license server URL for it, we'll
|
|
76
76
|
use it.
|
|
77
77
|
|
|
78
|
+
Alternative there are a config for make a mapping of keysystem if you know that
|
|
79
|
+
is broadly supported. For example, `com.microsoft.playready.recommendation`:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
player.configure({
|
|
83
|
+
drm: {
|
|
84
|
+
keySystemsMapping: {
|
|
85
|
+
'com.microsoft.playready': 'com.microsoft.playready.recommendation',
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
With the previous configuration you will choose the `recommendation` keySystem
|
|
92
|
+
when your manifest (HLS or DASH) uses PlayReady.
|
|
93
|
+
|
|
78
94
|
|
|
79
95
|
#### Clear Key
|
|
80
96
|
|
package/docs/tutorials/ui.md
CHANGED
|
@@ -130,6 +130,23 @@ Next, let's add a listener to the 'caststatuschanged' event in myapp.js:
|
|
|
130
130
|
}
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
+
|
|
134
|
+
#### Enabling Android Receiver Apps
|
|
135
|
+
|
|
136
|
+
If you'd like to take advantage of Android Receiver App support,
|
|
137
|
+
you will need to provide a boolean flag to enable support for
|
|
138
|
+
casting to an Android receiver app.
|
|
139
|
+
|
|
140
|
+
```html
|
|
141
|
+
<div data-shaka-player-container style="max-width:40em"
|
|
142
|
+
data-shaka-player-cast-receiver-id="E7271BEC"
|
|
143
|
+
data-shaka-player-cast-android-receiver-compatible="true">
|
|
144
|
+
<!-- The manifest url in the src attribute will be automatically loaded -->
|
|
145
|
+
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"
|
|
146
|
+
src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"></video>
|
|
147
|
+
</div
|
|
148
|
+
|
|
149
|
+
|
|
133
150
|
#### Providing source(s) for auto load.
|
|
134
151
|
|
|
135
152
|
It's also possible to provide the `src` attribute on the `<video>` element
|
|
@@ -200,6 +217,15 @@ const controls = ui.getControls();
|
|
|
200
217
|
// your API calls will be routed to the remote playback session.
|
|
201
218
|
const player = controls.getPlayer();
|
|
202
219
|
const video = controls.getVideo();
|
|
220
|
+
|
|
221
|
+
// Programatically configure the Chromecast Receiver App Id and Android
|
|
222
|
+
// Receiver Compatability.
|
|
223
|
+
ui.configure({
|
|
224
|
+
// Set the castReceiverAppId
|
|
225
|
+
'castReceiverAppId': 'BBED8D28',
|
|
226
|
+
// Enable casting to native Android Apps (e.g. Android TV Apps)
|
|
227
|
+
'castAndroidReceiverCompatible': true,
|
|
228
|
+
});
|
|
203
229
|
```
|
|
204
230
|
|
|
205
231
|
|
package/externs/chromecast.js
CHANGED
|
@@ -270,6 +270,13 @@ chrome.cast.Session = class {
|
|
|
270
270
|
|
|
271
271
|
|
|
272
272
|
chrome.cast.SessionRequest = class {
|
|
273
|
-
/**
|
|
274
|
-
|
|
273
|
+
/**
|
|
274
|
+
* @param {string} appId
|
|
275
|
+
* @param {Array.<Object>} capabilities
|
|
276
|
+
* @param {?number} timeout
|
|
277
|
+
* @param {boolean} androidReceiverCompatible
|
|
278
|
+
* @param {Object} credentialsData
|
|
279
|
+
*/
|
|
280
|
+
constructor(appId, capabilities, timeout, androidReceiverCompatible,
|
|
281
|
+
credentialsData) {}
|
|
275
282
|
};
|
|
@@ -176,6 +176,7 @@ shaka.extern.DrmInfo;
|
|
|
176
176
|
* @typedef {{
|
|
177
177
|
* id: number,
|
|
178
178
|
* language: string,
|
|
179
|
+
* disabledUntilTime: number,
|
|
179
180
|
* primary: boolean,
|
|
180
181
|
* audio: ?shaka.extern.Stream,
|
|
181
182
|
* video: ?shaka.extern.Stream,
|
|
@@ -198,6 +199,12 @@ shaka.extern.DrmInfo;
|
|
|
198
199
|
* The Variant's language, specified as a language code. <br>
|
|
199
200
|
* See {@link https://tools.ietf.org/html/rfc5646} <br>
|
|
200
201
|
* See {@link http://www.iso.org/iso/home/standards/language_codes.htm}
|
|
202
|
+
* @property {number} disabledUntilTime
|
|
203
|
+
* <i>Defaults to 0.</i> <br>
|
|
204
|
+
* 0 means the variant is enabled. The Player will set this value to
|
|
205
|
+
* "(Date.now() / 1000) + config.streaming.maxDisabledTime" and once this
|
|
206
|
+
* maxDisabledTime has passed Player will set the value to 0 in order to
|
|
207
|
+
* reenable the variant.
|
|
201
208
|
* @property {boolean} primary
|
|
202
209
|
* <i>Defaults to false.</i> <br>
|
|
203
210
|
* True indicates that the player should use this Variant over others if user
|
package/externs/shaka/player.js
CHANGED
|
@@ -82,6 +82,9 @@ shaka.extern.StateChange;
|
|
|
82
82
|
*
|
|
83
83
|
* maxSegmentDuration: number,
|
|
84
84
|
*
|
|
85
|
+
* gapsJumped: number,
|
|
86
|
+
* stallsDetected: number,
|
|
87
|
+
*
|
|
85
88
|
* switchHistory: !Array.<shaka.extern.TrackChoice>,
|
|
86
89
|
* stateHistory: !Array.<shaka.extern.StateChange>
|
|
87
90
|
* }}
|
|
@@ -112,6 +115,11 @@ shaka.extern.StateChange;
|
|
|
112
115
|
* @property {number} estimatedBandwidth
|
|
113
116
|
* The current estimated network bandwidth (in bit/sec).
|
|
114
117
|
*
|
|
118
|
+
* @property {number} gapsJumped
|
|
119
|
+
* The total number of playback gaps jumped by the GapJumpingController.
|
|
120
|
+
* @property {number} stallsDetected
|
|
121
|
+
* The total number of playback stalls detected by the StallDetector.
|
|
122
|
+
*
|
|
115
123
|
* @property {number} completionPercent
|
|
116
124
|
* This is the greatest completion percent that the user has experienced in
|
|
117
125
|
* playback. Also known as the "high water mark". Is NaN when there is no
|
|
@@ -623,7 +631,8 @@ shaka.extern.AdvancedDrmConfiguration;
|
|
|
623
631
|
* undefined),
|
|
624
632
|
* logLicenseExchange: boolean,
|
|
625
633
|
* updateExpirationTime: number,
|
|
626
|
-
* preferredKeySystems: !Array.<string
|
|
634
|
+
* preferredKeySystems: !Array.<string>,
|
|
635
|
+
* keySystemsMapping: !Object.<string, string>
|
|
627
636
|
* }}
|
|
628
637
|
*
|
|
629
638
|
* @property {shaka.extern.RetryParameters} retryParameters
|
|
@@ -664,6 +673,8 @@ shaka.extern.AdvancedDrmConfiguration;
|
|
|
664
673
|
* @property {!Array.<string>} preferredKeySystems
|
|
665
674
|
* <i>Defaults to an empty array. </i> <br>
|
|
666
675
|
* Specifies the priorties of available DRM key systems.
|
|
676
|
+
* @property {Object.<string, string>} keySystemsMapping
|
|
677
|
+
* A map of key system name to key system name.
|
|
667
678
|
*
|
|
668
679
|
* @exportDoc
|
|
669
680
|
*/
|
|
@@ -863,7 +874,8 @@ shaka.extern.ManifestConfiguration;
|
|
|
863
874
|
* preferNativeHls: boolean,
|
|
864
875
|
* updateIntervalSeconds: number,
|
|
865
876
|
* dispatchAllEmsgBoxes: boolean,
|
|
866
|
-
* observeQualityChanges: boolean
|
|
877
|
+
* observeQualityChanges: boolean,
|
|
878
|
+
* maxDisabledTime: number
|
|
867
879
|
* }}
|
|
868
880
|
*
|
|
869
881
|
* @description
|
|
@@ -968,6 +980,10 @@ shaka.extern.ManifestConfiguration;
|
|
|
968
980
|
* @property {boolean} observeQualityChanges
|
|
969
981
|
* If true, monitor media quality changes and emit
|
|
970
982
|
* <code.shaka.Player.MediaQualityChangedEvent</code>.
|
|
983
|
+
* @property {number} maxDisabledTime
|
|
984
|
+
* The maximum time a variant can be disabled when NETWORK HTTP_ERROR
|
|
985
|
+
* is reached, in seconds.
|
|
986
|
+
* If all variants are disabled this way, NETWORK HTTP_ERROR will be thrown.
|
|
971
987
|
* @exportDoc
|
|
972
988
|
*/
|
|
973
989
|
shaka.extern.StreamingConfiguration;
|
|
@@ -1264,11 +1280,14 @@ shaka.extern.Thumbnail;
|
|
|
1264
1280
|
|
|
1265
1281
|
/**
|
|
1266
1282
|
* @typedef {{
|
|
1283
|
+
* id: string,
|
|
1267
1284
|
* title: string,
|
|
1268
1285
|
* startTime: number,
|
|
1269
1286
|
* endTime: number
|
|
1270
1287
|
* }}
|
|
1271
1288
|
*
|
|
1289
|
+
* @property {string} id
|
|
1290
|
+
* The id of the chapter.
|
|
1272
1291
|
* @property {string} title
|
|
1273
1292
|
* The title of the chapter.
|
|
1274
1293
|
* @property {number} startTime
|
package/externs/shaka/text.js
CHANGED
|
@@ -297,6 +297,13 @@ shaka.extern.Cue = class {
|
|
|
297
297
|
*/
|
|
298
298
|
this.fontFamily;
|
|
299
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Text shadow color as a CSS text-shadow value.
|
|
302
|
+
* @type {string}
|
|
303
|
+
* @exportDoc
|
|
304
|
+
*/
|
|
305
|
+
this.textShadow = '';
|
|
306
|
+
|
|
300
307
|
/**
|
|
301
308
|
* Text stroke color as a CSS color, e.g. "#FFFFFF" or "white".
|
|
302
309
|
* @type {string}
|
package/lib/cast/cast_proxy.js
CHANGED
|
@@ -43,8 +43,11 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
|
|
43
43
|
* @param {string} receiverAppId The ID of the cast receiver application.
|
|
44
44
|
* If blank, casting will not be available, but the proxy will still
|
|
45
45
|
* function otherwise.
|
|
46
|
+
* @param {boolean} androidReceiverCompatible Indicates if the app is
|
|
47
|
+
* compatible with an Android Receiver.
|
|
46
48
|
*/
|
|
47
|
-
constructor(video, player, receiverAppId
|
|
49
|
+
constructor(video, player, receiverAppId,
|
|
50
|
+
androidReceiverCompatible = false) {
|
|
48
51
|
super();
|
|
49
52
|
|
|
50
53
|
/** @private {HTMLMediaElement} */
|
|
@@ -71,6 +74,9 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
|
|
71
74
|
/** @private {string} */
|
|
72
75
|
this.receiverAppId_ = receiverAppId;
|
|
73
76
|
|
|
77
|
+
/** @private {boolean} */
|
|
78
|
+
this.androidReceiverCompatible_ = androidReceiverCompatible;
|
|
79
|
+
|
|
74
80
|
/** @private {!Map} */
|
|
75
81
|
this.compiledToExternNames_ = new Map();
|
|
76
82
|
|
|
@@ -81,7 +87,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
|
|
81
87
|
() => this.onFirstCastStateUpdate_(),
|
|
82
88
|
(targetName, event) => this.onRemoteEvent_(targetName, event),
|
|
83
89
|
() => this.onResumeLocal_(),
|
|
84
|
-
() => this.getInitState_()
|
|
90
|
+
() => this.getInitState_(),
|
|
91
|
+
androidReceiverCompatible);
|
|
85
92
|
|
|
86
93
|
|
|
87
94
|
this.init_();
|
|
@@ -225,15 +232,18 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
|
|
225
232
|
|
|
226
233
|
/**
|
|
227
234
|
* @param {string} newAppId
|
|
235
|
+
* @param {boolean=} newCastAndroidReceiver
|
|
228
236
|
* @export
|
|
229
237
|
*/
|
|
230
|
-
async changeReceiverId(newAppId) {
|
|
231
|
-
if (newAppId == this.receiverAppId_
|
|
238
|
+
async changeReceiverId(newAppId, newCastAndroidReceiver = false) {
|
|
239
|
+
if (newAppId == this.receiverAppId_ &&
|
|
240
|
+
newCastAndroidReceiver == this.androidReceiverCompatible_) {
|
|
232
241
|
// Nothing to change
|
|
233
242
|
return;
|
|
234
243
|
}
|
|
235
244
|
|
|
236
245
|
this.receiverAppId_ = newAppId;
|
|
246
|
+
this.androidReceiverCompatible_ = newCastAndroidReceiver;
|
|
237
247
|
|
|
238
248
|
// Destroy the old sender
|
|
239
249
|
this.sender_.forceDisconnect();
|
|
@@ -248,7 +258,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
|
|
248
258
|
() => this.onFirstCastStateUpdate_(),
|
|
249
259
|
(targetName, event) => this.onRemoteEvent_(targetName, event),
|
|
250
260
|
() => this.onResumeLocal_(),
|
|
251
|
-
() => this.getInitState_()
|
|
261
|
+
() => this.getInitState_(),
|
|
262
|
+
newCastAndroidReceiver);
|
|
252
263
|
|
|
253
264
|
this.sender_.init();
|
|
254
265
|
}
|
|
@@ -267,8 +278,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
|
|
267
278
|
(event) => this.videoProxyLocalEvent_(event));
|
|
268
279
|
}
|
|
269
280
|
|
|
270
|
-
for (const key in shaka.
|
|
271
|
-
const name = shaka.
|
|
281
|
+
for (const key in shaka.util.FakeEvent.EventName) {
|
|
282
|
+
const name = shaka.util.FakeEvent.EventName[key];
|
|
272
283
|
this.eventManager_.listen(this.localPlayer_, name,
|
|
273
284
|
(event) => this.playerProxyLocalEvent_(event));
|
|
274
285
|
}
|
|
@@ -526,7 +537,7 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
|
|
|
526
537
|
// Pass any errors through to the app.
|
|
527
538
|
goog.asserts.assert(error instanceof shaka.util.Error,
|
|
528
539
|
'Wrong error type!');
|
|
529
|
-
const eventType = shaka.
|
|
540
|
+
const eventType = shaka.util.FakeEvent.EventName.Error;
|
|
530
541
|
const data = (new Map()).set('detail', error);
|
|
531
542
|
const event = new shaka.util.FakeEvent(eventType, data);
|
|
532
543
|
this.localPlayer_.dispatchEvent(event);
|
|
@@ -281,8 +281,8 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
|
|
|
281
281
|
this.video_, name, (event) => this.proxyEvent_('video', event));
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
for (const key in shaka.
|
|
285
|
-
const name = shaka.
|
|
284
|
+
for (const key in shaka.util.FakeEvent.EventName) {
|
|
285
|
+
const name = shaka.util.FakeEvent.EventName[key];
|
|
286
286
|
this.eventManager_.listen(
|
|
287
287
|
this.player_, name, (event) => this.proxyEvent_('player', event));
|
|
288
288
|
}
|
|
@@ -409,7 +409,7 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
|
|
|
409
409
|
// Pass any errors through to the app.
|
|
410
410
|
goog.asserts.assert(error instanceof shaka.util.Error,
|
|
411
411
|
'Wrong error type!');
|
|
412
|
-
const eventType = shaka.
|
|
412
|
+
const eventType = shaka.util.FakeEvent.EventName.Error;
|
|
413
413
|
const data = (new Map()).set('detail', error);
|
|
414
414
|
const event = new shaka.util.FakeEvent(eventType, data);
|
|
415
415
|
// Only dispatch the event if the player still exists.
|
package/lib/cast/cast_sender.js
CHANGED
|
@@ -32,12 +32,18 @@ shaka.cast.CastSender = class {
|
|
|
32
32
|
* should resume playback. Called before the cached remote state is wiped.
|
|
33
33
|
* @param {function()} onInitStateRequired A callback to get local player's.
|
|
34
34
|
* state. Invoked when casting is initiated from Chrome's cast button.
|
|
35
|
+
* @param {boolean} androidReceiverCompatible Indicates if the app is
|
|
36
|
+
* compatible with an Android Receiver.
|
|
35
37
|
*/
|
|
36
38
|
constructor(receiverAppId, onStatusChanged, onFirstCastStateUpdate,
|
|
37
|
-
onRemoteEvent, onResumeLocal, onInitStateRequired
|
|
39
|
+
onRemoteEvent, onResumeLocal, onInitStateRequired,
|
|
40
|
+
androidReceiverCompatible) {
|
|
38
41
|
/** @private {string} */
|
|
39
42
|
this.receiverAppId_ = receiverAppId;
|
|
40
43
|
|
|
44
|
+
/** @private {boolean} */
|
|
45
|
+
this.androidReceiverCompatible_ = androidReceiverCompatible;
|
|
46
|
+
|
|
41
47
|
/** @private {shaka.util.Timer} */
|
|
42
48
|
this.statusChangeTimer_ = new shaka.util.Timer(onStatusChanged);
|
|
43
49
|
|
|
@@ -211,7 +217,11 @@ shaka.cast.CastSender = class {
|
|
|
211
217
|
// Use static versions of the API callbacks, since the ChromeCast API is
|
|
212
218
|
// static. If we used local versions, we might end up retaining references
|
|
213
219
|
// to destroyed players here.
|
|
214
|
-
const sessionRequest = new chrome.cast.SessionRequest(this.receiverAppId_
|
|
220
|
+
const sessionRequest = new chrome.cast.SessionRequest(this.receiverAppId_,
|
|
221
|
+
/* capabilities= */ [],
|
|
222
|
+
/* timeout= */ null,
|
|
223
|
+
this.androidReceiverCompatible_,
|
|
224
|
+
/* credentialsData= */null);
|
|
215
225
|
const apiConfig = new chrome.cast.ApiConfig(sessionRequest,
|
|
216
226
|
(session) => CastSender.onExistingSessionJoined_(session),
|
|
217
227
|
(availability) => CastSender.onReceiverStatusChanged_(availability),
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -460,6 +460,7 @@ shaka.hls.HlsParser = class {
|
|
|
460
460
|
variants.push({
|
|
461
461
|
id: 0,
|
|
462
462
|
language: 'und',
|
|
463
|
+
disabledUntilTime: 0,
|
|
463
464
|
primary: true,
|
|
464
465
|
audio: type == 'audio' ? streamInfo.stream : null,
|
|
465
466
|
video: type == 'video' ? streamInfo.stream : null,
|
|
@@ -1926,6 +1927,11 @@ shaka.hls.HlsParser = class {
|
|
|
1926
1927
|
const pAbsoluteUri = shaka.hls.Utils.constructAbsoluteUri(
|
|
1927
1928
|
absoluteMediaPlaylistUri, pUri);
|
|
1928
1929
|
|
|
1930
|
+
let partialStatus = shaka.media.SegmentReference.Status.AVAILABLE;
|
|
1931
|
+
if (item.getAttributeValue('GAP') == 'YES') {
|
|
1932
|
+
partialStatus = shaka.media.SegmentReference.Status.MISSING;
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1929
1935
|
const partial = new shaka.media.SegmentReference(
|
|
1930
1936
|
pStartTime,
|
|
1931
1937
|
pEndTime,
|
|
@@ -1935,7 +1941,12 @@ shaka.hls.HlsParser = class {
|
|
|
1935
1941
|
initSegmentReference,
|
|
1936
1942
|
/* timestampOffset= */ 0, // This value is ignored in sequence mode.
|
|
1937
1943
|
/* appendWindowStart= */ 0,
|
|
1938
|
-
/* appendWindowEnd= */ Infinity
|
|
1944
|
+
/* appendWindowEnd= */ Infinity,
|
|
1945
|
+
/* partialReferences= */ [],
|
|
1946
|
+
/* tilesLayout= */ '',
|
|
1947
|
+
/* tileDuration= */ null,
|
|
1948
|
+
/* syncTime= */ null,
|
|
1949
|
+
partialStatus);
|
|
1939
1950
|
partialSegmentRefs.push(partial);
|
|
1940
1951
|
} // for-loop of hlsSegment.partialSegments
|
|
1941
1952
|
}
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -355,7 +355,8 @@ shaka.media.DrmEngine = class {
|
|
|
355
355
|
shaka.media.DrmEngine.fillInDrmInfoDefaults_(
|
|
356
356
|
info,
|
|
357
357
|
shaka.util.MapUtils.asMap(this.config_.servers),
|
|
358
|
-
shaka.util.MapUtils.asMap(this.config_.advanced || {})
|
|
358
|
+
shaka.util.MapUtils.asMap(this.config_.advanced || {}),
|
|
359
|
+
this.config_.keySystemsMapping);
|
|
359
360
|
}
|
|
360
361
|
}
|
|
361
362
|
|
|
@@ -2224,16 +2225,20 @@ shaka.media.DrmEngine = class {
|
|
|
2224
2225
|
* that the parser left blank. Before working with any drmInfo, it should be
|
|
2225
2226
|
* passed through here as it is uncommon for drmInfo to be complete when
|
|
2226
2227
|
* fetched from a manifest because most manifest formats do not have the
|
|
2227
|
-
* required information.
|
|
2228
|
+
* required information. Also applies the key systems mapping.
|
|
2228
2229
|
*
|
|
2229
2230
|
* @param {shaka.extern.DrmInfo} drmInfo
|
|
2230
2231
|
* @param {!Map.<string, string>} servers
|
|
2231
2232
|
* @param {!Map.<string, shaka.extern.AdvancedDrmConfiguration>}
|
|
2232
2233
|
* advancedConfigs
|
|
2234
|
+
* @param {!Object.<string, string>} keySystemsMapping
|
|
2233
2235
|
* @private
|
|
2234
2236
|
*/
|
|
2235
|
-
static fillInDrmInfoDefaults_(drmInfo, servers, advancedConfigs
|
|
2236
|
-
|
|
2237
|
+
static fillInDrmInfoDefaults_(drmInfo, servers, advancedConfigs,
|
|
2238
|
+
keySystemsMapping) {
|
|
2239
|
+
const originalKeySystem = drmInfo.keySystem;
|
|
2240
|
+
|
|
2241
|
+
if (!originalKeySystem) {
|
|
2237
2242
|
// This is a placeholder from the manifest parser for an unrecognized key
|
|
2238
2243
|
// system. Skip this entry, to avoid logging nonsensical errors.
|
|
2239
2244
|
return;
|
|
@@ -2254,14 +2259,14 @@ shaka.media.DrmEngine = class {
|
|
|
2254
2259
|
// The only way to get license servers from the manifest is not to specify
|
|
2255
2260
|
// any in your player config.
|
|
2256
2261
|
|
|
2257
|
-
if (
|
|
2262
|
+
if (originalKeySystem == 'org.w3.clearkey' && drmInfo.licenseServerUri) {
|
|
2258
2263
|
// Preference 1: Clear Key with pre-configured keys will have a data URI
|
|
2259
2264
|
// assigned as its license server. Don't change anything.
|
|
2260
2265
|
return;
|
|
2261
2266
|
} else if (servers.size) {
|
|
2262
2267
|
// Preference 2: If anything is configured at the application level,
|
|
2263
2268
|
// override whatever was in the manifest.
|
|
2264
|
-
const server = servers.get(
|
|
2269
|
+
const server = servers.get(originalKeySystem) || '';
|
|
2265
2270
|
|
|
2266
2271
|
drmInfo.licenseServerUri = server;
|
|
2267
2272
|
} else {
|
|
@@ -2273,7 +2278,7 @@ shaka.media.DrmEngine = class {
|
|
|
2273
2278
|
drmInfo.keyIds = new Set();
|
|
2274
2279
|
}
|
|
2275
2280
|
|
|
2276
|
-
const advancedConfig = advancedConfigs.get(
|
|
2281
|
+
const advancedConfig = advancedConfigs.get(originalKeySystem);
|
|
2277
2282
|
|
|
2278
2283
|
if (advancedConfig) {
|
|
2279
2284
|
if (!drmInfo.distinctiveIdentifierRequired) {
|
|
@@ -2307,6 +2312,10 @@ shaka.media.DrmEngine = class {
|
|
|
2307
2312
|
}
|
|
2308
2313
|
}
|
|
2309
2314
|
|
|
2315
|
+
if (keySystemsMapping[originalKeySystem]) {
|
|
2316
|
+
drmInfo.keySystem = keySystemsMapping[originalKeySystem];
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2310
2319
|
// Chromecast has a variant of PlayReady that uses a different key
|
|
2311
2320
|
// system ID. Since manifest parsers convert the standard PlayReady
|
|
2312
2321
|
// UUID to the standard PlayReady key system ID, here we will switch
|
|
@@ -2315,7 +2324,7 @@ shaka.media.DrmEngine = class {
|
|
|
2315
2324
|
// player config uses the standard PlayReady ID for license server
|
|
2316
2325
|
// configuration.
|
|
2317
2326
|
if (window.cast && window.cast.__platform__) {
|
|
2318
|
-
if (
|
|
2327
|
+
if (originalKeySystem == 'com.microsoft.playready') {
|
|
2319
2328
|
drmInfo.keySystem = 'com.chromecast.playready';
|
|
2320
2329
|
}
|
|
2321
2330
|
}
|
|
@@ -11,6 +11,7 @@ goog.require('shaka.media.PresentationTimeline');
|
|
|
11
11
|
goog.require('shaka.media.StallDetector');
|
|
12
12
|
goog.require('shaka.media.TimeRangesUtils');
|
|
13
13
|
goog.require('shaka.util.EventManager');
|
|
14
|
+
goog.require('shaka.util.FakeEvent');
|
|
14
15
|
goog.require('shaka.util.IReleasable');
|
|
15
16
|
goog.require('shaka.util.Timer');
|
|
16
17
|
|
|
@@ -32,8 +33,13 @@ shaka.media.GapJumpingController = class {
|
|
|
32
33
|
* playable region. The gap jumping controller takes ownership over the
|
|
33
34
|
* stall detector.
|
|
34
35
|
* If no stall detection logic is desired, |null| may be provided.
|
|
36
|
+
* @param {function(!Event)} onEvent
|
|
37
|
+
* Called when an event is raised to be sent to the application.
|
|
35
38
|
*/
|
|
36
|
-
constructor(video, timeline, config, stallDetector) {
|
|
39
|
+
constructor(video, timeline, config, stallDetector, onEvent) {
|
|
40
|
+
/** @private {?function(!Event)} */
|
|
41
|
+
this.onEvent_ = onEvent;
|
|
42
|
+
|
|
37
43
|
/** @private {HTMLMediaElement} */
|
|
38
44
|
this.video_ = video;
|
|
39
45
|
|
|
@@ -52,6 +58,9 @@ shaka.media.GapJumpingController = class {
|
|
|
52
58
|
/** @private {number} */
|
|
53
59
|
this.prevReadyState_ = video.readyState;
|
|
54
60
|
|
|
61
|
+
/** @private {number} */
|
|
62
|
+
this.gapsJumped_ = 0;
|
|
63
|
+
|
|
55
64
|
/**
|
|
56
65
|
* The stall detector tries to keep the playhead moving forward. It is
|
|
57
66
|
* managed by the gap-jumping controller to avoid conflicts. On some
|
|
@@ -98,6 +107,7 @@ shaka.media.GapJumpingController = class {
|
|
|
98
107
|
this.stallDetector_ = null;
|
|
99
108
|
}
|
|
100
109
|
|
|
110
|
+
this.onEvent_ = null;
|
|
101
111
|
this.timeline_ = null;
|
|
102
112
|
this.video_ = null;
|
|
103
113
|
}
|
|
@@ -121,6 +131,15 @@ shaka.media.GapJumpingController = class {
|
|
|
121
131
|
}
|
|
122
132
|
|
|
123
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Returns the total number of playback gaps jumped.
|
|
136
|
+
* @return {number}
|
|
137
|
+
*/
|
|
138
|
+
getGapsJumped() {
|
|
139
|
+
return this.gapsJumped_;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
124
143
|
/**
|
|
125
144
|
* Called on a recurring timer to check for gaps in the media. This is also
|
|
126
145
|
* called in a 'waiting' event.
|
|
@@ -209,6 +228,9 @@ shaka.media.GapJumpingController = class {
|
|
|
209
228
|
}
|
|
210
229
|
|
|
211
230
|
this.video_.currentTime = jumpTo;
|
|
231
|
+
this.gapsJumped_++;
|
|
232
|
+
this.onEvent_(
|
|
233
|
+
new shaka.util.FakeEvent(shaka.util.FakeEvent.EventName.GapJumped));
|
|
212
234
|
}
|
|
213
235
|
};
|
|
214
236
|
|
package/lib/media/playhead.js
CHANGED
|
@@ -41,6 +41,20 @@ shaka.media.Playhead = class {
|
|
|
41
41
|
*/
|
|
42
42
|
setStartTime(startTime) {}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Get the number of playback stalls detected by the StallDetector.
|
|
46
|
+
*
|
|
47
|
+
* @return {number}
|
|
48
|
+
*/
|
|
49
|
+
getStallsDetected() {}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get the number of playback gaps jumped by the GapJumpingController.
|
|
53
|
+
*
|
|
54
|
+
* @return {number}
|
|
55
|
+
*/
|
|
56
|
+
getGapsJumped() {}
|
|
57
|
+
|
|
44
58
|
/**
|
|
45
59
|
* Get the current playhead position. The position will be restricted to valid
|
|
46
60
|
* time ranges.
|
|
@@ -133,6 +147,16 @@ shaka.media.SrcEqualsPlayhead = class {
|
|
|
133
147
|
return time || 0;
|
|
134
148
|
}
|
|
135
149
|
|
|
150
|
+
/** @override */
|
|
151
|
+
getStallsDetected() {
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** @override */
|
|
156
|
+
getGapsJumped() {
|
|
157
|
+
return 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
136
160
|
/** @override */
|
|
137
161
|
notifyOfBufferingChange() {}
|
|
138
162
|
};
|
|
@@ -160,8 +184,10 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
160
184
|
* @param {function()} onSeek
|
|
161
185
|
* Called when the user agent seeks to a time within the presentation
|
|
162
186
|
* timeline.
|
|
187
|
+
* @param {function(!Event)} onEvent
|
|
188
|
+
* Called when an event is raised to be sent to the application.
|
|
163
189
|
*/
|
|
164
|
-
constructor(mediaElement, manifest, config, startTime, onSeek) {
|
|
190
|
+
constructor(mediaElement, manifest, config, startTime, onSeek, onEvent) {
|
|
165
191
|
/**
|
|
166
192
|
* The seek range must be at least this number of seconds long. If it is
|
|
167
193
|
* smaller than this, change it to be this big so we don't repeatedly seek
|
|
@@ -192,12 +218,17 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
192
218
|
/** @private {?number} */
|
|
193
219
|
this.lastCorrectiveSeek_ = null;
|
|
194
220
|
|
|
221
|
+
/** @private {shaka.media.StallDetector} */
|
|
222
|
+
this.stallDetector_ =
|
|
223
|
+
this.createStallDetector_(mediaElement, config, onEvent);
|
|
224
|
+
|
|
195
225
|
/** @private {shaka.media.GapJumpingController} */
|
|
196
226
|
this.gapController_ = new shaka.media.GapJumpingController(
|
|
197
227
|
mediaElement,
|
|
198
228
|
manifest.presentationTimeline,
|
|
199
229
|
config,
|
|
200
|
-
this.
|
|
230
|
+
this.stallDetector_,
|
|
231
|
+
onEvent);
|
|
201
232
|
|
|
202
233
|
/** @private {shaka.media.VideoWrapper} */
|
|
203
234
|
this.videoWrapper_ = new shaka.media.VideoWrapper(
|
|
@@ -261,6 +292,16 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
261
292
|
return time;
|
|
262
293
|
}
|
|
263
294
|
|
|
295
|
+
/** @override */
|
|
296
|
+
getStallsDetected() {
|
|
297
|
+
return this.stallDetector_.getStallsDetected();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** @override */
|
|
301
|
+
getGapsJumped() {
|
|
302
|
+
return this.gapController_.getGapsJumped();
|
|
303
|
+
}
|
|
304
|
+
|
|
264
305
|
/**
|
|
265
306
|
* Gets the playhead's initial position in seconds.
|
|
266
307
|
*
|
|
@@ -475,10 +516,12 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
475
516
|
*
|
|
476
517
|
* @param {!HTMLMediaElement} mediaElement
|
|
477
518
|
* @param {shaka.extern.StreamingConfiguration} config
|
|
519
|
+
* @param {function(!Event)} onEvent
|
|
520
|
+
* Called when an event is raised to be sent to the application.
|
|
478
521
|
* @return {shaka.media.StallDetector}
|
|
479
522
|
* @private
|
|
480
523
|
*/
|
|
481
|
-
createStallDetector_(mediaElement, config) {
|
|
524
|
+
createStallDetector_(mediaElement, config, onEvent) {
|
|
482
525
|
if (!config.stallEnabled) {
|
|
483
526
|
return null;
|
|
484
527
|
}
|
|
@@ -492,7 +535,7 @@ shaka.media.MediaSourcePlayhead = class {
|
|
|
492
535
|
// playhead forward.
|
|
493
536
|
const detector = new shaka.media.StallDetector(
|
|
494
537
|
new shaka.media.StallDetector.MediaElementImplementation(mediaElement),
|
|
495
|
-
threshold);
|
|
538
|
+
threshold, onEvent);
|
|
496
539
|
|
|
497
540
|
detector.onStall((at, duration) => {
|
|
498
541
|
shaka.log.debug(`Stall detected at ${at} for ${duration} seconds.`);
|