shaka-player 3.3.1 → 3.3.2
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 +73 -0
- package/.github/workflows/release.yaml +53 -0
- package/CHANGELOG.md +13 -1
- package/build/checkversion.py +1 -1
- package/build/test.py +6 -0
- package/dist/controls.css +1 -1
- package/dist/deps.js +4 -4
- package/dist/locales.js +29 -29
- package/dist/shaka-player.compiled.d.ts +2 -1
- package/dist/shaka-player.compiled.debug.d.ts +2 -1
- package/dist/shaka-player.compiled.debug.externs.js +17 -12
- package/dist/shaka-player.compiled.debug.js +93 -90
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +17 -12
- package/dist/shaka-player.compiled.js +687 -686
- package/dist/shaka-player.compiled.map +3 -3
- package/dist/shaka-player.ui.d.ts +4 -12
- package/dist/shaka-player.ui.debug.d.ts +4 -12
- package/dist/shaka-player.ui.debug.externs.js +19 -23
- package/dist/shaka-player.ui.debug.js +107 -105
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +19 -23
- package/dist/shaka-player.ui.js +952 -952
- package/dist/shaka-player.ui.map +2 -2
- package/karma.conf.js +19 -15
- package/lib/ads/ad_manager.js +1 -0
- package/lib/cast/cast_proxy.js +3 -0
- package/lib/cast/cast_receiver.js +3 -0
- package/lib/dash/dash_parser.js +31 -19
- package/lib/hls/manifest_text_parser.js +1 -0
- package/lib/media/quality_observer.js +17 -34
- package/lib/media/region_observer.js +39 -33
- package/lib/media/region_timeline.js +17 -19
- package/lib/media/streaming_engine.js +1 -1
- package/lib/net/networking_engine.js +4 -0
- package/lib/offline/storage.js +8 -9
- package/lib/player.js +32 -10
- package/lib/util/fake_event_target.js +21 -1
- package/package.json +3 -6
- package/test/dash/dash_parser_live_unit.js +43 -0
- package/test/dash/dash_parser_manifest_unit.js +2 -1
- package/test/hls/hls_parser_unit.js +74 -0
- package/test/media/quality_observer_unit.js +4 -1
- package/test/media/region_observer_unit.js +40 -5
- package/test/media/region_timeline_unit.js +40 -22
- package/test/test/util/util.js +10 -7
- package/test/ui/text_displayer_layout_unit.js +8 -7
- package/ui/controls.js +3 -0
- package/ui/localization.js +12 -36
package/karma.conf.js
CHANGED
|
@@ -11,6 +11,7 @@ const Jimp = require('jimp');
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const rimraf = require('rimraf');
|
|
14
|
+
const {ssim} = require('ssim.js');
|
|
14
15
|
const util = require('karma/common/util');
|
|
15
16
|
const which = require('which');
|
|
16
17
|
|
|
@@ -336,6 +337,10 @@ module.exports = (config) => {
|
|
|
336
337
|
|
|
337
338
|
config.set({reporters: reporters});
|
|
338
339
|
|
|
340
|
+
if (reporters.includes('spec') && settings.spec_hide_passed) {
|
|
341
|
+
config.set({specReporter: {suppressPassed: true}});
|
|
342
|
+
}
|
|
343
|
+
|
|
339
344
|
if (settings.random) {
|
|
340
345
|
// If --seed was specified use that value, else generate a seed so that the
|
|
341
346
|
// exact order can be reproduced if it catches an issue.
|
|
@@ -518,8 +523,7 @@ function WebDriverScreenshotMiddlewareFactory(launcher) {
|
|
|
518
523
|
*
|
|
519
524
|
* @param {karma.Launcher.Browser} browser
|
|
520
525
|
* @param {!Object.<string, string>} params
|
|
521
|
-
* @return {!Promise.<number>}
|
|
522
|
-
* and new screenshots, after cropping and scaling.
|
|
526
|
+
* @return {!Promise.<number>} A similarity score between 0 and 1.
|
|
523
527
|
*/
|
|
524
528
|
async function diffScreenshot(browser, params) {
|
|
525
529
|
const webDriverClient = getWebDriverClient(browser);
|
|
@@ -604,22 +608,22 @@ function WebDriverScreenshotMiddlewareFactory(launcher) {
|
|
|
604
608
|
// Compare the new screenshot to the old one and produce a diff image.
|
|
605
609
|
// Initially, the image data will be raw pixels, 4 bytes per pixel.
|
|
606
610
|
// The threshold parameter affects the sensitivity of individual pixel
|
|
607
|
-
// comparisons.
|
|
608
|
-
//
|
|
609
|
-
//
|
|
610
|
-
|
|
611
|
-
const diff = Jimp.diff(oldScreenshot, newScreenshot,
|
|
611
|
+
// comparisons. This diff is only used for visual review, not for
|
|
612
|
+
// automated similarity checks, so the threshold setting is not so critical
|
|
613
|
+
// as it used to be.
|
|
614
|
+
const threshold = 0.10;
|
|
615
|
+
const diff = Jimp.diff(oldScreenshot, newScreenshot, threshold);
|
|
612
616
|
|
|
613
617
|
// Write the diff to disk. This is used to review when there are changes.
|
|
618
|
+
const fullSizeDiff =
|
|
619
|
+
diff.image.clone().resize(width, height, Jimp.RESIZE_BICUBIC);
|
|
614
620
|
fs.writeFileSync(
|
|
615
|
-
diffScreenshotPath, await
|
|
616
|
-
|
|
617
|
-
//
|
|
618
|
-
//
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
diff.percent * diff.image.bitmap.width * diff.image.bitmap.height;
|
|
622
|
-
return pixelsChanged;
|
|
621
|
+
diffScreenshotPath, await fullSizeDiff.getBufferAsync('image/png'));
|
|
622
|
+
|
|
623
|
+
// Compare with a structural similarity algorithm. This produces a
|
|
624
|
+
// similarity score that we will use to pass or fail the test.
|
|
625
|
+
const ssimResult = ssim(oldScreenshot.bitmap, newScreenshot.bitmap);
|
|
626
|
+
return ssimResult.mssim; // A score between 0 and 1.
|
|
623
627
|
}
|
|
624
628
|
|
|
625
629
|
/**
|
package/lib/ads/ad_manager.js
CHANGED
package/lib/cast/cast_proxy.js
CHANGED
|
@@ -238,6 +238,9 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
|
|
|
238
238
|
this.shakaBus_ = null;
|
|
239
239
|
this.genericBus_ = null;
|
|
240
240
|
|
|
241
|
+
// FakeEventTarget implements IReleasable
|
|
242
|
+
super.release();
|
|
243
|
+
|
|
241
244
|
await Promise.all(waitFor);
|
|
242
245
|
|
|
243
246
|
const manager = cast.receiver.CastReceiverManager.getInstance();
|
package/lib/dash/dash_parser.js
CHANGED
|
@@ -410,6 +410,24 @@ shaka.dash.DashParser = class {
|
|
|
410
410
|
this.config_.dash.autoCorrectDrift);
|
|
411
411
|
}
|
|
412
412
|
|
|
413
|
+
presentationTimeline.setStatic(mpdType == 'static');
|
|
414
|
+
|
|
415
|
+
const isLive = presentationTimeline.isLive();
|
|
416
|
+
|
|
417
|
+
// If it's live, we check for an override.
|
|
418
|
+
if (isLive && !isNaN(this.config_.availabilityWindowOverride)) {
|
|
419
|
+
segmentAvailabilityDuration = this.config_.availabilityWindowOverride;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// If it's null, that means segments are always available. This is always
|
|
423
|
+
// the case for VOD, and sometimes the case for live.
|
|
424
|
+
if (segmentAvailabilityDuration == null) {
|
|
425
|
+
segmentAvailabilityDuration = Infinity;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
presentationTimeline.setSegmentAvailabilityDuration(
|
|
429
|
+
segmentAvailabilityDuration);
|
|
430
|
+
|
|
413
431
|
const profiles = mpd.getAttribute('profiles') || '';
|
|
414
432
|
|
|
415
433
|
/** @type {shaka.dash.DashParser.Context} */
|
|
@@ -432,7 +450,6 @@ shaka.dash.DashParser = class {
|
|
|
432
450
|
const duration = periodsAndDuration.duration;
|
|
433
451
|
const periods = periodsAndDuration.periods;
|
|
434
452
|
|
|
435
|
-
presentationTimeline.setStatic(mpdType == 'static');
|
|
436
453
|
if (mpdType == 'static' ||
|
|
437
454
|
!periodsAndDuration.durationDerivedFromPeriods) {
|
|
438
455
|
// Ignore duration calculated from Period lengths if this is dynamic.
|
|
@@ -451,6 +468,7 @@ shaka.dash.DashParser = class {
|
|
|
451
468
|
this.lowLatencyMode_ = this.playerInterface_.isLowLatencyMode();
|
|
452
469
|
}
|
|
453
470
|
}
|
|
471
|
+
|
|
454
472
|
if (this.lowLatencyMode_) {
|
|
455
473
|
presentationTimeline.setAvailabilityTimeOffset(
|
|
456
474
|
this.minTotalAvailabilityTimeOffset_);
|
|
@@ -464,22 +482,6 @@ shaka.dash.DashParser = class {
|
|
|
464
482
|
'https://bit.ly/3clctcj for details.');
|
|
465
483
|
}
|
|
466
484
|
|
|
467
|
-
const isLive = presentationTimeline.isLive();
|
|
468
|
-
|
|
469
|
-
// If it's live, we check for an override.
|
|
470
|
-
if (isLive && !isNaN(this.config_.availabilityWindowOverride)) {
|
|
471
|
-
segmentAvailabilityDuration = this.config_.availabilityWindowOverride;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// If it's null, that means segments are always available. This is always
|
|
475
|
-
// the case for VOD, and sometimes the case for live.
|
|
476
|
-
if (segmentAvailabilityDuration == null) {
|
|
477
|
-
segmentAvailabilityDuration = Infinity;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
presentationTimeline.setSegmentAvailabilityDuration(
|
|
481
|
-
segmentAvailabilityDuration);
|
|
482
|
-
|
|
483
485
|
// Use @maxSegmentDuration to override smaller, derived values.
|
|
484
486
|
presentationTimeline.notifyMaxSegmentDuration(maxSegmentDuration || 1);
|
|
485
487
|
if (goog.DEBUG) {
|
|
@@ -714,8 +716,12 @@ shaka.dash.DashParser = class {
|
|
|
714
716
|
|
|
715
717
|
const eventStreamNodes =
|
|
716
718
|
XmlUtils.findChildren(periodInfo.node, 'EventStream');
|
|
719
|
+
const availabilityStart =
|
|
720
|
+
context.presentationTimeline.getSegmentAvailabilityStart();
|
|
721
|
+
|
|
717
722
|
for (const node of eventStreamNodes) {
|
|
718
|
-
this.parseEventStream_(
|
|
723
|
+
this.parseEventStream_(
|
|
724
|
+
periodInfo.start, periodInfo.duration, node, availabilityStart);
|
|
719
725
|
}
|
|
720
726
|
|
|
721
727
|
const adaptationSetNodes =
|
|
@@ -1769,9 +1775,10 @@ shaka.dash.DashParser = class {
|
|
|
1769
1775
|
* @param {number} periodStart
|
|
1770
1776
|
* @param {?number} periodDuration
|
|
1771
1777
|
* @param {!Element} elem
|
|
1778
|
+
* @param {number} availabilityStart
|
|
1772
1779
|
* @private
|
|
1773
1780
|
*/
|
|
1774
|
-
parseEventStream_(periodStart, periodDuration, elem) {
|
|
1781
|
+
parseEventStream_(periodStart, periodDuration, elem, availabilityStart) {
|
|
1775
1782
|
const XmlUtils = shaka.util.XmlUtils;
|
|
1776
1783
|
const parseNumber = XmlUtils.parseNonNegativeInt;
|
|
1777
1784
|
|
|
@@ -1794,6 +1801,11 @@ shaka.dash.DashParser = class {
|
|
|
1794
1801
|
endTime = Math.min(endTime, periodStart + periodDuration);
|
|
1795
1802
|
}
|
|
1796
1803
|
|
|
1804
|
+
// Don't add unavailable regions to the timeline.
|
|
1805
|
+
if (endTime < availabilityStart) {
|
|
1806
|
+
continue;
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1797
1809
|
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
1798
1810
|
const region = {
|
|
1799
1811
|
schemeIdUri: schemeIdUri,
|
|
@@ -8,48 +8,40 @@ goog.provide('shaka.media.QualityObserver');
|
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.media.IPlayheadObserver');
|
|
10
10
|
goog.require('shaka.log');
|
|
11
|
+
goog.require('shaka.util.FakeEvent');
|
|
12
|
+
goog.require('shaka.util.FakeEventTarget');
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
|
-
* Monitors the quality of content being appended to the source
|
|
14
|
-
*
|
|
15
|
-
* at the playhead changes.
|
|
15
|
+
* Monitors the quality of content being appended to the source buffers and
|
|
16
|
+
* fires 'qualitychange' events when the media quality at the playhead changes.
|
|
16
17
|
*
|
|
17
18
|
* @implements {shaka.media.IPlayheadObserver}
|
|
18
19
|
* @final
|
|
19
20
|
*/
|
|
20
|
-
shaka.media.QualityObserver = class {
|
|
21
|
+
shaka.media.QualityObserver = class extends shaka.util.FakeEventTarget {
|
|
21
22
|
/**
|
|
22
23
|
* Creates a new QualityObserver.
|
|
23
24
|
*
|
|
24
25
|
* @param {!function():!shaka.extern.BufferedInfo} getBufferedInfo
|
|
25
|
-
*
|
|
26
|
-
*
|
|
26
|
+
* Buffered info is needed to purge QualityChanges that are no
|
|
27
|
+
* longer relevant.
|
|
27
28
|
*/
|
|
28
29
|
constructor(getBufferedInfo) {
|
|
30
|
+
super();
|
|
31
|
+
|
|
29
32
|
/**
|
|
30
33
|
* @private {!Map.<string,!shaka.media.QualityObserver.ContentTypeState>}
|
|
31
34
|
*/
|
|
32
35
|
this.contentTypeStates_ = new Map();
|
|
33
36
|
|
|
34
|
-
/** @private {shaka.media.QualityObserver.EventListener} */
|
|
35
|
-
this.onQualityChange_ = (mediaQuality, position) => {};
|
|
36
|
-
|
|
37
37
|
/** @private function():!shaka.extern.BufferedInfo */
|
|
38
38
|
this.getBufferedInfo_ = getBufferedInfo;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
/**
|
|
42
|
-
* Set all the listeners. This overrides any previous calls to |setListeners|.
|
|
43
|
-
*
|
|
44
|
-
* @param {shaka.media.QualityObserver.EventListener} onQualityChange
|
|
45
|
-
*/
|
|
46
|
-
setListeners(onQualityChange) {
|
|
47
|
-
this.onQualityChange_ = onQualityChange;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
41
|
/** @override */
|
|
51
42
|
release() {
|
|
52
43
|
this.contentTypeStates_.clear();
|
|
44
|
+
super.release();
|
|
53
45
|
}
|
|
54
46
|
|
|
55
47
|
/**
|
|
@@ -169,10 +161,15 @@ shaka.media.QualityObserver = class {
|
|
|
169
161
|
if (this.positionIsBuffered_(
|
|
170
162
|
positionInSeconds, qualityAtPosition.contentType)) {
|
|
171
163
|
contentTypeState.currentQuality = qualityAtPosition;
|
|
164
|
+
|
|
172
165
|
shaka.log.debug('Media quality changed at position ' +
|
|
173
166
|
positionInSeconds + ' ' + JSON.stringify(qualityAtPosition));
|
|
174
|
-
|
|
175
|
-
|
|
167
|
+
|
|
168
|
+
const event = new shaka.util.FakeEvent('qualitychange', new Map([
|
|
169
|
+
['quality', qualityAtPosition],
|
|
170
|
+
['position', positionInSeconds],
|
|
171
|
+
]));
|
|
172
|
+
this.dispatchEvent(event);
|
|
176
173
|
}
|
|
177
174
|
}
|
|
178
175
|
}
|
|
@@ -237,20 +234,6 @@ shaka.media.QualityObserver = class {
|
|
|
237
234
|
}
|
|
238
235
|
};
|
|
239
236
|
|
|
240
|
-
/**
|
|
241
|
-
* @typedef {function(shaka.extern.MediaQualityInfo, number)}
|
|
242
|
-
*
|
|
243
|
-
* @description
|
|
244
|
-
* A callback function used to notify the player when media quality changes
|
|
245
|
-
* are detected at the playhead.
|
|
246
|
-
*
|
|
247
|
-
* The first argument is information about media quality at the playhead
|
|
248
|
-
* position.
|
|
249
|
-
*
|
|
250
|
-
* The second argument is the playhead position in seconds.
|
|
251
|
-
*/
|
|
252
|
-
shaka.media.QualityObserver.EventListener;
|
|
253
|
-
|
|
254
237
|
/**
|
|
255
238
|
* @typedef {{
|
|
256
239
|
* mediaQuality: !shaka.extern.MediaQualityInfo,
|
|
@@ -8,16 +8,19 @@ goog.provide('shaka.media.RegionObserver');
|
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.media.IPlayheadObserver');
|
|
10
10
|
goog.require('shaka.media.RegionTimeline');
|
|
11
|
+
goog.require('shaka.util.EventManager');
|
|
12
|
+
goog.require('shaka.util.FakeEvent');
|
|
13
|
+
goog.require('shaka.util.FakeEventTarget');
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
/**
|
|
14
17
|
* The region observer watches a region timeline and playhead, and fires events
|
|
15
|
-
* (
|
|
18
|
+
* ('enter', 'exit', 'skip') as the playhead moves.
|
|
16
19
|
*
|
|
17
20
|
* @implements {shaka.media.IPlayheadObserver}
|
|
18
21
|
* @final
|
|
19
22
|
*/
|
|
20
|
-
shaka.media.RegionObserver = class {
|
|
23
|
+
shaka.media.RegionObserver = class extends shaka.util.FakeEventTarget {
|
|
21
24
|
/**
|
|
22
25
|
* Create a region observer for the given timeline. The observer does not
|
|
23
26
|
* own the timeline, only uses it. This means that the observer should NOT
|
|
@@ -26,6 +29,8 @@ shaka.media.RegionObserver = class {
|
|
|
26
29
|
* @param {!shaka.media.RegionTimeline} timeline
|
|
27
30
|
*/
|
|
28
31
|
constructor(timeline) {
|
|
32
|
+
super();
|
|
33
|
+
|
|
29
34
|
/** @private {shaka.media.RegionTimeline} */
|
|
30
35
|
this.timeline_ = timeline;
|
|
31
36
|
|
|
@@ -39,13 +44,6 @@ shaka.media.RegionObserver = class {
|
|
|
39
44
|
*/
|
|
40
45
|
this.oldPosition_ = new Map();
|
|
41
46
|
|
|
42
|
-
/** @private {shaka.media.RegionObserver.EventListener} */
|
|
43
|
-
this.onEnter_ = (region, seeking) => {};
|
|
44
|
-
/** @private {shaka.media.RegionObserver.EventListener} */
|
|
45
|
-
this.onExit_ = (region, seeking) => {};
|
|
46
|
-
/** @private {shaka.media.RegionObserver.EventListener} */
|
|
47
|
-
this.onSkip_ = (region, seeking) => {};
|
|
48
|
-
|
|
49
47
|
// To make the rules easier to read, alias all the relative positions.
|
|
50
48
|
const RelativePosition = shaka.media.RegionObserver.RelativePosition_;
|
|
51
49
|
const BEFORE_THE_REGION = RelativePosition.BEFORE_THE_REGION;
|
|
@@ -62,39 +60,48 @@ shaka.media.RegionObserver = class {
|
|
|
62
60
|
{
|
|
63
61
|
weWere: null,
|
|
64
62
|
weAre: IN_THE_REGION,
|
|
65
|
-
invoke: (region, seeking) => this.
|
|
63
|
+
invoke: (region, seeking) => this.onEvent_('enter', region, seeking),
|
|
66
64
|
},
|
|
67
65
|
{
|
|
68
66
|
weWere: BEFORE_THE_REGION,
|
|
69
67
|
weAre: IN_THE_REGION,
|
|
70
|
-
invoke: (region, seeking) => this.
|
|
68
|
+
invoke: (region, seeking) => this.onEvent_('enter', region, seeking),
|
|
71
69
|
},
|
|
72
70
|
{
|
|
73
71
|
weWere: AFTER_THE_REGION,
|
|
74
72
|
weAre: IN_THE_REGION,
|
|
75
|
-
invoke: (region, seeking) => this.
|
|
73
|
+
invoke: (region, seeking) => this.onEvent_('enter', region, seeking),
|
|
76
74
|
},
|
|
77
75
|
{
|
|
78
76
|
weWere: IN_THE_REGION,
|
|
79
77
|
weAre: BEFORE_THE_REGION,
|
|
80
|
-
invoke: (region, seeking) => this.
|
|
78
|
+
invoke: (region, seeking) => this.onEvent_('exit', region, seeking),
|
|
81
79
|
},
|
|
82
80
|
{
|
|
83
81
|
weWere: IN_THE_REGION,
|
|
84
82
|
weAre: AFTER_THE_REGION,
|
|
85
|
-
invoke: (region, seeking) => this.
|
|
83
|
+
invoke: (region, seeking) => this.onEvent_('exit', region, seeking),
|
|
86
84
|
},
|
|
87
85
|
{
|
|
88
86
|
weWere: BEFORE_THE_REGION,
|
|
89
87
|
weAre: AFTER_THE_REGION,
|
|
90
|
-
invoke: (region, seeking) => this.
|
|
88
|
+
invoke: (region, seeking) => this.onEvent_('skip', region, seeking),
|
|
91
89
|
},
|
|
92
90
|
{
|
|
93
91
|
weWere: AFTER_THE_REGION,
|
|
94
92
|
weAre: BEFORE_THE_REGION,
|
|
95
|
-
invoke: (region, seeking) => this.
|
|
93
|
+
invoke: (region, seeking) => this.onEvent_('skip', region, seeking),
|
|
96
94
|
},
|
|
97
95
|
];
|
|
96
|
+
|
|
97
|
+
/** @private {shaka.util.EventManager} */
|
|
98
|
+
this.eventManager_ = new shaka.util.EventManager();
|
|
99
|
+
|
|
100
|
+
this.eventManager_.listen(this.timeline_, 'regionremove', (event) => {
|
|
101
|
+
/** @type {shaka.extern.TimelineRegionInfo} */
|
|
102
|
+
const region = event['region'];
|
|
103
|
+
this.oldPosition_.delete(region);
|
|
104
|
+
});
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
/** @override */
|
|
@@ -105,11 +112,10 @@ shaka.media.RegionObserver = class {
|
|
|
105
112
|
// needed.
|
|
106
113
|
this.oldPosition_.clear();
|
|
107
114
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
this.onSkip_ = (region, seeking) => {};
|
|
115
|
+
this.eventManager_.release();
|
|
116
|
+
this.eventManager_ = null;
|
|
117
|
+
|
|
118
|
+
super.release();
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
/** @override */
|
|
@@ -134,20 +140,20 @@ shaka.media.RegionObserver = class {
|
|
|
134
140
|
}
|
|
135
141
|
|
|
136
142
|
/**
|
|
137
|
-
*
|
|
143
|
+
* Dispatch events of the given type. All event types in this class have the
|
|
144
|
+
* same parameters: region and seeking.
|
|
138
145
|
*
|
|
139
|
-
* @param {
|
|
140
|
-
*
|
|
141
|
-
* @param {
|
|
142
|
-
*
|
|
143
|
-
* @param {shaka.media.RegionObserver.EventListener} onSkip
|
|
144
|
-
* The callback for when we move from before to after a region or from
|
|
145
|
-
* after to before a region.
|
|
146
|
+
* @param {string} eventType
|
|
147
|
+
* @param {shaka.extern.TimelineRegionInfo} region
|
|
148
|
+
* @param {boolean} seeking
|
|
149
|
+
* @private
|
|
146
150
|
*/
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
+
onEvent_(eventType, region, seeking) {
|
|
152
|
+
const event = new shaka.util.FakeEvent(eventType, new Map([
|
|
153
|
+
['region', region],
|
|
154
|
+
['seeking', seeking],
|
|
155
|
+
]));
|
|
156
|
+
this.dispatchEvent(event);
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
/**
|
|
@@ -6,26 +6,30 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.media.RegionTimeline');
|
|
8
8
|
|
|
9
|
+
goog.require('shaka.util.FakeEvent');
|
|
10
|
+
goog.require('shaka.util.FakeEventTarget');
|
|
9
11
|
goog.require('shaka.util.IReleasable');
|
|
10
12
|
goog.require('shaka.util.Timer');
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* The region timeline is a set of unique timeline region info entries. When
|
|
15
|
-
* a new entry is added, the
|
|
17
|
+
* a new entry is added, the 'regionadd' event will be fired. When an entry is
|
|
18
|
+
* deleted, the 'regionremove' event will be fired.
|
|
16
19
|
*
|
|
17
20
|
* @implements {shaka.util.IReleasable}
|
|
18
21
|
* @final
|
|
19
22
|
*/
|
|
20
|
-
shaka.media.RegionTimeline = class {
|
|
23
|
+
shaka.media.RegionTimeline = class extends shaka.util.FakeEventTarget {
|
|
21
24
|
/**
|
|
22
25
|
* @param {!function():{start: number, end: number}} getSeekRange
|
|
23
26
|
*/
|
|
24
27
|
constructor(getSeekRange) {
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
super();
|
|
29
|
+
|
|
27
30
|
/** @private {!Set.<shaka.extern.TimelineRegionInfo>} */
|
|
28
31
|
this.regions_ = new Set();
|
|
32
|
+
|
|
29
33
|
/** @private {!function():{start: number, end: number}} */
|
|
30
34
|
this.getSeekRange_ = getSeekRange;
|
|
31
35
|
|
|
@@ -44,22 +48,9 @@ shaka.media.RegionTimeline = class {
|
|
|
44
48
|
|
|
45
49
|
/** @override */
|
|
46
50
|
release() {
|
|
47
|
-
// Prevent us from holding onto any external references via the callback.
|
|
48
|
-
this.onAddRegion_ = (region) => {};
|
|
49
51
|
this.regions_.clear();
|
|
50
52
|
this.filterTimer_.stop();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Set the callbacks for events. This will override any previous calls to
|
|
55
|
-
* |setListeners|.
|
|
56
|
-
*
|
|
57
|
-
* @param {function(shaka.extern.TimelineRegionInfo)} onAddRegion
|
|
58
|
-
* Set the callback for when we add a new region. This callback will only
|
|
59
|
-
* be called when a region is unique (we reject duplicate regions).
|
|
60
|
-
*/
|
|
61
|
-
setListeners(onAddRegion) {
|
|
62
|
-
this.onAddRegion_ = onAddRegion;
|
|
53
|
+
super.release();
|
|
63
54
|
}
|
|
64
55
|
|
|
65
56
|
/**
|
|
@@ -72,7 +63,10 @@ shaka.media.RegionTimeline = class {
|
|
|
72
63
|
// instead of making the parser track it.
|
|
73
64
|
if (similarRegion == null) {
|
|
74
65
|
this.regions_.add(region);
|
|
75
|
-
|
|
66
|
+
const event = new shaka.util.FakeEvent('regionadd', new Map([
|
|
67
|
+
['region', region],
|
|
68
|
+
]));
|
|
69
|
+
this.dispatchEvent(event);
|
|
76
70
|
}
|
|
77
71
|
}
|
|
78
72
|
|
|
@@ -89,6 +83,10 @@ shaka.media.RegionTimeline = class {
|
|
|
89
83
|
// reson to store or process them.
|
|
90
84
|
if (region.endTime < seekRange.start) {
|
|
91
85
|
this.regions_.delete(region);
|
|
86
|
+
const event = new shaka.util.FakeEvent('regionremove', new Map([
|
|
87
|
+
['region', region],
|
|
88
|
+
]));
|
|
89
|
+
this.dispatchEvent(event);
|
|
92
90
|
}
|
|
93
91
|
}
|
|
94
92
|
}
|
|
@@ -1659,7 +1659,7 @@ shaka.media.StreamingEngine = class {
|
|
|
1659
1659
|
// See DASH sec. 5.10.3.3.1
|
|
1660
1660
|
// If a DASH client detects an event message box with a scheme that is not
|
|
1661
1661
|
// defined in MPD, the client is expected to ignore it.
|
|
1662
|
-
if (emsgSchemeIdUris.includes(schemeId) ||
|
|
1662
|
+
if ((emsgSchemeIdUris && emsgSchemeIdUris.includes(schemeId)) ||
|
|
1663
1663
|
this.config_.dispatchAllEmsgBoxes) {
|
|
1664
1664
|
// See DASH sec. 5.10.4.1
|
|
1665
1665
|
// A special scheme in DASH used to signal manifest updates.
|
|
@@ -232,6 +232,10 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
232
232
|
this.destroyed_ = true;
|
|
233
233
|
this.requestFilters_.clear();
|
|
234
234
|
this.responseFilters_.clear();
|
|
235
|
+
|
|
236
|
+
// FakeEventTarget implements IReleasable
|
|
237
|
+
super.release();
|
|
238
|
+
|
|
235
239
|
return this.operationManager_.destroy();
|
|
236
240
|
}
|
|
237
241
|
|
package/lib/offline/storage.js
CHANGED
|
@@ -681,16 +681,15 @@ shaka.offline.Storage = class {
|
|
|
681
681
|
* @private
|
|
682
682
|
*/
|
|
683
683
|
async filterManifest_(manifest, drmEngine, config) {
|
|
684
|
-
const StreamUtils = shaka.util.StreamUtils;
|
|
685
|
-
|
|
686
684
|
// Filter the manifest based on the restrictions given in the player
|
|
687
685
|
// configuration.
|
|
688
686
|
const maxHwRes = {width: Infinity, height: Infinity};
|
|
689
|
-
StreamUtils.filterByRestrictions(
|
|
687
|
+
shaka.util.StreamUtils.filterByRestrictions(
|
|
688
|
+
manifest, config.restrictions, maxHwRes);
|
|
690
689
|
|
|
691
690
|
// Filter the manifest based on what we know MediaCapabilities will be able
|
|
692
691
|
// to play later (no point storing something we can't play).
|
|
693
|
-
StreamUtils.filterManifestByMediaCapabilities(
|
|
692
|
+
await shaka.util.StreamUtils.filterManifestByMediaCapabilities(
|
|
694
693
|
manifest, config.offline.usePersistentLicense);
|
|
695
694
|
|
|
696
695
|
// Gather all tracks.
|
|
@@ -702,24 +701,24 @@ shaka.offline.Storage = class {
|
|
|
702
701
|
const preferredVideoCodecs = config.preferredVideoCodecs;
|
|
703
702
|
const preferredAudioCodecs = config.preferredAudioCodecs;
|
|
704
703
|
|
|
705
|
-
StreamUtils.chooseCodecsAndFilterManifest(
|
|
704
|
+
shaka.util.StreamUtils.chooseCodecsAndFilterManifest(
|
|
706
705
|
manifest, preferredVideoCodecs, preferredAudioCodecs,
|
|
707
706
|
preferredAudioChannelCount, preferredDecodingAttributes);
|
|
708
707
|
|
|
709
708
|
for (const variant of manifest.variants) {
|
|
710
709
|
goog.asserts.assert(
|
|
711
|
-
StreamUtils.isPlayable(variant),
|
|
710
|
+
shaka.util.StreamUtils.isPlayable(variant),
|
|
712
711
|
'We should have already filtered by "is playable"');
|
|
713
712
|
|
|
714
|
-
allTracks.push(StreamUtils.variantToTrack(variant));
|
|
713
|
+
allTracks.push(shaka.util.StreamUtils.variantToTrack(variant));
|
|
715
714
|
}
|
|
716
715
|
|
|
717
716
|
for (const text of manifest.textStreams) {
|
|
718
|
-
allTracks.push(StreamUtils.textStreamToTrack(text));
|
|
717
|
+
allTracks.push(shaka.util.StreamUtils.textStreamToTrack(text));
|
|
719
718
|
}
|
|
720
719
|
|
|
721
720
|
for (const image of manifest.imageStreams) {
|
|
722
|
-
allTracks.push(StreamUtils.imageStreamToTrack(image));
|
|
721
|
+
allTracks.push(shaka.util.StreamUtils.imageStreamToTrack(image));
|
|
723
722
|
}
|
|
724
723
|
|
|
725
724
|
// Let the application choose which tracks to store.
|