shaka-player 4.0.0 → 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/.github/config.yml +1 -0
- package/.github/workflows/release-please.yaml +6 -11
- package/CHANGELOG.md +33 -0
- package/CONTRIBUTORS +1 -0
- package/README.md +4 -4
- package/build/misspellings.txt +1 -0
- package/demo/common/message_ids.js +1 -0
- package/demo/config.js +3 -1
- package/demo/demo.less +0 -1
- package/demo/locales/en.json +1 -0
- package/demo/locales/source.json +4 -0
- package/dist/controls.css +3 -3
- package/dist/controls.css.map +1 -1
- 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/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +3 -3
- package/dist/locales.js +32 -32
- 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 +15 -8
- package/dist/shaka-player.compiled.debug.d.ts +15 -8
- package/dist/shaka-player.compiled.debug.externs.js +11 -2
- package/dist/shaka-player.compiled.debug.js +266 -253
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +11 -2
- package/dist/shaka-player.compiled.js +792 -784
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +16 -9
- package/dist/shaka-player.ui.debug.d.ts +16 -9
- package/dist/shaka-player.ui.debug.externs.js +11 -2
- package/dist/shaka-player.ui.debug.js +299 -282
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +11 -2
- package/dist/shaka-player.ui.js +948 -941
- 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/docs/tutorials/upgrade.md +4 -2
- package/externs/chromecast.js +9 -2
- package/externs/shaka/manifest.js +7 -0
- package/externs/shaka/player.js +32 -2
- package/externs/shaka/text.js +7 -0
- package/karma.conf.js +4 -0
- package/lib/abr/simple_abr_manager.js +2 -1
- 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/dash/dash_parser.js +2 -2
- package/lib/hls/hls_parser.js +46 -15
- package/lib/media/drm_engine.js +36 -8
- package/lib/media/gap_jumping_controller.js +23 -1
- package/lib/media/media_source_engine.js +91 -37
- package/lib/media/playhead.js +47 -4
- package/lib/media/stall_detector.js +19 -2
- package/lib/media/streaming_engine.js +9 -3
- package/lib/offline/indexeddb/storage_mechanism.js +4 -2
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/player.js +339 -175
- package/lib/text/cue.js +6 -0
- package/lib/text/ui_text_displayer.js +1 -0
- package/lib/text/vtt_text_parser.js +11 -7
- package/lib/util/config_utils.js +25 -0
- package/lib/util/error.js +13 -0
- package/lib/util/fake_event.js +39 -0
- package/lib/util/manifest_parser_utils.js +1 -1
- package/lib/util/mime_utils.js +62 -6
- package/lib/util/periods.js +5 -4
- package/lib/util/player_configuration.js +16 -14
- package/lib/util/stats.js +28 -1
- package/lib/util/stream_utils.js +11 -2
- package/package.json +5 -2
- 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/hls/hls_live_unit.js +79 -7
- package/test/hls/manifest_text_parser_unit.js +19 -0
- 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/offline/indexeddb_storage_unit.js +1 -1
- package/test/offline/storage_compatibility_unit.js +1 -1
- package/test/player_external.js +7 -1
- package/test/player_integration.js +9 -10
- package/test/player_src_equals_integration.js +81 -83
- package/test/player_unit.js +240 -2
- package/test/test/boot.js +9 -0
- package/test/test/externs/filters.js +4 -0
- package/test/test/util/manifest_generator.js +2 -0
- package/test/test/util/simple_fakes.js +45 -2
- package/test/test/util/streaming_engine_util.js +1 -0
- package/test/text/vtt_text_parser_unit.js +68 -5
- package/test/ui/ui_unit.js +18 -5
- package/test/util/mime_utils_unit.js +54 -0
- package/test/util/periods_unit.js +79 -0
- package/test/util/stream_utils_unit.js +108 -0
- package/ui/controls.js +12 -8
- package/ui/externs/ui.js +10 -1
- package/ui/less/containers.less +1 -1
- package/ui/less/general.less +1 -1
- package/ui/less/range_elements.less +1 -0
- package/ui/resolution_selection.js +1 -1
- package/ui/statistics_button.js +10 -0
- package/ui/ui.js +12 -1
- package/.github/pull_request_template.md +0 -10
- package/docs/tutorials/gsoc-ideas-list.md +0 -3
|
@@ -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
|
|
|
@@ -42,10 +42,12 @@ application:
|
|
|
42
42
|
`manifest.defaultPresentationDelay` (deprecated in v3.0.0)
|
|
43
43
|
- Configuration of factories should be plain factory functions, not
|
|
44
44
|
constructors; these will not be invoked with `new` (deprecated in v3.1.0)
|
|
45
|
-
- `drm.initDataTransform`
|
|
46
|
-
minimum supported version of iOS is now 13)
|
|
45
|
+
- `drm.initDataTransform` now defaults to a no-op
|
|
47
46
|
- `streaming.smallGapLimit` and `streaming.jumpLargeGaps` have been removed;
|
|
48
47
|
all gaps will now be jumped
|
|
48
|
+
- `manifest.hls.useFullSegmentsForStartTime` has been removed; this setting
|
|
49
|
+
is no longer necessary, as we no longer fetch segments for start times in
|
|
50
|
+
the HLS parser
|
|
49
51
|
|
|
50
52
|
- Player API changes:
|
|
51
53
|
- `shaka.Player.prototype.addTextTrack()` has been replaced by
|
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
|
|
@@ -618,9 +626,13 @@ shaka.extern.AdvancedDrmConfiguration;
|
|
|
618
626
|
* clearKeys: !Object.<string, string>,
|
|
619
627
|
* delayLicenseRequestUntilPlayed: boolean,
|
|
620
628
|
* advanced: Object.<string, shaka.extern.AdvancedDrmConfiguration>,
|
|
629
|
+
* initDataTransform:
|
|
630
|
+
* ((function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array)|
|
|
631
|
+
* undefined),
|
|
621
632
|
* logLicenseExchange: boolean,
|
|
622
633
|
* updateExpirationTime: number,
|
|
623
|
-
* preferredKeySystems: !Array.<string
|
|
634
|
+
* preferredKeySystems: !Array.<string>,
|
|
635
|
+
* keySystemsMapping: !Object.<string, string>
|
|
624
636
|
* }}
|
|
625
637
|
*
|
|
626
638
|
* @property {shaka.extern.RetryParameters} retryParameters
|
|
@@ -641,6 +653,14 @@ shaka.extern.AdvancedDrmConfiguration;
|
|
|
641
653
|
* <i>Optional.</i> <br>
|
|
642
654
|
* A dictionary which maps key system IDs to advanced DRM configuration for
|
|
643
655
|
* those key systems.
|
|
656
|
+
* @property
|
|
657
|
+
* {((function(!Uint8Array, string, ?shaka.extern.DrmInfo):!Uint8Array)|
|
|
658
|
+
* undefined)}
|
|
659
|
+
* initDataTransform
|
|
660
|
+
* <i>Optional.</i><br>
|
|
661
|
+
* If given, this function is called with the init data from the
|
|
662
|
+
* manifest/media and should return the (possibly transformed) init data to
|
|
663
|
+
* pass to the browser.
|
|
644
664
|
* @property {boolean} logLicenseExchange
|
|
645
665
|
* <i>Optional.</i><br>
|
|
646
666
|
* If set to <code>true</code>, prints logs containing the license exchange.
|
|
@@ -653,6 +673,8 @@ shaka.extern.AdvancedDrmConfiguration;
|
|
|
653
673
|
* @property {!Array.<string>} preferredKeySystems
|
|
654
674
|
* <i>Defaults to an empty array. </i> <br>
|
|
655
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.
|
|
656
678
|
*
|
|
657
679
|
* @exportDoc
|
|
658
680
|
*/
|
|
@@ -852,7 +874,8 @@ shaka.extern.ManifestConfiguration;
|
|
|
852
874
|
* preferNativeHls: boolean,
|
|
853
875
|
* updateIntervalSeconds: number,
|
|
854
876
|
* dispatchAllEmsgBoxes: boolean,
|
|
855
|
-
* observeQualityChanges: boolean
|
|
877
|
+
* observeQualityChanges: boolean,
|
|
878
|
+
* maxDisabledTime: number
|
|
856
879
|
* }}
|
|
857
880
|
*
|
|
858
881
|
* @description
|
|
@@ -957,6 +980,10 @@ shaka.extern.ManifestConfiguration;
|
|
|
957
980
|
* @property {boolean} observeQualityChanges
|
|
958
981
|
* If true, monitor media quality changes and emit
|
|
959
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.
|
|
960
987
|
* @exportDoc
|
|
961
988
|
*/
|
|
962
989
|
shaka.extern.StreamingConfiguration;
|
|
@@ -1253,11 +1280,14 @@ shaka.extern.Thumbnail;
|
|
|
1253
1280
|
|
|
1254
1281
|
/**
|
|
1255
1282
|
* @typedef {{
|
|
1283
|
+
* id: string,
|
|
1256
1284
|
* title: string,
|
|
1257
1285
|
* startTime: number,
|
|
1258
1286
|
* endTime: number
|
|
1259
1287
|
* }}
|
|
1260
1288
|
*
|
|
1289
|
+
* @property {string} id
|
|
1290
|
+
* The id of the chapter.
|
|
1261
1291
|
* @property {string} title
|
|
1262
1292
|
* The title of the chapter.
|
|
1263
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/karma.conf.js
CHANGED
|
@@ -363,6 +363,8 @@ module.exports = (config) => {
|
|
|
363
363
|
// If testing custom assets, we don't serve other unit or integration tests.
|
|
364
364
|
// External asset tests are the basis for custom asset testing, so this file
|
|
365
365
|
// is automatically included.
|
|
366
|
+
clientArgs.testFiles.push('demo/common/asset.js');
|
|
367
|
+
clientArgs.testFiles.push('demo/common/assets.js');
|
|
366
368
|
clientArgs.testFiles.push('test/player_external.js');
|
|
367
369
|
} else {
|
|
368
370
|
// In a normal test run, we serve unit tests.
|
|
@@ -374,6 +376,8 @@ module.exports = (config) => {
|
|
|
374
376
|
}
|
|
375
377
|
if (settings.external) {
|
|
376
378
|
// If --external is present, we serve external asset tests.
|
|
379
|
+
clientArgs.testFiles.push('demo/common/asset.js');
|
|
380
|
+
clientArgs.testFiles.push('demo/common/assets.js');
|
|
377
381
|
clientArgs.testFiles.push('test/**/*_external.js');
|
|
378
382
|
}
|
|
379
383
|
}
|
|
@@ -219,8 +219,9 @@ shaka.abr.SimpleAbrManager = class {
|
|
|
219
219
|
* @export
|
|
220
220
|
*/
|
|
221
221
|
getBandwidthEstimate() {
|
|
222
|
+
const defaultBandwidthEstimate = this.getDefaultBandwidth_();
|
|
222
223
|
return this.bandwidthEstimator_.getBandwidthEstimate(
|
|
223
|
-
|
|
224
|
+
defaultBandwidthEstimate);
|
|
224
225
|
}
|
|
225
226
|
|
|
226
227
|
|
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/dash/dash_parser.js
CHANGED
|
@@ -765,8 +765,8 @@ shaka.dash.DashParser = class {
|
|
|
765
765
|
// currently support that. Just choose one.
|
|
766
766
|
// TODO: https://github.com/shaka-project/shaka-player/issues/1528
|
|
767
767
|
stream.trickModeVideo = trickModeSet.streams.find((trickStream) =>
|
|
768
|
-
shaka.util.MimeUtils.
|
|
769
|
-
shaka.util.MimeUtils.
|
|
768
|
+
shaka.util.MimeUtils.getNormalizedCodec(stream.codecs) ==
|
|
769
|
+
shaka.util.MimeUtils.getNormalizedCodec(trickStream.codecs));
|
|
770
770
|
}
|
|
771
771
|
}
|
|
772
772
|
}
|
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,
|
|
@@ -1825,7 +1826,7 @@ shaka.hls.HlsParser = class {
|
|
|
1825
1826
|
* @param {!Map.<string, string>} variables
|
|
1826
1827
|
* @param {string} absoluteMediaPlaylistUri
|
|
1827
1828
|
* @param {string} type
|
|
1828
|
-
* @return {
|
|
1829
|
+
* @return {shaka.media.SegmentReference}
|
|
1829
1830
|
* @private
|
|
1830
1831
|
*/
|
|
1831
1832
|
createSegmentReference_(
|
|
@@ -1870,17 +1871,42 @@ shaka.hls.HlsParser = class {
|
|
|
1870
1871
|
status = shaka.media.SegmentReference.Status.MISSING;
|
|
1871
1872
|
}
|
|
1872
1873
|
|
|
1874
|
+
if (!extinfTag) {
|
|
1875
|
+
if (hlsSegment.partialSegments.length == 0) {
|
|
1876
|
+
// EXTINF tag must be available if the segment has no partial segments.
|
|
1877
|
+
throw new shaka.util.Error(
|
|
1878
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
1879
|
+
shaka.util.Error.Category.MANIFEST,
|
|
1880
|
+
shaka.util.Error.Code.HLS_REQUIRED_TAG_MISSING, 'EXTINF');
|
|
1881
|
+
} else if (!this.lowLatencyMode_) {
|
|
1882
|
+
// Without EXTINF and without low-latency mode, partial segments get
|
|
1883
|
+
// ignored.
|
|
1884
|
+
return null;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1873
1888
|
// Create SegmentReferences for the partial segments.
|
|
1874
1889
|
const partialSegmentRefs = [];
|
|
1875
|
-
if (this.lowLatencyMode_
|
|
1890
|
+
if (this.lowLatencyMode_) {
|
|
1876
1891
|
for (let i = 0; i < hlsSegment.partialSegments.length; i++) {
|
|
1877
1892
|
const item = hlsSegment.partialSegments[i];
|
|
1878
1893
|
const pPreviousReference = i == 0 ?
|
|
1879
1894
|
previousReference : partialSegmentRefs[partialSegmentRefs.length - 1];
|
|
1880
1895
|
const pStartTime = (i == 0) ? startTime : pPreviousReference.endTime;
|
|
1881
|
-
|
|
1882
|
-
//
|
|
1883
|
-
//
|
|
1896
|
+
|
|
1897
|
+
// If DURATION is missing from this partial segment, use the target
|
|
1898
|
+
// partial duration from the top of the playlist, which is a required
|
|
1899
|
+
// attribute for content with partial segments.
|
|
1900
|
+
const pDuration = Number(item.getAttributeValue('DURATION')) ||
|
|
1901
|
+
this.partialTargetDuration_;
|
|
1902
|
+
|
|
1903
|
+
// If for some reason we have neither an explicit duration, nor a target
|
|
1904
|
+
// partial duration, we should SKIP this partial segment to avoid
|
|
1905
|
+
// duplicating content in the presentation timeline.
|
|
1906
|
+
if (!pDuration) {
|
|
1907
|
+
continue;
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1884
1910
|
const pEndTime = pStartTime + pDuration;
|
|
1885
1911
|
|
|
1886
1912
|
let pStartByte = 0;
|
|
@@ -1901,6 +1927,11 @@ shaka.hls.HlsParser = class {
|
|
|
1901
1927
|
const pAbsoluteUri = shaka.hls.Utils.constructAbsoluteUri(
|
|
1902
1928
|
absoluteMediaPlaylistUri, pUri);
|
|
1903
1929
|
|
|
1930
|
+
let partialStatus = shaka.media.SegmentReference.Status.AVAILABLE;
|
|
1931
|
+
if (item.getAttributeValue('GAP') == 'YES') {
|
|
1932
|
+
partialStatus = shaka.media.SegmentReference.Status.MISSING;
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1904
1935
|
const partial = new shaka.media.SegmentReference(
|
|
1905
1936
|
pStartTime,
|
|
1906
1937
|
pEndTime,
|
|
@@ -1910,17 +1941,14 @@ shaka.hls.HlsParser = class {
|
|
|
1910
1941
|
initSegmentReference,
|
|
1911
1942
|
/* timestampOffset= */ 0, // This value is ignored in sequence mode.
|
|
1912
1943
|
/* appendWindowStart= */ 0,
|
|
1913
|
-
/* appendWindowEnd= */ Infinity
|
|
1944
|
+
/* appendWindowEnd= */ Infinity,
|
|
1945
|
+
/* partialReferences= */ [],
|
|
1946
|
+
/* tilesLayout= */ '',
|
|
1947
|
+
/* tileDuration= */ null,
|
|
1948
|
+
/* syncTime= */ null,
|
|
1949
|
+
partialStatus);
|
|
1914
1950
|
partialSegmentRefs.push(partial);
|
|
1915
1951
|
} // for-loop of hlsSegment.partialSegments
|
|
1916
|
-
} else {
|
|
1917
|
-
// EXTINF tag must be available if the segment has no partial segments.
|
|
1918
|
-
if (!extinfTag) {
|
|
1919
|
-
throw new shaka.util.Error(
|
|
1920
|
-
shaka.util.Error.Severity.CRITICAL,
|
|
1921
|
-
shaka.util.Error.Category.MANIFEST,
|
|
1922
|
-
shaka.util.Error.Code.HLS_REQUIRED_TAG_MISSING, 'EXTINF');
|
|
1923
|
-
}
|
|
1924
1952
|
}
|
|
1925
1953
|
|
|
1926
1954
|
// If the segment has EXTINF tag, set the segment's end time, start byte
|
|
@@ -1939,6 +1967,7 @@ shaka.hls.HlsParser = class {
|
|
|
1939
1967
|
} else {
|
|
1940
1968
|
endTime = partialSegmentRefs[partialSegmentRefs.length - 1].endTime;
|
|
1941
1969
|
}
|
|
1970
|
+
|
|
1942
1971
|
// If the segment has EXT-X-BYTERANGE tag, set the start byte and end byte
|
|
1943
1972
|
// base on the byterange information. If segment has no EXT-X-BYTERANGE tag
|
|
1944
1973
|
// and has partial segments, set the start byte and end byte base on the
|
|
@@ -2107,7 +2136,9 @@ shaka.hls.HlsParser = class {
|
|
|
2107
2136
|
playlist.absoluteUri,
|
|
2108
2137
|
type);
|
|
2109
2138
|
|
|
2110
|
-
|
|
2139
|
+
if (reference) {
|
|
2140
|
+
references.push(reference);
|
|
2141
|
+
}
|
|
2111
2142
|
}
|
|
2112
2143
|
|
|
2113
2144
|
// If some segments have sync times, but not all, extrapolate the sync
|
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
|
|
|
@@ -1253,6 +1254,22 @@ shaka.media.DrmEngine = class {
|
|
|
1253
1254
|
};
|
|
1254
1255
|
this.activeSessions_.set(session, metadata);
|
|
1255
1256
|
|
|
1257
|
+
try {
|
|
1258
|
+
initData = this.config_.initDataTransform(
|
|
1259
|
+
initData, initDataType, this.currentDrmInfo_);
|
|
1260
|
+
} catch (error) {
|
|
1261
|
+
let shakaError = error;
|
|
1262
|
+
if (!(error instanceof shaka.util.Error)) {
|
|
1263
|
+
shakaError = new shaka.util.Error(
|
|
1264
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
1265
|
+
shaka.util.Error.Category.DRM,
|
|
1266
|
+
shaka.util.Error.Code.INIT_DATA_TRANSFORM_ERROR,
|
|
1267
|
+
error);
|
|
1268
|
+
}
|
|
1269
|
+
this.onError_(shakaError);
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1256
1273
|
if (this.config_.logLicenseExchange) {
|
|
1257
1274
|
const str = shaka.util.Uint8ArrayUtils.toBase64(initData);
|
|
1258
1275
|
shaka.log.info('EME init data: type=', initDataType, 'data=', str);
|
|
@@ -1408,6 +1425,9 @@ shaka.media.DrmEngine = class {
|
|
|
1408
1425
|
}
|
|
1409
1426
|
return;
|
|
1410
1427
|
}
|
|
1428
|
+
if (this.destroyer_.destroyed()) {
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1411
1431
|
|
|
1412
1432
|
const updateEvent = new shaka.util.FakeEvent('drmsessionupdate');
|
|
1413
1433
|
this.playerInterface_.onEvent(updateEvent);
|
|
@@ -2205,16 +2225,20 @@ shaka.media.DrmEngine = class {
|
|
|
2205
2225
|
* that the parser left blank. Before working with any drmInfo, it should be
|
|
2206
2226
|
* passed through here as it is uncommon for drmInfo to be complete when
|
|
2207
2227
|
* fetched from a manifest because most manifest formats do not have the
|
|
2208
|
-
* required information.
|
|
2228
|
+
* required information. Also applies the key systems mapping.
|
|
2209
2229
|
*
|
|
2210
2230
|
* @param {shaka.extern.DrmInfo} drmInfo
|
|
2211
2231
|
* @param {!Map.<string, string>} servers
|
|
2212
2232
|
* @param {!Map.<string, shaka.extern.AdvancedDrmConfiguration>}
|
|
2213
2233
|
* advancedConfigs
|
|
2234
|
+
* @param {!Object.<string, string>} keySystemsMapping
|
|
2214
2235
|
* @private
|
|
2215
2236
|
*/
|
|
2216
|
-
static fillInDrmInfoDefaults_(drmInfo, servers, advancedConfigs
|
|
2217
|
-
|
|
2237
|
+
static fillInDrmInfoDefaults_(drmInfo, servers, advancedConfigs,
|
|
2238
|
+
keySystemsMapping) {
|
|
2239
|
+
const originalKeySystem = drmInfo.keySystem;
|
|
2240
|
+
|
|
2241
|
+
if (!originalKeySystem) {
|
|
2218
2242
|
// This is a placeholder from the manifest parser for an unrecognized key
|
|
2219
2243
|
// system. Skip this entry, to avoid logging nonsensical errors.
|
|
2220
2244
|
return;
|
|
@@ -2235,14 +2259,14 @@ shaka.media.DrmEngine = class {
|
|
|
2235
2259
|
// The only way to get license servers from the manifest is not to specify
|
|
2236
2260
|
// any in your player config.
|
|
2237
2261
|
|
|
2238
|
-
if (
|
|
2262
|
+
if (originalKeySystem == 'org.w3.clearkey' && drmInfo.licenseServerUri) {
|
|
2239
2263
|
// Preference 1: Clear Key with pre-configured keys will have a data URI
|
|
2240
2264
|
// assigned as its license server. Don't change anything.
|
|
2241
2265
|
return;
|
|
2242
2266
|
} else if (servers.size) {
|
|
2243
2267
|
// Preference 2: If anything is configured at the application level,
|
|
2244
2268
|
// override whatever was in the manifest.
|
|
2245
|
-
const server = servers.get(
|
|
2269
|
+
const server = servers.get(originalKeySystem) || '';
|
|
2246
2270
|
|
|
2247
2271
|
drmInfo.licenseServerUri = server;
|
|
2248
2272
|
} else {
|
|
@@ -2254,7 +2278,7 @@ shaka.media.DrmEngine = class {
|
|
|
2254
2278
|
drmInfo.keyIds = new Set();
|
|
2255
2279
|
}
|
|
2256
2280
|
|
|
2257
|
-
const advancedConfig = advancedConfigs.get(
|
|
2281
|
+
const advancedConfig = advancedConfigs.get(originalKeySystem);
|
|
2258
2282
|
|
|
2259
2283
|
if (advancedConfig) {
|
|
2260
2284
|
if (!drmInfo.distinctiveIdentifierRequired) {
|
|
@@ -2288,6 +2312,10 @@ shaka.media.DrmEngine = class {
|
|
|
2288
2312
|
}
|
|
2289
2313
|
}
|
|
2290
2314
|
|
|
2315
|
+
if (keySystemsMapping[originalKeySystem]) {
|
|
2316
|
+
drmInfo.keySystem = keySystemsMapping[originalKeySystem];
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2291
2319
|
// Chromecast has a variant of PlayReady that uses a different key
|
|
2292
2320
|
// system ID. Since manifest parsers convert the standard PlayReady
|
|
2293
2321
|
// UUID to the standard PlayReady key system ID, here we will switch
|
|
@@ -2296,7 +2324,7 @@ shaka.media.DrmEngine = class {
|
|
|
2296
2324
|
// player config uses the standard PlayReady ID for license server
|
|
2297
2325
|
// configuration.
|
|
2298
2326
|
if (window.cast && window.cast.__platform__) {
|
|
2299
|
-
if (
|
|
2327
|
+
if (originalKeySystem == 'com.microsoft.playready') {
|
|
2300
2328
|
drmInfo.keySystem = 'com.chromecast.playready';
|
|
2301
2329
|
}
|
|
2302
2330
|
}
|