shaka-player 4.1.0 → 4.1.3
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/workflows/build-and-test.yaml +67 -15
- package/.github/workflows/compute-incremental-coverage.py +251 -0
- package/.github/workflows/report-incremental-coverage.yaml +39 -0
- package/.github/workflows/selenium-lab-tests.yaml +44 -1
- package/AUTHORS +2 -0
- package/CHANGELOG.md +42 -0
- package/CONTRIBUTORS +4 -0
- package/README.md +20 -6
- package/demo/common/assets.js +1 -1
- package/demo/index.html +1 -3
- package/demo/main.js +3 -3
- package/dist/controls.css +1 -1
- package/dist/demo.compiled.debug.js +3 -3
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +3 -3
- package/dist/demo.compiled.map +2 -2
- package/dist/deps.js +3 -3
- package/dist/locales.js +29 -29
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.debug.map +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/receiver.compiled.map +1 -1
- package/dist/shaka-player.compiled.d.ts +12 -1
- package/dist/shaka-player.compiled.debug.d.ts +12 -1
- package/dist/shaka-player.compiled.debug.externs.js +52 -40
- package/dist/shaka-player.compiled.debug.js +270 -257
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +52 -40
- package/dist/shaka-player.compiled.js +754 -746
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +12 -1
- package/dist/shaka-player.ui.debug.d.ts +12 -1
- package/dist/shaka-player.ui.debug.externs.js +52 -40
- package/dist/shaka-player.ui.debug.js +296 -283
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +52 -40
- package/dist/shaka-player.ui.js +874 -867
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ui.md +6 -6
- package/docs/tutorials/upgrade.md +1 -0
- package/docs/tutorials/welcome.md +6 -3
- package/externs/shaka/player.js +4 -2
- package/karma.conf.js +3 -11
- package/lib/ads/client_side_ad_manager.js +1 -1
- package/lib/dash/dash_parser.js +5 -0
- package/lib/hls/hls_parser.js +183 -70
- package/lib/media/drm_engine.js +1 -1
- package/lib/media/media_source_engine.js +57 -24
- package/lib/media/presentation_timeline.js +24 -1
- package/lib/media/segment_index.js +26 -1
- package/lib/media/segment_reference.js +10 -3
- package/lib/media/streaming_engine.js +4 -4
- package/lib/media/time_ranges_utils.js +46 -0
- package/lib/player.js +1 -6
- package/lib/polyfill/media_capabilities.js +9 -0
- package/lib/polyfill/videoplaybackquality.js +3 -1
- package/lib/text/cue.js +0 -5
- package/lib/text/simple_text_displayer.js +21 -9
- package/lib/text/ui_text_displayer.js +83 -19
- package/lib/util/buffer_utils.js +3 -0
- package/lib/util/object_utils.js +1 -0
- package/lib/util/platform.js +19 -1
- package/lib/util/stream_utils.js +24 -3
- package/lib/util/string_utils.js +102 -13
- package/maintained-branches.md +15 -0
- package/package.json +3 -5
- package/test/hls/hls_parser_unit.js +102 -8
- package/test/media/drm_engine_integration.js +24 -8
- package/test/media/media_source_engine_integration.js +24 -10
- package/test/media/media_source_engine_unit.js +53 -38
- package/test/media/streaming_engine_unit.js +28 -27
- package/test/test/util/fake_media_source_engine.js +10 -9
- package/test/text/simple_text_displayer_unit.js +27 -0
- package/test/text/ui_text_displayer_unit.js +65 -0
- package/test/util/string_utils_unit.js +46 -8
- package/ui/text_selection.js +2 -2
- package/app-engine/README.md +0 -9
- package/app-engine/demo-version-index/README.md +0 -14
- package/app-engine/demo-version-index/app.yaml +0 -28
- package/app-engine/demo-version-index/generate.py +0 -164
- package/app-engine/demo-version-index/main.py +0 -11
- package/app-engine/demo-version-index/requirements.txt +0 -5
- package/app-engine/demo-version-index/templates/index.html +0 -114
- package/app-engine/shaka-player-demo/README.md +0 -18
- package/app-engine/shaka-player-demo/app.yaml +0 -61
- package/app-engine/shaka-player-demo/main.py +0 -95
- package/app-engine/shaka-player-demo/posters/10yo.png +0 -0
- package/app-engine/shaka-player-demo/posters/10yo.xcf +0 -0
- package/app-engine/shaka-player-demo/posters/1yo.jpg +0 -0
- package/app-engine/shaka-player-demo/posters/2yo.png +0 -0
- package/app-engine/shaka-player-demo/posters/2yo.xcf +0 -0
- package/app-engine/shaka-player-demo/posters/3yo.png +0 -0
- package/app-engine/shaka-player-demo/posters/3yo.xcf +0 -0
- package/app-engine/shaka-player-demo/posters/4yo.png +0 -0
- package/app-engine/shaka-player-demo/posters/4yo.xcf +0 -0
- package/app-engine/shaka-player-demo/posters/5yo.png +0 -0
- package/app-engine/shaka-player-demo/posters/5yo.xcf +0 -0
- package/app-engine/shaka-player-demo/posters/6yo.png +0 -0
- package/app-engine/shaka-player-demo/posters/6yo.xcf +0 -0
- package/app-engine/shaka-player-demo/posters/7yo.png +0 -0
- package/app-engine/shaka-player-demo/posters/7yo.xcf +0 -0
- package/app-engine/shaka-player-demo/posters/8yo.png +0 -0
- package/app-engine/shaka-player-demo/posters/8yo.xcf +0 -0
- package/app-engine/shaka-player-demo/posters/9yo.png +0 -0
- package/app-engine/shaka-player-demo/posters/9yo.xcf +0 -0
- package/app-engine/shaka-player-demo/posters/audioOnly.gif +0 -0
- package/app-engine/shaka-player-demo/posters/audioRickRoll.gif +0 -0
- package/app-engine/shaka-player-demo/posters/colorbars.gif +0 -0
- package/app-engine/shaka-player-demo/posters/fireworks.jpg +0 -0
- package/app-engine/shaka-player-demo/posters/party-hat.png +0 -0
- package/app-engine/shaka-player-demo/posters/sources.txt +0 -46
- package/app-engine/shaka-player-demo/posters/standard.png +0 -0
- package/app-engine/shaka-player-demo/posters/trek.jpg +0 -0
- package/app-engine/shaka-player-demo/posters/valentines.png +0 -0
- package/app-engine/shaka-player-demo/posters/zombie.jpg +0 -0
- package/app-engine/shaka-player-demo/posters.py +0 -110
- package/app-engine/shaka-player-demo/requirements.txt +0 -5
- package/app-engine/shaka-player-demo/time.txt +0 -1
- package/build/README.md +0 -127
- package/build/__init__.py +0 -0
- package/build/all.py +0 -150
- package/build/apps.py +0 -186
- package/build/build.py +0 -392
- package/build/check.py +0 -354
- package/build/checkversion.py +0 -84
- package/build/compiler.py +0 -513
- package/build/conformance.textproto +0 -335
- package/build/docker/Dockerfile +0 -20
- package/build/docker/docker-compose.yml +0 -10
- package/build/docs.py +0 -54
- package/build/eslint-plugin-shaka-rules/arg-comment-spacing.js +0 -72
- package/build/eslint-plugin-shaka-rules/index.js +0 -24
- package/build/eslint-plugin-shaka-rules/package.json +0 -7
- package/build/eslint-plugin-shaka-rules/private.js +0 -109
- package/build/gendeps.py +0 -71
- package/build/generateExterns.js +0 -867
- package/build/generateLocalizations.py +0 -262
- package/build/generateTsDefs.py +0 -137
- package/build/install-linux-prereqs.sh +0 -136
- package/build/license-header +0 -5
- package/build/misspellings.txt +0 -39
- package/build/pixelsChanged.js +0 -35
- package/build/shaka-lab.yaml +0 -161
- package/build/shakaBuildHelpers.py +0 -356
- package/build/stats.py +0 -816
- package/build/subprocessWindowsPatch.py +0 -103
- package/build/test.py +0 -541
- package/build/types/ads +0 -8
- package/build/types/cast +0 -6
- package/build/types/cea +0 -13
- package/build/types/complete +0 -9
- package/build/types/core +0 -108
- package/build/types/dash +0 -8
- package/build/types/hls +0 -9
- package/build/types/manifests +0 -5
- package/build/types/networking +0 -6
- package/build/types/offline +0 -22
- package/build/types/polyfill +0 -18
- package/build/types/text +0 -11
- package/build/types/ui +0 -39
- package/build/updateScreenshots.py +0 -85
- package/build/wrapper.template.js +0 -52
package/docs/tutorials/ui.md
CHANGED
|
@@ -30,7 +30,7 @@ Set up controls with HTML data attributes:
|
|
|
30
30
|
The data-shaka-player-cast-receiver-id tag allows you to provide a Cast Application ID that
|
|
31
31
|
the cast button will cast to; the value provided here is the sample cast receiver. -->
|
|
32
32
|
<div data-shaka-player-container style="max-width:40em"
|
|
33
|
-
data-shaka-player-cast-receiver-id="
|
|
33
|
+
data-shaka-player-cast-receiver-id="06EDA6EE">
|
|
34
34
|
<!-- The data-shaka-player tag will make the UI library use this video element.
|
|
35
35
|
If no video is provided, the UI will automatically make one inside the container div. -->
|
|
36
36
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"></video>
|
|
@@ -111,7 +111,7 @@ set up a listener for the 'caststatuschanged' events.
|
|
|
111
111
|
<!-- Add a data-shaka-player-cast-receiver-id tag to provide a Cast Application ID that
|
|
112
112
|
the cast button will cast to; the value provided here is the sample cast receiver. -->
|
|
113
113
|
<div data-shaka-player-container style="max-width:40em"
|
|
114
|
-
data-shaka-player-cast-receiver-id="
|
|
114
|
+
data-shaka-player-cast-receiver-id="06EDA6EE">
|
|
115
115
|
</div>
|
|
116
116
|
```
|
|
117
117
|
|
|
@@ -154,7 +154,7 @@ or a `<source>` tag inside it to enable auto loading of the specified content.
|
|
|
154
154
|
|
|
155
155
|
```html
|
|
156
156
|
<div data-shaka-player-container style="max-width:40em"
|
|
157
|
-
data-shaka-player-cast-receiver-id="
|
|
157
|
+
data-shaka-player-cast-receiver-id="06EDA6EE">
|
|
158
158
|
<!-- The manifest url in the src attribute will be automatically loaded -->
|
|
159
159
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"
|
|
160
160
|
src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"></video>
|
|
@@ -165,7 +165,7 @@ or
|
|
|
165
165
|
|
|
166
166
|
```html
|
|
167
167
|
<div data-shaka-player-container style="max-width:40em"
|
|
168
|
-
data-shaka-player-cast-receiver-id="
|
|
168
|
+
data-shaka-player-cast-receiver-id="06EDA6EE">
|
|
169
169
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%">
|
|
170
170
|
<!-- The manifest url in the src attribute will be auto loaded -->
|
|
171
171
|
<source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
|
|
@@ -178,7 +178,7 @@ call to the first one fails.
|
|
|
178
178
|
|
|
179
179
|
```html
|
|
180
180
|
<div data-shaka-player-container style="max-width:40em"
|
|
181
|
-
data-shaka-player-cast-receiver-id="
|
|
181
|
+
data-shaka-player-cast-receiver-id="06EDA6EE">
|
|
182
182
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%">
|
|
183
183
|
<!-- Try this first -->
|
|
184
184
|
<source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
|
|
@@ -222,7 +222,7 @@ const video = controls.getVideo();
|
|
|
222
222
|
// Receiver Compatability.
|
|
223
223
|
ui.configure({
|
|
224
224
|
// Set the castReceiverAppId
|
|
225
|
-
'castReceiverAppId': '
|
|
225
|
+
'castReceiverAppId': '06EDA6EE',
|
|
226
226
|
// Enable casting to native Android Apps (e.g. Android TV Apps)
|
|
227
227
|
'castAndroidReceiverCompatible': true,
|
|
228
228
|
});
|
|
@@ -25,6 +25,7 @@ application:
|
|
|
25
25
|
- TextDecoder/TextEncoder platform support or polyfill required (affects
|
|
26
26
|
Xbox One, but not evergreen browsers); we suggest the polyfill
|
|
27
27
|
[https://github.com/anonyco/FastestSmallestTextEncoderDecoder](fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js)
|
|
28
|
+
Fallback included by default in v4.2
|
|
28
29
|
|
|
29
30
|
- Support removed:
|
|
30
31
|
- IE11 support removed
|
|
@@ -98,11 +98,14 @@ You can find a full list of available browsers with `--browsers help`, and you
|
|
|
98
98
|
can find a complete list of testing options with `--help`.
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
####
|
|
101
|
+
#### Announcements
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
To subscribe to new releases on GitHub, you can follow
|
|
104
|
+
[instructions from this blog](https://www.jessesquires.com/blog/2020/07/30/github-tip-watching-releases/).
|
|
105
|
+
|
|
106
|
+
To receive infrequent announcements and surveys from us, you can join our
|
|
104
107
|
[mailing list](https://groups.google.com/forum/#!forum/shaka-player-users).
|
|
105
|
-
The list is very low volume.
|
|
108
|
+
The list is very low volume, and can only be written to by us.
|
|
106
109
|
|
|
107
110
|
|
|
108
111
|
#### Continue the Tutorials
|
package/externs/shaka/player.js
CHANGED
|
@@ -777,8 +777,10 @@ shaka.extern.DashManifestConfiguration;
|
|
|
777
777
|
* <i>Defaults to <code>'avc1.42E01E'</code>.</i>
|
|
778
778
|
* @property {boolean} ignoreManifestProgramDateTime
|
|
779
779
|
* If <code>true</code>, the HLS parser will ignore the
|
|
780
|
-
* <code>EXT-X-PROGRAM-DATE-TIME</code> tags in the manifest
|
|
781
|
-
*
|
|
780
|
+
* <code>EXT-X-PROGRAM-DATE-TIME</code> tags in the manifest and use media
|
|
781
|
+
* sequence numbers instead.
|
|
782
|
+
* Meant for streams where <code>EXT-X-PROGRAM-DATE-TIME</code> is incorrect
|
|
783
|
+
* or malformed.
|
|
782
784
|
* <i>Defaults to <code>false</code>.</i>
|
|
783
785
|
* @property {string} mediaPlaylistFullMimeType
|
|
784
786
|
* A string containing a full mime type, including both the basic mime type
|
package/karma.conf.js
CHANGED
|
@@ -160,10 +160,6 @@ module.exports = (config) => {
|
|
|
160
160
|
'node_modules/es6-promise-polyfill/promise.js',
|
|
161
161
|
// Babel polyfill, required for async/await
|
|
162
162
|
'node_modules/@babel/polyfill/dist/polyfill.js',
|
|
163
|
-
// TextDecoder polyfill, required for TextDecoder/TextEncoder on IE and
|
|
164
|
-
// legacy Edge
|
|
165
|
-
// eslint-disable-next-line max-len
|
|
166
|
-
'node_modules/fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js',
|
|
167
163
|
|
|
168
164
|
// muxjs module next
|
|
169
165
|
'node_modules/mux.js/dist/mux.min.js',
|
|
@@ -306,13 +302,6 @@ module.exports = (config) => {
|
|
|
306
302
|
// Force failure when running empty test-suites.
|
|
307
303
|
failOnEmptyTestSuite: true,
|
|
308
304
|
|
|
309
|
-
coverageReporter: {
|
|
310
|
-
includeAllSources: true,
|
|
311
|
-
reporters: [
|
|
312
|
-
{type: 'text'},
|
|
313
|
-
],
|
|
314
|
-
},
|
|
315
|
-
|
|
316
305
|
specReporter: {
|
|
317
306
|
suppressSkipped: true,
|
|
318
307
|
showBrowser: true,
|
|
@@ -405,9 +394,12 @@ module.exports = (config) => {
|
|
|
405
394
|
|
|
406
395
|
config.set({
|
|
407
396
|
coverageReporter: {
|
|
397
|
+
includeAllSources: true,
|
|
408
398
|
reporters: [
|
|
409
399
|
{type: 'html', dir: 'coverage'},
|
|
410
400
|
{type: 'cobertura', dir: 'coverage', file: 'coverage.xml'},
|
|
401
|
+
{type: 'json-summary', dir: 'coverage', file: 'coverage.json'},
|
|
402
|
+
{type: 'json', dir: 'coverage', file: 'coverage-details.json'},
|
|
411
403
|
],
|
|
412
404
|
},
|
|
413
405
|
});
|
|
@@ -427,7 +427,7 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
427
427
|
onAdComplete_(e) {
|
|
428
428
|
this.onEvent_(new shaka.util.FakeEvent(shaka.ads.AdManager.AD_STOPPED,
|
|
429
429
|
(new Map()).set('originalEvent', e)));
|
|
430
|
-
if (this.ad_.isLinear()) {
|
|
430
|
+
if (this.ad_ && this.ad_.isLinear()) {
|
|
431
431
|
this.adContainer_.removeAttribute('ad-active');
|
|
432
432
|
this.video_.play();
|
|
433
433
|
}
|
package/lib/dash/dash_parser.js
CHANGED
|
@@ -513,6 +513,11 @@ shaka.dash.DashParser = class {
|
|
|
513
513
|
}
|
|
514
514
|
presentationTimeline.setClockOffset(offset);
|
|
515
515
|
}
|
|
516
|
+
|
|
517
|
+
// This is the first point where we have a meaningful presentation start
|
|
518
|
+
// time, and we need to tell PresentationTimeline that so that it can
|
|
519
|
+
// maintain consistency from here on.
|
|
520
|
+
presentationTimeline.lockStartTime();
|
|
516
521
|
} else {
|
|
517
522
|
// Just update the variants and text streams, which may change as periods
|
|
518
523
|
// are added or removed.
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -121,13 +121,19 @@ shaka.hls.HlsParser = class {
|
|
|
121
121
|
this.updatePlaylistDelay_ = 0;
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
|
|
124
|
+
* If true, we have already calculated offsets to synchronize streams.
|
|
125
|
+
* Offsets are computed in syncStreams*_().
|
|
126
|
+
* @private {boolean}
|
|
127
|
+
*/
|
|
128
|
+
this.streamsSynced_ = false;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The minimum sequence number for generated segments, when ignoring
|
|
132
|
+
* EXT-X-PROGRAM-DATE-TIME.
|
|
133
|
+
*
|
|
128
134
|
* @private {?number}
|
|
129
135
|
*/
|
|
130
|
-
this.
|
|
136
|
+
this.minSequenceNumber_ = null;
|
|
131
137
|
|
|
132
138
|
/**
|
|
133
139
|
* This timer is used to trigger the start of a manifest update. A manifest
|
|
@@ -361,39 +367,155 @@ shaka.hls.HlsParser = class {
|
|
|
361
367
|
}
|
|
362
368
|
|
|
363
369
|
/**
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
* measured in time since 1970.
|
|
370
|
+
* Align all streams by sequence number by dropping early segments. Then
|
|
371
|
+
* offset all streams to begin at presentation time 0.
|
|
367
372
|
* @private
|
|
368
373
|
*/
|
|
369
|
-
|
|
370
|
-
if (this.
|
|
371
|
-
// The offset was already calculated.
|
|
374
|
+
syncStreamsWithSequenceNumber_() {
|
|
375
|
+
if (this.streamsSynced_) {
|
|
372
376
|
return;
|
|
373
377
|
}
|
|
374
378
|
|
|
375
|
-
|
|
376
|
-
|
|
379
|
+
// Sync using media sequence number. Find the highest starting sequence
|
|
380
|
+
// number among all streams. Later, we will drop any references to
|
|
381
|
+
// earlier segments in other streams, then offset everything back to 0.
|
|
382
|
+
let highestStartingSequenceNumber = -1;
|
|
383
|
+
const firstSequenceNumberMap = new Map();
|
|
384
|
+
|
|
377
385
|
for (const streamInfo of this.uriToStreamInfosMap_.values()) {
|
|
378
386
|
const segmentIndex = streamInfo.stream.segmentIndex;
|
|
379
387
|
if (segmentIndex) {
|
|
380
|
-
segmentIndex.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
388
|
+
const segment0 = segmentIndex.earliestReference();
|
|
389
|
+
if (segment0) {
|
|
390
|
+
// This looks inefficient, but iteration order is insertion order.
|
|
391
|
+
// So the very first entry should be the one we want.
|
|
392
|
+
// We assert that this holds true so that we are alerted by debug
|
|
393
|
+
// builds and tests if it changes. We still do a loop, though, so
|
|
394
|
+
// that the code functions correctly in production no matter what.
|
|
395
|
+
if (goog.DEBUG) {
|
|
396
|
+
const firstSequenceStartTime =
|
|
397
|
+
streamInfo.mediaSequenceToStartTime.values().next().value;
|
|
398
|
+
goog.asserts.assert(
|
|
399
|
+
firstSequenceStartTime == segment0.startTime,
|
|
400
|
+
'Sequence number map is not ordered as expected!');
|
|
401
|
+
}
|
|
402
|
+
for (const [sequence, start] of streamInfo.mediaSequenceToStartTime) {
|
|
403
|
+
if (start == segment0.startTime) {
|
|
404
|
+
firstSequenceNumberMap.set(streamInfo, sequence);
|
|
405
|
+
|
|
406
|
+
highestStartingSequenceNumber = Math.max(
|
|
407
|
+
highestStartingSequenceNumber, sequence);
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
384
410
|
}
|
|
385
|
-
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (highestStartingSequenceNumber < 0) {
|
|
416
|
+
// Nothing to sync.
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// From now on, updates will ignore any references before this number.
|
|
421
|
+
this.minSequenceNumber_ = highestStartingSequenceNumber;
|
|
422
|
+
|
|
423
|
+
shaka.log.debug('Syncing HLS streams against base sequence number:',
|
|
424
|
+
this.minSequenceNumber_);
|
|
425
|
+
|
|
426
|
+
for (const streamInfo of this.uriToStreamInfosMap_.values()) {
|
|
427
|
+
const segmentIndex = streamInfo.stream.segmentIndex;
|
|
428
|
+
if (segmentIndex) {
|
|
429
|
+
// Drop any earlier references.
|
|
430
|
+
const numSegmentsToDrop = this.minSequenceNumber_ -
|
|
431
|
+
firstSequenceNumberMap.get(streamInfo);
|
|
432
|
+
segmentIndex.dropFirstReferences(numSegmentsToDrop);
|
|
433
|
+
|
|
434
|
+
// Now adjust timestamps back to begin at 0.
|
|
435
|
+
const segmentN = segmentIndex.earliestReference();
|
|
436
|
+
if (segmentN) {
|
|
437
|
+
this.offsetStream_(streamInfo, -segmentN.startTime);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
this.streamsSynced_ = true;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Synchronize streams by the EXT-X-PROGRAM-DATE-TIME tags attached to their
|
|
447
|
+
* segments. Also normalizes segment times so that the earliest segment in
|
|
448
|
+
* any stream is at time 0.
|
|
449
|
+
* @private
|
|
450
|
+
*/
|
|
451
|
+
syncStreamsWithProgramDateTime_() {
|
|
452
|
+
if (this.streamsSynced_) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
let lowestSyncTime = Infinity;
|
|
457
|
+
|
|
458
|
+
for (const streamInfo of this.uriToStreamInfosMap_.values()) {
|
|
459
|
+
const segmentIndex = streamInfo.stream.segmentIndex;
|
|
460
|
+
if (segmentIndex) {
|
|
461
|
+
const segment0 = segmentIndex.earliestReference();
|
|
462
|
+
if (segment0 != null && segment0.syncTime != null) {
|
|
463
|
+
lowestSyncTime = Math.min(lowestSyncTime, segment0.syncTime);
|
|
464
|
+
}
|
|
386
465
|
}
|
|
387
466
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
467
|
+
|
|
468
|
+
if (lowestSyncTime == Infinity) {
|
|
469
|
+
// Nothing to sync.
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
shaka.log.debug('Syncing HLS streams against base time:', lowestSyncTime);
|
|
474
|
+
|
|
475
|
+
for (const streamInfo of this.uriToStreamInfosMap_.values()) {
|
|
476
|
+
const segmentIndex = streamInfo.stream.segmentIndex;
|
|
477
|
+
if (segmentIndex != null) {
|
|
478
|
+
const segment0 = segmentIndex.earliestReference();
|
|
479
|
+
if (segment0.syncTime == null) {
|
|
480
|
+
shaka.log.alwaysError('Missing EXT-X-PROGRAM-DATE-TIME for stream',
|
|
481
|
+
streamInfo.verbatimMediaPlaylistUri,
|
|
482
|
+
'Expect AV sync issues!');
|
|
483
|
+
} else {
|
|
484
|
+
// The first segment's target startTime should be based entirely on
|
|
485
|
+
// its syncTime. The rest of the stream will be based on that
|
|
486
|
+
// starting point. The earliest segment sync time from any stream
|
|
487
|
+
// will become presentation time 0. If two streams start e.g. 6
|
|
488
|
+
// seconds apart in syncTime, then their first segments will also
|
|
489
|
+
// start 6 seconds apart in presentation time.
|
|
490
|
+
const segment0TargetTime = segment0.syncTime - lowestSyncTime;
|
|
491
|
+
const streamOffset = segment0TargetTime - segment0.startTime;
|
|
492
|
+
|
|
493
|
+
this.offsetStream_(streamInfo, streamOffset);
|
|
394
494
|
}
|
|
395
495
|
}
|
|
396
496
|
}
|
|
497
|
+
|
|
498
|
+
this.streamsSynced_ = true;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* @param {!shaka.hls.HlsParser.StreamInfo} streamInfo
|
|
503
|
+
* @param {number} offset
|
|
504
|
+
* @private
|
|
505
|
+
*/
|
|
506
|
+
offsetStream_(streamInfo, offset) {
|
|
507
|
+
streamInfo.stream.segmentIndex.offset(offset);
|
|
508
|
+
|
|
509
|
+
streamInfo.maxTimestamp += offset;
|
|
510
|
+
goog.asserts.assert(streamInfo.maxTimestamp >= 0,
|
|
511
|
+
'Negative maxTimestamp after adjustment!');
|
|
512
|
+
|
|
513
|
+
for (const [key, value] of streamInfo.mediaSequenceToStartTime) {
|
|
514
|
+
streamInfo.mediaSequenceToStartTime.set(key, value + offset);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
shaka.log.debug('Offset', offset, 'applied to',
|
|
518
|
+
streamInfo.verbatimMediaPlaylistUri);
|
|
397
519
|
}
|
|
398
520
|
|
|
399
521
|
/**
|
|
@@ -525,7 +647,11 @@ shaka.hls.HlsParser = class {
|
|
|
525
647
|
|
|
526
648
|
// Now that we have generated all streams, we can determine the offset to
|
|
527
649
|
// apply to sync times.
|
|
528
|
-
this.
|
|
650
|
+
if (this.config_.hls.ignoreManifestProgramDateTime) {
|
|
651
|
+
this.syncStreamsWithSequenceNumber_();
|
|
652
|
+
} else {
|
|
653
|
+
this.syncStreamsWithProgramDateTime_();
|
|
654
|
+
}
|
|
529
655
|
|
|
530
656
|
if (this.aesEncrypted_ && variants.length == 0) {
|
|
531
657
|
// We do not support AES-128 encryption with HLS yet. Variants is null
|
|
@@ -540,15 +666,12 @@ shaka.hls.HlsParser = class {
|
|
|
540
666
|
|
|
541
667
|
// Find the min and max timestamp of the earliest segment in all streams.
|
|
542
668
|
// Find the minimum duration of all streams as well.
|
|
543
|
-
let minFirstTimestamp = Infinity;
|
|
544
669
|
let minDuration = Infinity;
|
|
545
|
-
|
|
546
670
|
for (const streamInfo of this.uriToStreamInfosMap_.values()) {
|
|
547
|
-
minFirstTimestamp =
|
|
548
|
-
Math.min(minFirstTimestamp, streamInfo.minTimestamp);
|
|
549
671
|
if (streamInfo.stream.type != 'text') {
|
|
550
|
-
|
|
551
|
-
|
|
672
|
+
// Since everything is already offset to 0 (either by sync or by being
|
|
673
|
+
// VOD), only maxTimestamp is necessary to compute the duration.
|
|
674
|
+
minDuration = Math.min(minDuration, streamInfo.maxTimestamp);
|
|
552
675
|
}
|
|
553
676
|
}
|
|
554
677
|
|
|
@@ -592,18 +715,11 @@ shaka.hls.HlsParser = class {
|
|
|
592
715
|
segmentAvailabilityDuration);
|
|
593
716
|
}
|
|
594
717
|
} else {
|
|
595
|
-
// For VOD/EVENT content, offset everything back to 0.
|
|
596
|
-
// Use the minimum timestamp as the offset for all streams.
|
|
597
718
|
// Use the minimum duration as the presentation duration.
|
|
598
719
|
this.presentationTimeline_.setDuration(minDuration);
|
|
599
|
-
// Use a negative offset to adjust towards 0.
|
|
600
|
-
this.presentationTimeline_.offset(-minFirstTimestamp);
|
|
601
720
|
|
|
602
721
|
for (const streamInfo of this.uriToStreamInfosMap_.values()) {
|
|
603
|
-
//
|
|
604
|
-
// presentation-aligned times, so offset them all now.
|
|
605
|
-
streamInfo.stream.segmentIndex.offset(-minFirstTimestamp);
|
|
606
|
-
// Finally, fit the segments to the playlist duration.
|
|
722
|
+
// Fit the segments to the playlist duration.
|
|
607
723
|
streamInfo.stream.segmentIndex.fit(/* periodStart= */ 0, minDuration);
|
|
608
724
|
}
|
|
609
725
|
}
|
|
@@ -624,8 +740,14 @@ shaka.hls.HlsParser = class {
|
|
|
624
740
|
for (const stream of streamsToNotify) {
|
|
625
741
|
this.segmentsToNotifyByStream_.push(stream.segmentIndex.references);
|
|
626
742
|
}
|
|
743
|
+
|
|
627
744
|
this.notifySegments_();
|
|
628
745
|
|
|
746
|
+
// This is the first point where we have a meaningful presentation start
|
|
747
|
+
// time, and we need to tell PresentationTimeline that so that it can
|
|
748
|
+
// maintain consistency from here on.
|
|
749
|
+
this.presentationTimeline_.lockStartTime();
|
|
750
|
+
|
|
629
751
|
// This asserts that the live edge is being calculated from segment times.
|
|
630
752
|
// For VOD and event streams, this check should still pass.
|
|
631
753
|
goog.asserts.assert(
|
|
@@ -811,12 +933,7 @@ shaka.hls.HlsParser = class {
|
|
|
811
933
|
});
|
|
812
934
|
|
|
813
935
|
// Create stream info for each audio / video media tag.
|
|
814
|
-
|
|
815
|
-
// and can be reused.
|
|
816
|
-
if (mediaTags.length) {
|
|
817
|
-
await this.createStreamInfoFromMediaTag_(mediaTags[0]);
|
|
818
|
-
}
|
|
819
|
-
const promises = mediaTags.slice(1).map((tag) => {
|
|
936
|
+
const promises = mediaTags.map((tag) => {
|
|
820
937
|
return this.createStreamInfoFromMediaTag_(tag);
|
|
821
938
|
});
|
|
822
939
|
await Promise.all(promises);
|
|
@@ -1584,7 +1701,6 @@ shaka.hls.HlsParser = class {
|
|
|
1584
1701
|
throw error;
|
|
1585
1702
|
}
|
|
1586
1703
|
|
|
1587
|
-
const minTimestamp = segments[0].startTime;
|
|
1588
1704
|
const lastEndTime = segments[segments.length - 1].endTime;
|
|
1589
1705
|
/** @type {!shaka.media.SegmentIndex} */
|
|
1590
1706
|
const segmentIndex = new shaka.media.SegmentIndex(segments);
|
|
@@ -1642,7 +1758,6 @@ shaka.hls.HlsParser = class {
|
|
|
1642
1758
|
stream,
|
|
1643
1759
|
verbatimMediaPlaylistUri,
|
|
1644
1760
|
absoluteMediaPlaylistUri,
|
|
1645
|
-
minTimestamp,
|
|
1646
1761
|
maxTimestamp: lastEndTime,
|
|
1647
1762
|
mediaSequenceToStartTime,
|
|
1648
1763
|
canSkipSegments,
|
|
@@ -1750,8 +1865,6 @@ shaka.hls.HlsParser = class {
|
|
|
1750
1865
|
/* presentationStartTime= */ null, /* delay= */ 0);
|
|
1751
1866
|
this.presentationTimeline_.setStatic(true);
|
|
1752
1867
|
}
|
|
1753
|
-
|
|
1754
|
-
this.notifySegments_();
|
|
1755
1868
|
}
|
|
1756
1869
|
|
|
1757
1870
|
/**
|
|
@@ -1854,15 +1967,9 @@ shaka.hls.HlsParser = class {
|
|
|
1854
1967
|
const dateTimeTag =
|
|
1855
1968
|
shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-PROGRAM-DATE-TIME');
|
|
1856
1969
|
if (dateTimeTag && dateTimeTag.value) {
|
|
1857
|
-
|
|
1858
|
-
goog.asserts.assert(
|
|
1970
|
+
syncTime = shaka.util.XmlUtils.parseDate(dateTimeTag.value);
|
|
1971
|
+
goog.asserts.assert(syncTime != null,
|
|
1859
1972
|
'EXT-X-PROGRAM-DATE-TIME format not valid');
|
|
1860
|
-
// Sync time offset is null on the first go-through. This indicates that
|
|
1861
|
-
// we have not yet seen every stream, and thus do not yet have enough
|
|
1862
|
-
// information to determine how to normalize the sync times.
|
|
1863
|
-
// For that first go-through, the sync time will be applied after the
|
|
1864
|
-
// references are all created. Until then, just offset by 0.
|
|
1865
|
-
syncTime = time + (this.syncTimeOffset_ || 0);
|
|
1866
1973
|
}
|
|
1867
1974
|
}
|
|
1868
1975
|
|
|
@@ -2096,16 +2203,13 @@ shaka.hls.HlsParser = class {
|
|
|
2096
2203
|
firstStartTime = mediaSequenceToStartTime.get(position);
|
|
2097
2204
|
}
|
|
2098
2205
|
|
|
2099
|
-
const firstSegmentUri = hlsSegments[0].absoluteUri;
|
|
2100
|
-
shaka.log.debug('First segment', firstSegmentUri.split('/').pop(),
|
|
2101
|
-
'starts at', firstStartTime);
|
|
2102
|
-
|
|
2103
2206
|
/** @type {!Array.<!shaka.media.SegmentReference>} */
|
|
2104
2207
|
const references = [];
|
|
2105
2208
|
|
|
2209
|
+
let previousReference = null;
|
|
2210
|
+
|
|
2106
2211
|
for (let i = 0; i < hlsSegments.length; i++) {
|
|
2107
2212
|
const item = hlsSegments[i];
|
|
2108
|
-
const previousReference = references[references.length - 1];
|
|
2109
2213
|
const startTime =
|
|
2110
2214
|
(i == 0) ? firstStartTime : previousReference.endTime;
|
|
2111
2215
|
position = mediaSequenceNumber + skippedSegments + i;
|
|
@@ -2135,9 +2239,17 @@ shaka.hls.HlsParser = class {
|
|
|
2135
2239
|
variables,
|
|
2136
2240
|
playlist.absoluteUri,
|
|
2137
2241
|
type);
|
|
2242
|
+
previousReference = reference;
|
|
2138
2243
|
|
|
2139
2244
|
if (reference) {
|
|
2140
|
-
|
|
2245
|
+
if (this.config_.hls.ignoreManifestProgramDateTime &&
|
|
2246
|
+
this.minSequenceNumber_ != null &&
|
|
2247
|
+
position < this.minSequenceNumber_) {
|
|
2248
|
+
// This segment is ignored as part of our fallback synchronization
|
|
2249
|
+
// method.
|
|
2250
|
+
} else {
|
|
2251
|
+
references.push(reference);
|
|
2252
|
+
}
|
|
2141
2253
|
}
|
|
2142
2254
|
}
|
|
2143
2255
|
|
|
@@ -2267,12 +2379,16 @@ shaka.hls.HlsParser = class {
|
|
|
2267
2379
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
2268
2380
|
const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
|
2269
2381
|
|
|
2382
|
+
// If you wait long enough, requesting the first segment can fail
|
|
2383
|
+
// because it has fallen off the left edge of DVR, so to be safer,
|
|
2384
|
+
// let's request the middle segment.
|
|
2270
2385
|
goog.asserts.assert(playlist.segments.length,
|
|
2271
2386
|
'Playlist should have segments!');
|
|
2272
|
-
const
|
|
2273
|
-
|
|
2387
|
+
const middleSegmentIdx = Math.trunc((playlist.segments.length - 1) / 2);
|
|
2388
|
+
const middleSegmentUri = this.variableSubstitution_(
|
|
2389
|
+
playlist.segments[middleSegmentIdx].absoluteUri, variables);
|
|
2274
2390
|
|
|
2275
|
-
const parsedUri = new goog.Uri(
|
|
2391
|
+
const parsedUri = new goog.Uri(middleSegmentUri);
|
|
2276
2392
|
const extension = parsedUri.getPath().split('.').pop();
|
|
2277
2393
|
const map = HlsParser.EXTENSION_MAP_BY_CONTENT_TYPE_[contentType];
|
|
2278
2394
|
|
|
@@ -2302,7 +2418,7 @@ shaka.hls.HlsParser = class {
|
|
|
2302
2418
|
// If unable to guess mime type, request a segment and try getting it
|
|
2303
2419
|
// from the response.
|
|
2304
2420
|
const headRequest = shaka.net.NetworkingEngine.makeRequest(
|
|
2305
|
-
[
|
|
2421
|
+
[middleSegmentUri], this.config_.retryParameters);
|
|
2306
2422
|
headRequest.method = 'HEAD';
|
|
2307
2423
|
|
|
2308
2424
|
const response = await this.makeNetworkRequest_(
|
|
@@ -2589,7 +2705,6 @@ shaka.hls.HlsParser = class {
|
|
|
2589
2705
|
* stream: !shaka.extern.Stream,
|
|
2590
2706
|
* verbatimMediaPlaylistUri: string,
|
|
2591
2707
|
* absoluteMediaPlaylistUri: string,
|
|
2592
|
-
* minTimestamp: number,
|
|
2593
2708
|
* maxTimestamp: number,
|
|
2594
2709
|
* mediaSequenceToStartTime: !Map.<number, number>,
|
|
2595
2710
|
* canSkipSegments: boolean
|
|
@@ -2608,8 +2723,6 @@ shaka.hls.HlsParser = class {
|
|
|
2608
2723
|
* @property {string} absoluteMediaPlaylistUri
|
|
2609
2724
|
* The absolute media playlist URI, resolved relative to the master playlist
|
|
2610
2725
|
* and updated to reflect any redirects.
|
|
2611
|
-
* @property {number} minTimestamp
|
|
2612
|
-
* The minimum timestamp found in the stream.
|
|
2613
2726
|
* @property {number} maxTimestamp
|
|
2614
2727
|
* The maximum timestamp found in the stream.
|
|
2615
2728
|
* @property {!Map.<number, number>} mediaSequenceToStartTime
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -1548,7 +1548,7 @@ shaka.media.DrmEngine = class {
|
|
|
1548
1548
|
if (shaka.media.DrmEngine.isPlayReadyKeySystem(
|
|
1549
1549
|
this.currentDrmInfo_.keySystem) &&
|
|
1550
1550
|
keyId.byteLength == 16 &&
|
|
1551
|
-
shaka.util.Platform.isEdge()) {
|
|
1551
|
+
(shaka.util.Platform.isEdge() || shaka.util.Platform.isPS4())) {
|
|
1552
1552
|
// Read out some fields in little-endian:
|
|
1553
1553
|
const dataView = shaka.util.BufferUtils.toDataView(keyId);
|
|
1554
1554
|
const part0 = dataView.getUint32(0, /* LE= */ true);
|