shaka-player 3.1.0 → 3.1.4
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/AUTHORS +8 -2
- package/CHANGELOG.md +209 -0
- package/CONTRIBUTORS +10 -3
- package/README.md +3 -0
- package/build/apps.py +2 -2
- package/build/check.py +2 -2
- package/build/conformance.textproto +0 -9
- package/build/generateExterns.js +1 -1
- package/build/misspellings.txt +1 -0
- package/build/shakaBuildHelpers.py +6 -5
- package/build/subprocessWindowsPatch.py +103 -0
- package/demo/common/asset.js +11 -0
- package/demo/common/assets.js +1 -1
- package/demo/common/message_ids.js +5 -0
- package/demo/config.js +2 -1
- package/demo/custom.js +84 -3
- package/demo/demo.less +13 -1
- package/demo/demo_utils.js +1 -0
- package/demo/input.js +31 -12
- package/demo/input_container.js +24 -1
- package/demo/locales/en.json +6 -1
- package/demo/locales/source.json +20 -0
- package/demo/main.js +5 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +99 -91
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +99 -91
- package/dist/demo.compiled.map +3 -3
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +9 -9
- package/dist/locales.js +36 -34
- package/dist/receiver.compiled.debug.js +74 -70
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +74 -70
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +165 -9
- package/dist/shaka-player.compiled.debug.d.ts +165 -9
- package/dist/shaka-player.compiled.debug.externs.js +179 -11
- package/dist/shaka-player.compiled.debug.js +367 -339
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +179 -11
- package/dist/shaka-player.compiled.js +818 -807
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +165 -9
- package/dist/shaka-player.ui.debug.d.ts +165 -9
- package/dist/shaka-player.ui.debug.externs.js +181 -13
- package/dist/shaka-player.ui.debug.js +459 -429
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +181 -13
- package/dist/shaka-player.ui.js +973 -961
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/faq.md +25 -0
- package/docs/tutorials/index.json +2 -2
- package/docs/tutorials/upgrade-v3.md +38 -0
- package/docs/tutorials/upgrade.md +3 -3
- package/externs/ima.js +4 -0
- package/externs/media_capabilities.js +16 -0
- package/externs/shaka/ads.js +2 -0
- package/externs/shaka/net.js +2 -2
- package/externs/shaka/player.js +6 -0
- package/karma.conf.js +1 -0
- package/lib/abr/simple_abr_manager.js +1 -1
- package/lib/ads/ad_manager.js +29 -11
- package/lib/ads/client_side_ad_manager.js +76 -86
- package/lib/ads/server_side_ad_manager.js +26 -11
- package/lib/cast/cast_proxy.js +3 -2
- package/lib/cast/cast_receiver.js +2 -1
- package/lib/cast/cast_sender.js +2 -2
- package/lib/cea/sei_processor.js +24 -17
- package/lib/dash/dash_parser.js +107 -4
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +45 -15
- package/lib/dependencies/all.js +15 -15
- package/lib/hls/hls_parser.js +46 -18
- package/lib/media/closed_caption_parser.js +3 -1
- package/lib/media/content_workarounds.js +18 -0
- package/lib/media/drm_engine.js +20 -1
- package/lib/media/gap_jumping_controller.js +8 -4
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/playhead.js +1 -1
- package/lib/media/presentation_timeline.js +3 -1
- package/lib/media/segment_index.js +38 -12
- package/lib/media/segment_reference.js +8 -0
- package/lib/media/streaming_engine.js +30 -21
- package/lib/net/http_fetch_plugin.js +2 -2
- package/lib/net/networking_engine.js +3 -2
- package/lib/offline/storage.js +4 -0
- package/lib/player.js +113 -56
- package/lib/polyfill/encryption_scheme.js +2 -0
- package/lib/polyfill/fullscreen.js +2 -0
- package/lib/polyfill/mathround.js +2 -0
- package/lib/polyfill/media_capabilities.js +62 -23
- package/lib/polyfill/mediasource.js +8 -0
- package/lib/polyfill/orientation.js +3 -1
- package/lib/polyfill/patchedmediakeys_apple.js +6 -4
- package/lib/polyfill/patchedmediakeys_ms.js +6 -4
- package/lib/polyfill/patchedmediakeys_nop.js +2 -0
- package/lib/polyfill/patchedmediakeys_webkit.js +6 -4
- package/lib/polyfill/pip_webkit.js +2 -0
- package/lib/polyfill/storage_estimate.js +2 -0
- package/lib/polyfill/video_play_promise.js +2 -0
- package/lib/polyfill/videoplaybackquality.js +2 -0
- package/lib/polyfill/vttcue.js +2 -0
- package/lib/text/cue.js +11 -0
- package/lib/text/mp4_ttml_parser.js +3 -0
- package/lib/text/text_engine.js +14 -2
- package/lib/text/ttml_text_parser.js +52 -30
- package/lib/text/ui_text_displayer.js +255 -82
- package/lib/text/vtt_text_parser.js +4 -2
- package/lib/util/error.js +1 -4
- package/lib/util/fake_event.js +53 -7
- package/lib/util/mime_utils.js +33 -0
- package/lib/util/mp4_box_parsers.js +1 -1
- package/lib/util/mp4_parser.js +14 -0
- package/lib/util/networking.js +1 -1
- package/lib/util/periods.js +132 -37
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +46 -7
- package/package.json +3 -3
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_receiver_integration.js +1 -1
- package/test/cast/cast_utils_unit.js +1 -1
- package/test/cea/mp4_cea_parser_unit.js +26 -0
- package/test/dash/dash_parser_live_unit.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +187 -1
- package/test/dash/dash_parser_segment_template_unit.js +3 -3
- package/test/demo/demo_unit.js +0 -1
- package/test/hls/hls_parser_unit.js +45 -1
- package/test/media/closed_caption_parser_unit.js +20 -0
- package/test/media/drm_engine_unit.js +59 -3
- package/test/media/playhead_unit.js +27 -1
- package/test/media/presentation_timeline_unit.js +19 -0
- package/test/media/segment_index_unit.js +76 -3
- package/test/media/streaming_engine_unit.js +3 -3
- package/test/player_integration.js +38 -0
- package/test/player_unit.js +125 -6
- package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
- package/test/test/assets/3675/dash_0.mpd +30 -0
- package/test/test/assets/3675/dash_1.mpd +30 -0
- package/test/test/assets/3675/dash_2.mpd +30 -0
- package/test/test/assets/3675/dash_3.mpd +30 -0
- package/test/test/assets/3675/dash_4.mpd +30 -0
- package/test/test/assets/3675/dash_5.mpd +26 -0
- package/test/test/assets/3675/init-stream0.m4s +0 -0
- package/test/test/assets/empty_caption_video_init.mp4 +0 -0
- package/test/test/assets/empty_caption_video_segment.mp4 +0 -0
- package/test/test/util/fake_ad_manager.js +4 -1
- package/test/test/util/simple_fakes.js +5 -3
- package/test/test/util/waiter.js +36 -2
- package/test/text/ttml_text_parser_unit.js +55 -0
- package/test/text/ui_text_displayer_unit.js +95 -18
- package/test/text/vtt_text_parser_unit.js +17 -1
- package/test/ui/localization_unit.js +15 -14
- package/test/ui/text_displayer_layout_unit.js +52 -1
- package/test/ui/ui_unit.js +4 -0
- package/test/util/mp4_box_parsers_unit.js +28 -0
- package/test/util/periods_unit.js +190 -2
- package/test/util/stream_utils_unit.js +56 -5
- package/ui/airplay_button.js +5 -0
- package/ui/big_play_button.js +10 -0
- package/ui/cast_button.js +2 -3
- package/ui/controls.js +9 -7
- package/ui/less/containers.less +10 -5
- package/ui/locales/en.json +1 -0
- package/ui/locales/ru.json +6 -6
- package/ui/locales/source.json +4 -0
- package/ui/localization.js +28 -51
- package/ui/pip_button.js +2 -3
- package/ui/play_button.js +10 -10
- package/ui/small_play_button.js +12 -0
- package/ui/ui.js +1 -1
- package/docs/tutorials/upgrade-v2-4.md +0 -481
package/lib/hls/hls_parser.js
CHANGED
|
@@ -414,18 +414,16 @@ shaka.hls.HlsParser = class {
|
|
|
414
414
|
const uri = tag.getAttributeValue('URI');
|
|
415
415
|
const language = tag.getAttributeValue('LANGUAGE');
|
|
416
416
|
const value = tag.getAttributeValue('VALUE');
|
|
417
|
-
const data =
|
|
418
|
-
'id': id,
|
|
419
|
-
};
|
|
417
|
+
const data = (new Map()).set('id', id);
|
|
420
418
|
if (uri) {
|
|
421
|
-
data.uri
|
|
422
|
-
shaka.hls.Utils.constructAbsoluteUri(this.masterPlaylistUri_, uri);
|
|
419
|
+
data.set('uri',
|
|
420
|
+
shaka.hls.Utils.constructAbsoluteUri(this.masterPlaylistUri_, uri));
|
|
423
421
|
}
|
|
424
422
|
if (language) {
|
|
425
|
-
data.language
|
|
423
|
+
data.set('language', language);
|
|
426
424
|
}
|
|
427
425
|
if (value) {
|
|
428
|
-
data.value
|
|
426
|
+
data.set('value', value);
|
|
429
427
|
}
|
|
430
428
|
const event = new shaka.util.FakeEvent('sessiondata', data);
|
|
431
429
|
if (this.playerInterface_) {
|
|
@@ -529,6 +527,30 @@ shaka.hls.HlsParser = class {
|
|
|
529
527
|
}
|
|
530
528
|
}
|
|
531
529
|
|
|
530
|
+
// Now that the content has been fit, notify segments.
|
|
531
|
+
this.segmentsToNotifyByStream_ = [];
|
|
532
|
+
const streamsToNotify = [];
|
|
533
|
+
for (const variant of variants) {
|
|
534
|
+
for (const stream of [variant.video, variant.audio]) {
|
|
535
|
+
if (stream) {
|
|
536
|
+
streamsToNotify.push(stream);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
await Promise.all(streamsToNotify.map(async (stream) => {
|
|
541
|
+
await stream.createSegmentIndex();
|
|
542
|
+
}));
|
|
543
|
+
for (const stream of streamsToNotify) {
|
|
544
|
+
this.segmentsToNotifyByStream_.push(stream.segmentIndex.references);
|
|
545
|
+
}
|
|
546
|
+
this.notifySegments_();
|
|
547
|
+
|
|
548
|
+
// This asserts that the live edge is being calculated from segment times.
|
|
549
|
+
// For VOD and event streams, this check should still pass.
|
|
550
|
+
goog.asserts.assert(
|
|
551
|
+
!this.presentationTimeline_.usingPresentationStartTime(),
|
|
552
|
+
'We should not be using the presentation start time in HLS!');
|
|
553
|
+
|
|
532
554
|
this.manifest_ = {
|
|
533
555
|
presentationTimeline: this.presentationTimeline_,
|
|
534
556
|
variants,
|
|
@@ -699,7 +721,8 @@ shaka.hls.HlsParser = class {
|
|
|
699
721
|
// Create variants for each variant tag.
|
|
700
722
|
const variantsPromises = tags.map(async (tag) => {
|
|
701
723
|
const frameRate = tag.getAttributeValue('FRAME-RATE');
|
|
702
|
-
const bandwidth = Number(tag.
|
|
724
|
+
const bandwidth = Number(tag.getAttributeValue('AVERAGE-BANDWIDTH')) ||
|
|
725
|
+
Number(tag.getRequiredAttrValue('BANDWIDTH'));
|
|
703
726
|
|
|
704
727
|
const resolution = tag.getAttributeValue('RESOLUTION');
|
|
705
728
|
const [width, height] = resolution ? resolution.split('x') : [null, null];
|
|
@@ -1150,7 +1173,7 @@ shaka.hls.HlsParser = class {
|
|
|
1150
1173
|
// "AUTOSELECT=YES". A value of "AUTOSELECT=NO" would imply that it may
|
|
1151
1174
|
// only be selected explicitly by the user, and we don't have a way to
|
|
1152
1175
|
// represent that in our model.
|
|
1153
|
-
const defaultAttrValue = tag.
|
|
1176
|
+
const defaultAttrValue = tag.getAttributeValue('DEFAULT');
|
|
1154
1177
|
const primary = defaultAttrValue == 'YES';
|
|
1155
1178
|
|
|
1156
1179
|
const channelsCount = type == 'audio' ? this.getChannelsCount_(tag) : null;
|
|
@@ -1537,12 +1560,6 @@ shaka.hls.HlsParser = class {
|
|
|
1537
1560
|
}
|
|
1538
1561
|
|
|
1539
1562
|
this.notifySegments_();
|
|
1540
|
-
|
|
1541
|
-
// This asserts that the live edge is being calculated from segment times.
|
|
1542
|
-
// For VOD and event streams, this check should still pass.
|
|
1543
|
-
goog.asserts.assert(
|
|
1544
|
-
!this.presentationTimeline_.usingPresentationStartTime(),
|
|
1545
|
-
'We should not be using the presentation start time in HLS!');
|
|
1546
1563
|
}
|
|
1547
1564
|
|
|
1548
1565
|
/**
|
|
@@ -1914,9 +1931,6 @@ shaka.hls.HlsParser = class {
|
|
|
1914
1931
|
}
|
|
1915
1932
|
}
|
|
1916
1933
|
|
|
1917
|
-
this.segmentsToNotifyByStream_.push(references);
|
|
1918
|
-
this.notifySegments_();
|
|
1919
|
-
|
|
1920
1934
|
return references;
|
|
1921
1935
|
}
|
|
1922
1936
|
|
|
@@ -2252,6 +2266,12 @@ shaka.hls.HlsParser = class {
|
|
|
2252
2266
|
reader.rewind(1);
|
|
2253
2267
|
};
|
|
2254
2268
|
|
|
2269
|
+
// We will look a few packet-lengths forward to find the first sync byte.
|
|
2270
|
+
// Note that we are using this method on what is already a subset of the
|
|
2271
|
+
// file (the first |shaka.hls.HlsParser.START_OF_SEGMENT_SIZE_| bytes), so
|
|
2272
|
+
// we can't look too far ahead to begin with.
|
|
2273
|
+
let syncByteScanLength = Math.min(reader.getLength() - 188, 5 * 188);
|
|
2274
|
+
|
|
2255
2275
|
// TODO: refactor this while loop for better readability.
|
|
2256
2276
|
// eslint-disable-next-line no-constant-condition
|
|
2257
2277
|
while (true) {
|
|
@@ -2260,8 +2280,16 @@ shaka.hls.HlsParser = class {
|
|
|
2260
2280
|
|
|
2261
2281
|
syncByte = reader.readUint8();
|
|
2262
2282
|
if (syncByte != 0x47) {
|
|
2283
|
+
if (syncByteScanLength > 0) {
|
|
2284
|
+
// This file could have started with a cut-off TS packet. Scan forward
|
|
2285
|
+
// until we find a sync byte.
|
|
2286
|
+
syncByteScanLength -= 1;
|
|
2287
|
+
continue;
|
|
2288
|
+
}
|
|
2263
2289
|
fail();
|
|
2264
2290
|
}
|
|
2291
|
+
// If we've found a sync byte, stop scanning forward for future packets.
|
|
2292
|
+
syncByteScanLength = 0;
|
|
2265
2293
|
|
|
2266
2294
|
const flagsAndPacketId = reader.readUint16();
|
|
2267
2295
|
const packetId = flagsAndPacketId & 0x1fff;
|
|
@@ -87,7 +87,9 @@ shaka.media.ClosedCaptionParser = class {
|
|
|
87
87
|
for (const captionPacket of captionPackets) {
|
|
88
88
|
const uint8ArrayData =
|
|
89
89
|
shaka.util.BufferUtils.toUint8(captionPacket.packet);
|
|
90
|
-
|
|
90
|
+
if (uint8ArrayData.length > 0) {
|
|
91
|
+
this.ceaDecoder_.extract(uint8ArrayData, captionPacket.pts);
|
|
92
|
+
}
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
// Decode and return the parsed captions.
|
|
@@ -75,6 +75,24 @@ shaka.media.ContentWorkarounds = class {
|
|
|
75
75
|
newType: ContentWorkarounds.BOX_TYPE_ENCV_,
|
|
76
76
|
});
|
|
77
77
|
})
|
|
78
|
+
.fullBox('avc3', (box) => {
|
|
79
|
+
boxesToModify.push({
|
|
80
|
+
box,
|
|
81
|
+
newType: ContentWorkarounds.BOX_TYPE_ENCV_,
|
|
82
|
+
});
|
|
83
|
+
})
|
|
84
|
+
.fullBox('ac-3', (box) => {
|
|
85
|
+
boxesToModify.push({
|
|
86
|
+
box,
|
|
87
|
+
newType: ContentWorkarounds.BOX_TYPE_ENCA_,
|
|
88
|
+
});
|
|
89
|
+
})
|
|
90
|
+
.fullBox('ec-3', (box) => {
|
|
91
|
+
boxesToModify.push({
|
|
92
|
+
box,
|
|
93
|
+
newType: ContentWorkarounds.BOX_TYPE_ENCA_,
|
|
94
|
+
});
|
|
95
|
+
})
|
|
78
96
|
.fullBox('mp4a', (box) => {
|
|
79
97
|
boxesToModify.push({
|
|
80
98
|
box,
|
package/lib/media/drm_engine.js
CHANGED
|
@@ -126,6 +126,9 @@ shaka.media.DrmEngine = class {
|
|
|
126
126
|
|
|
127
127
|
/** @const {!shaka.util.Destroyer} */
|
|
128
128
|
this.destroyer_ = new shaka.util.Destroyer(() => this.destroyNow_());
|
|
129
|
+
|
|
130
|
+
/** @private {boolean} */
|
|
131
|
+
this.srcEquals_ = false;
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
/** @override */
|
|
@@ -181,6 +184,7 @@ shaka.media.DrmEngine = class {
|
|
|
181
184
|
this.config_ = null;
|
|
182
185
|
this.onError_ = () => {};
|
|
183
186
|
this.playerInterface_ = null;
|
|
187
|
+
this.srcEquals_ = false;
|
|
184
188
|
}
|
|
185
189
|
|
|
186
190
|
/**
|
|
@@ -194,6 +198,13 @@ shaka.media.DrmEngine = class {
|
|
|
194
198
|
this.config_ = config;
|
|
195
199
|
}
|
|
196
200
|
|
|
201
|
+
/**
|
|
202
|
+
* @param {!boolean} value
|
|
203
|
+
*/
|
|
204
|
+
setSrcEquals(value) {
|
|
205
|
+
this.srcEquals_ = value;
|
|
206
|
+
}
|
|
207
|
+
|
|
197
208
|
/**
|
|
198
209
|
* Initialize the drm engine for storing and deleting stored content.
|
|
199
210
|
*
|
|
@@ -356,7 +367,7 @@ shaka.media.DrmEngine = class {
|
|
|
356
367
|
// in the drm infos, and before queryMediaKeys_().
|
|
357
368
|
if (useMediaCapabilities) {
|
|
358
369
|
await shaka.util.StreamUtils.getDecodingInfosForVariants(variants,
|
|
359
|
-
this.usePersistentLicenses_);
|
|
370
|
+
this.usePersistentLicenses_, this.srcEquals_);
|
|
360
371
|
} else {
|
|
361
372
|
configsByKeySystem =
|
|
362
373
|
this.prepareMediaKeyConfigsForVariants_(variants);
|
|
@@ -512,6 +523,7 @@ shaka.media.DrmEngine = class {
|
|
|
512
523
|
tasks.push(session.remove());
|
|
513
524
|
|
|
514
525
|
await Promise.all(tasks);
|
|
526
|
+
this.activeSessions_.delete(session);
|
|
515
527
|
}
|
|
516
528
|
|
|
517
529
|
/**
|
|
@@ -1877,9 +1889,16 @@ shaka.media.DrmEngine = class {
|
|
|
1877
1889
|
// audio adaptations, so we shouldn't have to worry about checking
|
|
1878
1890
|
// robustness.
|
|
1879
1891
|
if (drm1.keySystem == drm2.keySystem) {
|
|
1892
|
+
/** @type {Array<shaka.extern.InitDataOverride>} */
|
|
1880
1893
|
let initData = [];
|
|
1881
1894
|
initData = initData.concat(drm1.initData || []);
|
|
1882
1895
|
initData = initData.concat(drm2.initData || []);
|
|
1896
|
+
initData = initData.filter((d, i) => {
|
|
1897
|
+
return d.keyId === undefined || i === initData.findIndex((d2) => {
|
|
1898
|
+
return d2.keyId === d.keyId;
|
|
1899
|
+
});
|
|
1900
|
+
});
|
|
1901
|
+
|
|
1883
1902
|
const keyIds = drm1.keyIds && drm2.keyIds ?
|
|
1884
1903
|
new Set([...drm1.keyIds, ...drm2.keyIds]) :
|
|
1885
1904
|
drm1.keyIds || drm2.keyIds;
|
|
@@ -154,8 +154,10 @@ shaka.media.GapJumpingController = class {
|
|
|
154
154
|
}
|
|
155
155
|
// Don't gap jump while paused, so that you don't constantly jump ahead
|
|
156
156
|
// while paused on a livestream. We make an exception for time 0, since we
|
|
157
|
-
// may be _required_ to seek on startup before play can begin
|
|
158
|
-
|
|
157
|
+
// may be _required_ to seek on startup before play can begin, but only if
|
|
158
|
+
// autoplay is enabled.
|
|
159
|
+
if (this.video_.paused && (this.video_.currentTime != 0 ||
|
|
160
|
+
(!this.video_.autoplay && this.video_.currentTime == 0))) {
|
|
159
161
|
return;
|
|
160
162
|
}
|
|
161
163
|
|
|
@@ -215,8 +217,10 @@ shaka.media.GapJumpingController = class {
|
|
|
215
217
|
this.didFireLargeGap_ = true;
|
|
216
218
|
|
|
217
219
|
// Event firing is synchronous.
|
|
218
|
-
const
|
|
219
|
-
'
|
|
220
|
+
const data = new Map()
|
|
221
|
+
.set('currentTime', currentTime)
|
|
222
|
+
.set('gapSize', jumpSize);
|
|
223
|
+
const event = new shaka.util.FakeEvent('largegap', data);
|
|
220
224
|
event.cancelable = true;
|
|
221
225
|
this.onEvent_(event);
|
|
222
226
|
|
|
@@ -48,8 +48,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
48
48
|
* @param {!function(!Array.<shaka.extern.ID3Metadata>, number, ?number)=}
|
|
49
49
|
* onMetadata
|
|
50
50
|
*/
|
|
51
|
-
constructor(video, closedCaptionParser, textDisplayer,
|
|
52
|
-
onMetadata) {
|
|
51
|
+
constructor(video, closedCaptionParser, textDisplayer, onMetadata) {
|
|
53
52
|
/** @private {HTMLMediaElement} */
|
|
54
53
|
this.video_ = video;
|
|
55
54
|
|
package/lib/media/playhead.js
CHANGED
|
@@ -80,7 +80,7 @@ shaka.media.SrcEqualsPlayhead = class {
|
|
|
80
80
|
// We listen for the loaded-data-event so that we know when we can
|
|
81
81
|
// interact with |currentTime|.
|
|
82
82
|
const onLoaded = () => {
|
|
83
|
-
if (this.startTime_ == null) {
|
|
83
|
+
if (this.startTime_ == null || this.startTime_ == 0) {
|
|
84
84
|
this.started_ = true;
|
|
85
85
|
} else {
|
|
86
86
|
// Startup is complete only when the video element acknowledges the
|
|
@@ -361,8 +361,10 @@ shaka.media.PresentationTimeline = class {
|
|
|
361
361
|
*/
|
|
362
362
|
getSegmentAvailabilityEnd() {
|
|
363
363
|
if (!this.isLive() && !this.isInProgress()) {
|
|
364
|
-
|
|
364
|
+
// It's a static manifest (can also be a dynamic->static conversion)
|
|
365
|
+
return this.maxSegmentEndTime_ || this.duration_;
|
|
365
366
|
}
|
|
367
|
+
// Can be either live or "in-progress recording" (live with known duration)
|
|
366
368
|
return Math.min(this.getLiveEdge_() + this.availabilityTimeOffset_,
|
|
367
369
|
this.duration_);
|
|
368
370
|
}
|
|
@@ -181,7 +181,9 @@ shaka.media.SegmentIndex = class {
|
|
|
181
181
|
|
|
182
182
|
/**
|
|
183
183
|
* Merges the given SegmentReferences. Supports extending the original
|
|
184
|
-
* references only. Will
|
|
184
|
+
* references only. Will replace old references with equivalent new ones, and
|
|
185
|
+
* keep any unique old ones.
|
|
186
|
+
*
|
|
185
187
|
* Used, for example, by the DASH and HLS parser, where manifests may not list
|
|
186
188
|
* all available references, so we must keep available references in memory to
|
|
187
189
|
* fill the availability window.
|
|
@@ -189,6 +191,8 @@ shaka.media.SegmentIndex = class {
|
|
|
189
191
|
* @param {!Array.<!shaka.media.SegmentReference>} references The list of
|
|
190
192
|
* SegmentReferences, which must be sorted first by their start times
|
|
191
193
|
* (ascending) and second by their end times (ascending).
|
|
194
|
+
* @deprecated Not used directly by our own parsers, so will become private in
|
|
195
|
+
* v4. Use mergeAndEvict() instead.
|
|
192
196
|
* @export
|
|
193
197
|
*/
|
|
194
198
|
merge(references) {
|
|
@@ -199,13 +203,13 @@ shaka.media.SegmentIndex = class {
|
|
|
199
203
|
return;
|
|
200
204
|
}
|
|
201
205
|
|
|
202
|
-
// Partial segments are used for live edge, and should be removed when they
|
|
203
|
-
// get older. Remove the old SegmentReferences after the first new
|
|
204
|
-
// reference's start time.
|
|
205
206
|
if (!references.length) {
|
|
206
207
|
return;
|
|
207
208
|
}
|
|
208
209
|
|
|
210
|
+
// Partial segments are used for live edge, and should be removed when they
|
|
211
|
+
// get older. Remove the old SegmentReferences after the first new
|
|
212
|
+
// reference's start time.
|
|
209
213
|
this.references = this.references.filter((r) => {
|
|
210
214
|
return r.startTime < references[0].startTime;
|
|
211
215
|
});
|
|
@@ -233,13 +237,27 @@ shaka.media.SegmentIndex = class {
|
|
|
233
237
|
* @export
|
|
234
238
|
*/
|
|
235
239
|
mergeAndEvict(references, windowStart) {
|
|
236
|
-
//
|
|
240
|
+
// Filter out the references that are no longer available to avoid
|
|
237
241
|
// repeatedly evicting them and messing up eviction count.
|
|
238
242
|
references = references.filter((r) => {
|
|
239
|
-
return r.endTime > windowStart
|
|
243
|
+
return r.endTime > windowStart &&
|
|
244
|
+
(this.references.length == 0 ||
|
|
245
|
+
r.endTime > this.references[0].startTime);
|
|
240
246
|
});
|
|
241
247
|
|
|
248
|
+
const oldFirstRef = this.references[0];
|
|
242
249
|
this.merge(references);
|
|
250
|
+
const newFirstRef = this.references[0];
|
|
251
|
+
|
|
252
|
+
if (oldFirstRef) {
|
|
253
|
+
// We don't compare the actual ref, since the object could legitimately be
|
|
254
|
+
// replaced with an equivalent. Even the URIs could change due to
|
|
255
|
+
// load-balancing actions taken by the server. However, if the time
|
|
256
|
+
// changes, its not an equivalent reference.
|
|
257
|
+
goog.asserts.assert(oldFirstRef.startTime == newFirstRef.startTime,
|
|
258
|
+
'SegmentIndex.merge should not change the first reference time!');
|
|
259
|
+
}
|
|
260
|
+
|
|
243
261
|
this.evict(windowStart);
|
|
244
262
|
}
|
|
245
263
|
|
|
@@ -363,22 +381,25 @@ shaka.media.SegmentIndex = class {
|
|
|
363
381
|
|
|
364
382
|
/** @return {!shaka.media.SegmentIterator} */
|
|
365
383
|
[Symbol.iterator]() {
|
|
366
|
-
|
|
384
|
+
const iter = this.getIteratorForTime(0);
|
|
385
|
+
goog.asserts.assert(iter != null, 'Iterator for 0 should never be null!');
|
|
386
|
+
return iter;
|
|
367
387
|
}
|
|
368
388
|
|
|
369
389
|
/**
|
|
370
390
|
* Returns a new iterator that initially points to the segment that contains
|
|
371
391
|
* the given time. Like the normal iterator, next() must be called first to
|
|
372
|
-
* get to the first element.
|
|
392
|
+
* get to the first element. Returns null if we do not find a segment at the
|
|
393
|
+
* requested time.
|
|
373
394
|
*
|
|
374
395
|
* @param {number} time
|
|
375
|
-
* @return {
|
|
396
|
+
* @return {?shaka.media.SegmentIterator}
|
|
376
397
|
* @export
|
|
377
398
|
*/
|
|
378
399
|
getIteratorForTime(time) {
|
|
379
400
|
let index = this.find(time);
|
|
380
401
|
if (index == null) {
|
|
381
|
-
|
|
402
|
+
return null;
|
|
382
403
|
} else {
|
|
383
404
|
index--;
|
|
384
405
|
}
|
|
@@ -497,8 +518,13 @@ shaka.media.SegmentIterator = class {
|
|
|
497
518
|
'Please use SegmentIndex.getIteratorForTime instead of seek().');
|
|
498
519
|
|
|
499
520
|
const iter = this.segmentIndex_.getIteratorForTime(time);
|
|
500
|
-
|
|
501
|
-
|
|
521
|
+
if (iter) {
|
|
522
|
+
this.currentPosition_ = iter.currentPosition_;
|
|
523
|
+
this.currentPartialPosition_ = iter.currentPartialPosition_;
|
|
524
|
+
} else {
|
|
525
|
+
this.currentPosition_ = Number.MAX_VALUE;
|
|
526
|
+
this.currentPartialPosition_ = 0;
|
|
527
|
+
}
|
|
502
528
|
return this.next().value;
|
|
503
529
|
}
|
|
504
530
|
|
|
@@ -153,6 +153,14 @@ shaka.media.SegmentReference = class {
|
|
|
153
153
|
/** @type {number} */
|
|
154
154
|
this.endTime = endTime;
|
|
155
155
|
|
|
156
|
+
/**
|
|
157
|
+
* The "true" end time of the segment, without considering the period end
|
|
158
|
+
* time. This is necessary for thumbnail segments, where timing requires us
|
|
159
|
+
* to know the original segment duration as described in the manifest.
|
|
160
|
+
* @type {number}
|
|
161
|
+
*/
|
|
162
|
+
this.trueEndTime = endTime;
|
|
163
|
+
|
|
156
164
|
/** @type {function():!Array.<string>} */
|
|
157
165
|
this.getUrisInner = uris;
|
|
158
166
|
|
|
@@ -317,8 +317,10 @@ shaka.media.StreamingEngine = class {
|
|
|
317
317
|
* @param {shaka.extern.Variant} variant
|
|
318
318
|
* @param {boolean=} clearBuffer
|
|
319
319
|
* @param {number=} safeMargin
|
|
320
|
+
* @param {boolean=} force
|
|
321
|
+
* If true, reload the variant even if it did not change.
|
|
320
322
|
*/
|
|
321
|
-
switchVariant(variant, clearBuffer = false, safeMargin = 0) {
|
|
323
|
+
switchVariant(variant, clearBuffer = false, safeMargin = 0, force = false) {
|
|
322
324
|
this.currentVariant_ = variant;
|
|
323
325
|
|
|
324
326
|
if (!this.startupComplete_) {
|
|
@@ -329,12 +331,12 @@ shaka.media.StreamingEngine = class {
|
|
|
329
331
|
if (variant.video) {
|
|
330
332
|
this.switchInternal_(
|
|
331
333
|
variant.video, /* clearBuffer= */ clearBuffer,
|
|
332
|
-
/* safeMargin= */ safeMargin, /* force= */
|
|
334
|
+
/* safeMargin= */ safeMargin, /* force= */ force);
|
|
333
335
|
}
|
|
334
336
|
if (variant.audio) {
|
|
335
337
|
this.switchInternal_(
|
|
336
338
|
variant.audio, /* clearBuffer= */ clearBuffer,
|
|
337
|
-
/* safeMargin= */ safeMargin, /* force= */
|
|
339
|
+
/* safeMargin= */ safeMargin, /* force= */ force);
|
|
338
340
|
}
|
|
339
341
|
}
|
|
340
342
|
|
|
@@ -1113,7 +1115,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1113
1115
|
|
|
1114
1116
|
mediaState.segmentIterator =
|
|
1115
1117
|
mediaState.stream.segmentIndex.getIteratorForTime(time);
|
|
1116
|
-
const ref = mediaState.segmentIterator
|
|
1118
|
+
const ref = mediaState.segmentIterator &&
|
|
1119
|
+
mediaState.segmentIterator.next().value;
|
|
1117
1120
|
if (ref == null) {
|
|
1118
1121
|
shaka.log.warning(logPrefix, 'cannot find segment', 'endTime:', time);
|
|
1119
1122
|
}
|
|
@@ -1135,14 +1138,16 @@ shaka.media.StreamingEngine = class {
|
|
|
1135
1138
|
if (inaccurateTolerance) {
|
|
1136
1139
|
mediaState.segmentIterator =
|
|
1137
1140
|
mediaState.stream.segmentIndex.getIteratorForTime(lookupTime);
|
|
1138
|
-
ref = mediaState.segmentIterator
|
|
1141
|
+
ref = mediaState.segmentIterator &&
|
|
1142
|
+
mediaState.segmentIterator.next().value;
|
|
1139
1143
|
}
|
|
1140
1144
|
if (!ref) {
|
|
1141
1145
|
// If we can't find a valid segment with the drifted time, look for a
|
|
1142
1146
|
// segment with the presentation time.
|
|
1143
1147
|
mediaState.segmentIterator =
|
|
1144
1148
|
mediaState.stream.segmentIndex.getIteratorForTime(presentationTime);
|
|
1145
|
-
ref = mediaState.segmentIterator
|
|
1149
|
+
ref = mediaState.segmentIterator &&
|
|
1150
|
+
mediaState.segmentIterator.next().value;
|
|
1146
1151
|
}
|
|
1147
1152
|
if (ref == null) {
|
|
1148
1153
|
shaka.log.warning(logPrefix, 'cannot find segment',
|
|
@@ -1185,10 +1190,14 @@ shaka.media.StreamingEngine = class {
|
|
|
1185
1190
|
|
|
1186
1191
|
mediaState.performingUpdate = true;
|
|
1187
1192
|
|
|
1188
|
-
const initSourceBuffer = this.initSourceBuffer_(mediaState, reference);
|
|
1189
|
-
|
|
1190
|
-
shaka.log.v2(logPrefix, 'fetching segment');
|
|
1191
1193
|
try {
|
|
1194
|
+
await this.initSourceBuffer_(mediaState, reference);
|
|
1195
|
+
this.destroyer_.ensureNotDestroyed();
|
|
1196
|
+
if (this.fatalError_) {
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
shaka.log.v2(logPrefix, 'fetching segment');
|
|
1192
1201
|
const isMP4 = stream.mimeType == 'video/mp4' ||
|
|
1193
1202
|
stream.mimeType == 'audio/mp4';
|
|
1194
1203
|
const isReadableStreamSupported = window.ReadableStream;
|
|
@@ -1196,7 +1205,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1196
1205
|
if (this.config_.lowLatencyMode && isReadableStreamSupported && isMP4) {
|
|
1197
1206
|
let remaining = new Uint8Array(0);
|
|
1198
1207
|
const streamDataCallback = async (data) => {
|
|
1199
|
-
await initSourceBuffer;
|
|
1200
1208
|
this.destroyer_.ensureNotDestroyed();
|
|
1201
1209
|
if (this.fatalError_) {
|
|
1202
1210
|
return;
|
|
@@ -1234,7 +1242,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1234
1242
|
'ReadableStream is not supported by the browser.');
|
|
1235
1243
|
}
|
|
1236
1244
|
const fetchSegment = this.fetch_(mediaState, reference);
|
|
1237
|
-
const
|
|
1245
|
+
const result = await fetchSegment;
|
|
1238
1246
|
this.destroyer_.ensureNotDestroyed();
|
|
1239
1247
|
if (this.fatalError_) {
|
|
1240
1248
|
return;
|
|
@@ -1251,7 +1259,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1251
1259
|
return;
|
|
1252
1260
|
}
|
|
1253
1261
|
await this.append_(
|
|
1254
|
-
mediaState, presentationTime, stream, reference,
|
|
1262
|
+
mediaState, presentationTime, stream, reference, result);
|
|
1255
1263
|
}
|
|
1256
1264
|
|
|
1257
1265
|
this.destroyer_.ensureNotDestroyed();
|
|
@@ -1289,7 +1297,13 @@ shaka.media.StreamingEngine = class {
|
|
|
1289
1297
|
|
|
1290
1298
|
mediaState.performingUpdate = false;
|
|
1291
1299
|
|
|
1292
|
-
if (
|
|
1300
|
+
if (error.code == shaka.util.Error.Code.OPERATION_ABORTED) {
|
|
1301
|
+
// If the network slows down, abort the current fetch request and start
|
|
1302
|
+
// a new one, and ignore the error message.
|
|
1303
|
+
mediaState.performingUpdate = false;
|
|
1304
|
+
mediaState.updateTimer = null;
|
|
1305
|
+
this.scheduleUpdate_(mediaState, 0);
|
|
1306
|
+
} else if (mediaState.type == ContentType.TEXT &&
|
|
1293
1307
|
this.config_.ignoreTextStreamFailures) {
|
|
1294
1308
|
if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS) {
|
|
1295
1309
|
shaka.log.warning(logPrefix,
|
|
@@ -1299,12 +1313,6 @@ shaka.media.StreamingEngine = class {
|
|
|
1299
1313
|
'Text stream failed to parse. Proceeding without it.');
|
|
1300
1314
|
}
|
|
1301
1315
|
this.mediaStates_.delete(ContentType.TEXT);
|
|
1302
|
-
} else if (error.code == shaka.util.Error.Code.OPERATION_ABORTED) {
|
|
1303
|
-
// If the network slows down, abort the current fetch request and start
|
|
1304
|
-
// a new one, and ignore the error message.
|
|
1305
|
-
mediaState.performingUpdate = false;
|
|
1306
|
-
mediaState.updateTimer = null;
|
|
1307
|
-
this.scheduleUpdate_(mediaState, 0);
|
|
1308
1316
|
} else if (error.code == shaka.util.Error.Code.QUOTA_EXCEEDED_ERROR) {
|
|
1309
1317
|
this.handleQuotaExceeded_(mediaState, error);
|
|
1310
1318
|
} else if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS &&
|
|
@@ -1654,7 +1662,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1654
1662
|
|
|
1655
1663
|
// Dispatch an event to notify the application about the emsg box.
|
|
1656
1664
|
const eventName = shaka.Player.EventName.Emsg;
|
|
1657
|
-
const
|
|
1665
|
+
const data = (new Map()).set('detail', emsg);
|
|
1666
|
+
const event = new shaka.util.FakeEvent(eventName, data);
|
|
1658
1667
|
this.playerInterface_.onEvent(event);
|
|
1659
1668
|
}
|
|
1660
1669
|
}
|
|
@@ -1738,7 +1747,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1738
1747
|
* @param {!shaka.media.StreamingEngine.MediaState_} mediaState
|
|
1739
1748
|
* @param {(!shaka.media.InitSegmentReference|!shaka.media.SegmentReference)}
|
|
1740
1749
|
* reference
|
|
1741
|
-
* @param {?function(BufferSource)=} streamDataCallback
|
|
1750
|
+
* @param {?function(BufferSource):!Promise=} streamDataCallback
|
|
1742
1751
|
*
|
|
1743
1752
|
* @return {!Promise.<BufferSource>}
|
|
1744
1753
|
* @private
|
|
@@ -92,7 +92,7 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
92
92
|
* @param {!RequestInit} init
|
|
93
93
|
* @param {shaka.net.HttpFetchPlugin.AbortStatus} abortStatus
|
|
94
94
|
* @param {shaka.extern.ProgressUpdated} progressUpdated
|
|
95
|
-
* @param {?function(BufferSource)} streamDataCallback
|
|
95
|
+
* @param {?function(BufferSource):!Promise} streamDataCallback
|
|
96
96
|
* @return {!Promise<!shaka.extern.Response>}
|
|
97
97
|
* @private
|
|
98
98
|
*/
|
|
@@ -140,7 +140,7 @@ shaka.net.HttpFetchPlugin = class {
|
|
|
140
140
|
if (!readObj.done) {
|
|
141
141
|
loaded += readObj.value.byteLength;
|
|
142
142
|
if (streamDataCallback) {
|
|
143
|
-
streamDataCallback(readObj.value);
|
|
143
|
+
await streamDataCallback(readObj.value);
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
|
|
@@ -196,7 +196,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
196
196
|
*
|
|
197
197
|
* @param {!Array.<string>} uris
|
|
198
198
|
* @param {shaka.extern.RetryParameters} retryParams
|
|
199
|
-
* @param {?function(BufferSource)=} streamDataCallback
|
|
199
|
+
* @param {?function(BufferSource):!Promise=} streamDataCallback
|
|
200
200
|
* @return {shaka.extern.Request}
|
|
201
201
|
* @export
|
|
202
202
|
*/
|
|
@@ -544,7 +544,8 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
544
544
|
}
|
|
545
545
|
|
|
546
546
|
if (error.severity == shaka.util.Error.Severity.RECOVERABLE) {
|
|
547
|
-
const
|
|
547
|
+
const data = (new Map()).set('error', error);
|
|
548
|
+
const event = new shaka.util.FakeEvent('retry', data);
|
|
548
549
|
this.dispatchEvent(event);
|
|
549
550
|
|
|
550
551
|
// Move to the next URI.
|
package/lib/offline/storage.js
CHANGED
|
@@ -539,6 +539,10 @@ shaka.offline.Storage = class {
|
|
|
539
539
|
shaka.util.Error.Code.STORAGE_LIMIT_REACHED);
|
|
540
540
|
}
|
|
541
541
|
} catch (e) {
|
|
542
|
+
// It is necessary to be able to catch the STORAGE_LIMIT_REACHED error
|
|
543
|
+
if (e instanceof shaka.util.Error) {
|
|
544
|
+
throw e;
|
|
545
|
+
}
|
|
542
546
|
shaka.log.warning(
|
|
543
547
|
'downloadSizeCallback has produced an unexpected error', e);
|
|
544
548
|
throw new shaka.util.Error(
|