shaka-player 4.0.2 → 4.0.5
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 +43 -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 +269 -256
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +52 -40
- package/dist/shaka-player.compiled.js +739 -730
- 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 -282
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +52 -40
- package/dist/shaka-player.ui.js +885 -878
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/ui.md +5 -5
- 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="1AAA1145">
|
|
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="1AAA1145">
|
|
115
115
|
</div>
|
|
116
116
|
```
|
|
117
117
|
|
|
@@ -137,7 +137,7 @@ or a `<source>` tag inside it to enable auto loading of the specified content.
|
|
|
137
137
|
|
|
138
138
|
```html
|
|
139
139
|
<div data-shaka-player-container style="max-width:40em"
|
|
140
|
-
data-shaka-player-cast-receiver-id="
|
|
140
|
+
data-shaka-player-cast-receiver-id="1AAA1145">
|
|
141
141
|
<!-- The manifest url in the src attribute will be automatically loaded -->
|
|
142
142
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%"
|
|
143
143
|
src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"></video>
|
|
@@ -148,7 +148,7 @@ or
|
|
|
148
148
|
|
|
149
149
|
```html
|
|
150
150
|
<div data-shaka-player-container style="max-width:40em"
|
|
151
|
-
data-shaka-player-cast-receiver-id="
|
|
151
|
+
data-shaka-player-cast-receiver-id="1AAA1145">
|
|
152
152
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%">
|
|
153
153
|
<!-- The manifest url in the src attribute will be auto loaded -->
|
|
154
154
|
<source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
|
|
@@ -161,7 +161,7 @@ call to the first one fails.
|
|
|
161
161
|
|
|
162
162
|
```html
|
|
163
163
|
<div data-shaka-player-container style="max-width:40em"
|
|
164
|
-
data-shaka-player-cast-receiver-id="
|
|
164
|
+
data-shaka-player-cast-receiver-id="1AAA1145">
|
|
165
165
|
<video autoplay data-shaka-player id="video" style="width:100%;height:100%">
|
|
166
166
|
<!-- Try this first -->
|
|
167
167
|
<source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
|
|
@@ -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
|
@@ -766,8 +766,10 @@ shaka.extern.DashManifestConfiguration;
|
|
|
766
766
|
* <i>Defaults to <code>'avc1.42E01E'</code>.</i>
|
|
767
767
|
* @property {boolean} ignoreManifestProgramDateTime
|
|
768
768
|
* If <code>true</code>, the HLS parser will ignore the
|
|
769
|
-
* <code>EXT-X-PROGRAM-DATE-TIME</code> tags in the manifest
|
|
770
|
-
*
|
|
769
|
+
* <code>EXT-X-PROGRAM-DATE-TIME</code> tags in the manifest and use media
|
|
770
|
+
* sequence numbers instead.
|
|
771
|
+
* Meant for streams where <code>EXT-X-PROGRAM-DATE-TIME</code> is incorrect
|
|
772
|
+
* or malformed.
|
|
771
773
|
* <i>Defaults to <code>false</code>.</i>
|
|
772
774
|
* @property {string} mediaPlaylistFullMimeType
|
|
773
775
|
* 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
|
/**
|
|
@@ -524,7 +646,11 @@ shaka.hls.HlsParser = class {
|
|
|
524
646
|
|
|
525
647
|
// Now that we have generated all streams, we can determine the offset to
|
|
526
648
|
// apply to sync times.
|
|
527
|
-
this.
|
|
649
|
+
if (this.config_.hls.ignoreManifestProgramDateTime) {
|
|
650
|
+
this.syncStreamsWithSequenceNumber_();
|
|
651
|
+
} else {
|
|
652
|
+
this.syncStreamsWithProgramDateTime_();
|
|
653
|
+
}
|
|
528
654
|
|
|
529
655
|
if (this.aesEncrypted_ && variants.length == 0) {
|
|
530
656
|
// We do not support AES-128 encryption with HLS yet. Variants is null
|
|
@@ -539,15 +665,12 @@ shaka.hls.HlsParser = class {
|
|
|
539
665
|
|
|
540
666
|
// Find the min and max timestamp of the earliest segment in all streams.
|
|
541
667
|
// Find the minimum duration of all streams as well.
|
|
542
|
-
let minFirstTimestamp = Infinity;
|
|
543
668
|
let minDuration = Infinity;
|
|
544
|
-
|
|
545
669
|
for (const streamInfo of this.uriToStreamInfosMap_.values()) {
|
|
546
|
-
minFirstTimestamp =
|
|
547
|
-
Math.min(minFirstTimestamp, streamInfo.minTimestamp);
|
|
548
670
|
if (streamInfo.stream.type != 'text') {
|
|
549
|
-
|
|
550
|
-
|
|
671
|
+
// Since everything is already offset to 0 (either by sync or by being
|
|
672
|
+
// VOD), only maxTimestamp is necessary to compute the duration.
|
|
673
|
+
minDuration = Math.min(minDuration, streamInfo.maxTimestamp);
|
|
551
674
|
}
|
|
552
675
|
}
|
|
553
676
|
|
|
@@ -591,18 +714,11 @@ shaka.hls.HlsParser = class {
|
|
|
591
714
|
segmentAvailabilityDuration);
|
|
592
715
|
}
|
|
593
716
|
} else {
|
|
594
|
-
// For VOD/EVENT content, offset everything back to 0.
|
|
595
|
-
// Use the minimum timestamp as the offset for all streams.
|
|
596
717
|
// Use the minimum duration as the presentation duration.
|
|
597
718
|
this.presentationTimeline_.setDuration(minDuration);
|
|
598
|
-
// Use a negative offset to adjust towards 0.
|
|
599
|
-
this.presentationTimeline_.offset(-minFirstTimestamp);
|
|
600
719
|
|
|
601
720
|
for (const streamInfo of this.uriToStreamInfosMap_.values()) {
|
|
602
|
-
//
|
|
603
|
-
// presentation-aligned times, so offset them all now.
|
|
604
|
-
streamInfo.stream.segmentIndex.offset(-minFirstTimestamp);
|
|
605
|
-
// Finally, fit the segments to the playlist duration.
|
|
721
|
+
// Fit the segments to the playlist duration.
|
|
606
722
|
streamInfo.stream.segmentIndex.fit(/* periodStart= */ 0, minDuration);
|
|
607
723
|
}
|
|
608
724
|
}
|
|
@@ -623,8 +739,14 @@ shaka.hls.HlsParser = class {
|
|
|
623
739
|
for (const stream of streamsToNotify) {
|
|
624
740
|
this.segmentsToNotifyByStream_.push(stream.segmentIndex.references);
|
|
625
741
|
}
|
|
742
|
+
|
|
626
743
|
this.notifySegments_();
|
|
627
744
|
|
|
745
|
+
// This is the first point where we have a meaningful presentation start
|
|
746
|
+
// time, and we need to tell PresentationTimeline that so that it can
|
|
747
|
+
// maintain consistency from here on.
|
|
748
|
+
this.presentationTimeline_.lockStartTime();
|
|
749
|
+
|
|
628
750
|
// This asserts that the live edge is being calculated from segment times.
|
|
629
751
|
// For VOD and event streams, this check should still pass.
|
|
630
752
|
goog.asserts.assert(
|
|
@@ -810,12 +932,7 @@ shaka.hls.HlsParser = class {
|
|
|
810
932
|
});
|
|
811
933
|
|
|
812
934
|
// Create stream info for each audio / video media tag.
|
|
813
|
-
|
|
814
|
-
// and can be reused.
|
|
815
|
-
if (mediaTags.length) {
|
|
816
|
-
await this.createStreamInfoFromMediaTag_(mediaTags[0]);
|
|
817
|
-
}
|
|
818
|
-
const promises = mediaTags.slice(1).map((tag) => {
|
|
935
|
+
const promises = mediaTags.map((tag) => {
|
|
819
936
|
return this.createStreamInfoFromMediaTag_(tag);
|
|
820
937
|
});
|
|
821
938
|
await Promise.all(promises);
|
|
@@ -1583,7 +1700,6 @@ shaka.hls.HlsParser = class {
|
|
|
1583
1700
|
throw error;
|
|
1584
1701
|
}
|
|
1585
1702
|
|
|
1586
|
-
const minTimestamp = segments[0].startTime;
|
|
1587
1703
|
const lastEndTime = segments[segments.length - 1].endTime;
|
|
1588
1704
|
/** @type {!shaka.media.SegmentIndex} */
|
|
1589
1705
|
const segmentIndex = new shaka.media.SegmentIndex(segments);
|
|
@@ -1641,7 +1757,6 @@ shaka.hls.HlsParser = class {
|
|
|
1641
1757
|
stream,
|
|
1642
1758
|
verbatimMediaPlaylistUri,
|
|
1643
1759
|
absoluteMediaPlaylistUri,
|
|
1644
|
-
minTimestamp,
|
|
1645
1760
|
maxTimestamp: lastEndTime,
|
|
1646
1761
|
mediaSequenceToStartTime,
|
|
1647
1762
|
canSkipSegments,
|
|
@@ -1749,8 +1864,6 @@ shaka.hls.HlsParser = class {
|
|
|
1749
1864
|
/* presentationStartTime= */ null, /* delay= */ 0);
|
|
1750
1865
|
this.presentationTimeline_.setStatic(true);
|
|
1751
1866
|
}
|
|
1752
|
-
|
|
1753
|
-
this.notifySegments_();
|
|
1754
1867
|
}
|
|
1755
1868
|
|
|
1756
1869
|
/**
|
|
@@ -1853,15 +1966,9 @@ shaka.hls.HlsParser = class {
|
|
|
1853
1966
|
const dateTimeTag =
|
|
1854
1967
|
shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-PROGRAM-DATE-TIME');
|
|
1855
1968
|
if (dateTimeTag && dateTimeTag.value) {
|
|
1856
|
-
|
|
1857
|
-
goog.asserts.assert(
|
|
1969
|
+
syncTime = shaka.util.XmlUtils.parseDate(dateTimeTag.value);
|
|
1970
|
+
goog.asserts.assert(syncTime != null,
|
|
1858
1971
|
'EXT-X-PROGRAM-DATE-TIME format not valid');
|
|
1859
|
-
// Sync time offset is null on the first go-through. This indicates that
|
|
1860
|
-
// we have not yet seen every stream, and thus do not yet have enough
|
|
1861
|
-
// information to determine how to normalize the sync times.
|
|
1862
|
-
// For that first go-through, the sync time will be applied after the
|
|
1863
|
-
// references are all created. Until then, just offset by 0.
|
|
1864
|
-
syncTime = time + (this.syncTimeOffset_ || 0);
|
|
1865
1972
|
}
|
|
1866
1973
|
}
|
|
1867
1974
|
|
|
@@ -2085,16 +2192,13 @@ shaka.hls.HlsParser = class {
|
|
|
2085
2192
|
firstStartTime = mediaSequenceToStartTime.get(position);
|
|
2086
2193
|
}
|
|
2087
2194
|
|
|
2088
|
-
const firstSegmentUri = hlsSegments[0].absoluteUri;
|
|
2089
|
-
shaka.log.debug('First segment', firstSegmentUri.split('/').pop(),
|
|
2090
|
-
'starts at', firstStartTime);
|
|
2091
|
-
|
|
2092
2195
|
/** @type {!Array.<!shaka.media.SegmentReference>} */
|
|
2093
2196
|
const references = [];
|
|
2094
2197
|
|
|
2198
|
+
let previousReference = null;
|
|
2199
|
+
|
|
2095
2200
|
for (let i = 0; i < hlsSegments.length; i++) {
|
|
2096
2201
|
const item = hlsSegments[i];
|
|
2097
|
-
const previousReference = references[references.length - 1];
|
|
2098
2202
|
const startTime =
|
|
2099
2203
|
(i == 0) ? firstStartTime : previousReference.endTime;
|
|
2100
2204
|
position = mediaSequenceNumber + skippedSegments + i;
|
|
@@ -2124,9 +2228,17 @@ shaka.hls.HlsParser = class {
|
|
|
2124
2228
|
variables,
|
|
2125
2229
|
playlist.absoluteUri,
|
|
2126
2230
|
type);
|
|
2231
|
+
previousReference = reference;
|
|
2127
2232
|
|
|
2128
2233
|
if (reference) {
|
|
2129
|
-
|
|
2234
|
+
if (this.config_.hls.ignoreManifestProgramDateTime &&
|
|
2235
|
+
this.minSequenceNumber_ != null &&
|
|
2236
|
+
position < this.minSequenceNumber_) {
|
|
2237
|
+
// This segment is ignored as part of our fallback synchronization
|
|
2238
|
+
// method.
|
|
2239
|
+
} else {
|
|
2240
|
+
references.push(reference);
|
|
2241
|
+
}
|
|
2130
2242
|
}
|
|
2131
2243
|
}
|
|
2132
2244
|
|
|
@@ -2256,12 +2368,16 @@ shaka.hls.HlsParser = class {
|
|
|
2256
2368
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
2257
2369
|
const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
|
|
2258
2370
|
|
|
2371
|
+
// If you wait long enough, requesting the first segment can fail
|
|
2372
|
+
// because it has fallen off the left edge of DVR, so to be safer,
|
|
2373
|
+
// let's request the middle segment.
|
|
2259
2374
|
goog.asserts.assert(playlist.segments.length,
|
|
2260
2375
|
'Playlist should have segments!');
|
|
2261
|
-
const
|
|
2262
|
-
|
|
2376
|
+
const middleSegmentIdx = Math.trunc((playlist.segments.length - 1) / 2);
|
|
2377
|
+
const middleSegmentUri = this.variableSubstitution_(
|
|
2378
|
+
playlist.segments[middleSegmentIdx].absoluteUri, variables);
|
|
2263
2379
|
|
|
2264
|
-
const parsedUri = new goog.Uri(
|
|
2380
|
+
const parsedUri = new goog.Uri(middleSegmentUri);
|
|
2265
2381
|
const extension = parsedUri.getPath().split('.').pop();
|
|
2266
2382
|
const map = HlsParser.EXTENSION_MAP_BY_CONTENT_TYPE_[contentType];
|
|
2267
2383
|
|
|
@@ -2291,7 +2407,7 @@ shaka.hls.HlsParser = class {
|
|
|
2291
2407
|
// If unable to guess mime type, request a segment and try getting it
|
|
2292
2408
|
// from the response.
|
|
2293
2409
|
const headRequest = shaka.net.NetworkingEngine.makeRequest(
|
|
2294
|
-
[
|
|
2410
|
+
[middleSegmentUri], this.config_.retryParameters);
|
|
2295
2411
|
headRequest.method = 'HEAD';
|
|
2296
2412
|
|
|
2297
2413
|
const response = await this.makeNetworkRequest_(
|
|
@@ -2578,7 +2694,6 @@ shaka.hls.HlsParser = class {
|
|
|
2578
2694
|
* stream: !shaka.extern.Stream,
|
|
2579
2695
|
* verbatimMediaPlaylistUri: string,
|
|
2580
2696
|
* absoluteMediaPlaylistUri: string,
|
|
2581
|
-
* minTimestamp: number,
|
|
2582
2697
|
* maxTimestamp: number,
|
|
2583
2698
|
* mediaSequenceToStartTime: !Map.<number, number>,
|
|
2584
2699
|
* canSkipSegments: boolean
|
|
@@ -2597,8 +2712,6 @@ shaka.hls.HlsParser = class {
|
|
|
2597
2712
|
* @property {string} absoluteMediaPlaylistUri
|
|
2598
2713
|
* The absolute media playlist URI, resolved relative to the master playlist
|
|
2599
2714
|
* and updated to reflect any redirects.
|
|
2600
|
-
* @property {number} minTimestamp
|
|
2601
|
-
* The minimum timestamp found in the stream.
|
|
2602
2715
|
* @property {number} maxTimestamp
|
|
2603
2716
|
* The maximum timestamp found in the stream.
|
|
2604
2717
|
* @property {!Map.<number, number>} mediaSequenceToStartTime
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -1547,7 +1547,7 @@ shaka.media.DrmEngine = class {
|
|
|
1547
1547
|
if (shaka.media.DrmEngine.isPlayReadyKeySystem(
|
|
1548
1548
|
this.currentDrmInfo_.keySystem) &&
|
|
1549
1549
|
keyId.byteLength == 16 &&
|
|
1550
|
-
shaka.util.Platform.isEdge()) {
|
|
1550
|
+
(shaka.util.Platform.isEdge() || shaka.util.Platform.isPS4())) {
|
|
1551
1551
|
// Read out some fields in little-endian:
|
|
1552
1552
|
const dataView = shaka.util.BufferUtils.toDataView(keyId);
|
|
1553
1553
|
const part0 = dataView.getUint32(0, /* LE= */ true);
|