shaka-player 4.9.13 → 4.10.1
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/README.md +4 -0
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/deps.js +24 -19
- package/dist/locales.js +46 -35
- package/dist/shaka-player.compiled.d.ts +291 -12
- package/dist/shaka-player.compiled.debug.d.ts +291 -12
- package/dist/shaka-player.compiled.debug.externs.js +443 -6
- package/dist/shaka-player.compiled.debug.js +651 -556
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +443 -6
- package/dist/shaka-player.compiled.js +1325 -1270
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +306 -13
- package/dist/shaka-player.ui.debug.d.ts +306 -13
- package/dist/shaka-player.ui.debug.externs.js +477 -18
- package/dist/shaka-player.ui.debug.js +799 -681
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +477 -18
- package/dist/shaka-player.ui.js +1600 -1533
- package/dist/shaka-player.ui.map +4 -4
- package/externs/shaka/ads.js +21 -1
- package/externs/shaka/manifest.js +20 -1
- package/externs/shaka/manifest_parser.js +18 -1
- package/externs/shaka/player.js +164 -50
- package/lib/ads/ad_manager.js +225 -10
- package/lib/ads/ad_utils.js +323 -0
- package/lib/ads/ads_stats.js +24 -0
- package/lib/ads/client_side_ad_manager.js +42 -34
- package/lib/ads/interstitial_ad.js +292 -0
- package/lib/ads/interstitial_ad_manager.js +772 -0
- package/lib/ads/media_tailor_ad_manager.js +19 -22
- package/lib/ads/server_side_ad_manager.js +12 -15
- package/lib/cast/cast_utils.js +1 -0
- package/lib/cea/cea_utils.js +12 -0
- package/lib/cea/mp4_cea_parser.js +17 -0
- package/lib/dash/dash_parser.js +81 -28
- package/lib/dash/segment_base.js +3 -0
- package/lib/hls/hls_parser.js +429 -121
- package/lib/hls/manifest_text_parser.js +1 -0
- package/lib/media/adaptation_set_criteria.js +31 -1
- package/lib/media/drm_engine.js +16 -2
- package/lib/media/manifest_filterer.js +7 -5
- package/lib/media/preload_manager.js +8 -10
- package/lib/media/presentation_timeline.js +37 -5
- package/lib/media/quality_observer.js +68 -2
- package/lib/media/segment_reference.js +1 -1
- package/lib/media/segment_utils.js +33 -1
- package/lib/media/streaming_engine.js +120 -43
- package/lib/mss/mss_parser.js +13 -2
- package/lib/net/networking_engine.js +1 -0
- package/lib/offline/manifest_converter.js +3 -0
- package/lib/offline/offline_manifest_parser.js +5 -0
- package/lib/offline/storage.js +2 -0
- package/lib/player.js +498 -165
- package/lib/util/fake_event.js +1 -0
- package/lib/util/manifest_parser_utils.js +9 -2
- package/lib/util/mp4_box_parsers.js +32 -0
- package/lib/util/player_configuration.js +21 -9
- package/lib/util/stats.js +43 -0
- package/package.json +1 -1
- package/shaka-player.uncompiled.js +2 -0
- package/ui/ad_counter.js +3 -3
- package/ui/ad_position.js +3 -3
- package/ui/ad_statistics_button.js +237 -0
- package/ui/audio_language_selection.js +8 -4
- package/ui/controls.js +6 -3
- package/ui/element.js +3 -3
- package/ui/enums.js +1 -0
- package/ui/externs/ui.js +3 -0
- package/ui/language_utils.js +48 -3
- package/ui/less/containers.less +32 -0
- package/ui/less/thumbnails.less +13 -0
- package/ui/locales/en.json +2 -0
- package/ui/locales/es-419.json +2 -0
- package/ui/locales/es.json +3 -0
- package/ui/locales/fr.json +1 -0
- package/ui/locales/pl.json +1 -0
- package/ui/locales/pt-BR.json +1 -0
- package/ui/locales/pt-PT.json +1 -0
- package/ui/locales/source.json +8 -0
- package/ui/mute_button.js +4 -4
- package/ui/overflow_menu.js +3 -3
- package/ui/play_button.js +5 -7
- package/ui/presentation_time.js +3 -3
- package/ui/save_video_frame_button.js +196 -0
- package/ui/seek_bar.js +75 -20
- package/ui/skip_ad_button.js +4 -4
- package/ui/statistics_button.js +14 -2
- package/ui/ui.js +15 -0
- package/ui/volume_bar.js +4 -4
package/lib/player.js
CHANGED
|
@@ -175,7 +175,6 @@ goog.requireType('shaka.media.PresentationTimeline');
|
|
|
175
175
|
* @description Fired when the media quality changes at the playhead.
|
|
176
176
|
* That may be caused by an adaptation change or a DASH period transition.
|
|
177
177
|
* Separate events are emitted for audio and video contentTypes.
|
|
178
|
-
* This is supported for only DASH streams at this time.
|
|
179
178
|
* @property {string} type
|
|
180
179
|
* 'mediaqualitychanged'
|
|
181
180
|
* @property {shaka.extern.MediaQualityInfo} mediaQuality
|
|
@@ -185,6 +184,19 @@ goog.requireType('shaka.media.PresentationTimeline');
|
|
|
185
184
|
* @exportDoc
|
|
186
185
|
*/
|
|
187
186
|
|
|
187
|
+
/**
|
|
188
|
+
* @event shaka.Player.AudioTrackChangedEvent
|
|
189
|
+
* @description Fired when the audio track changes at the playhead.
|
|
190
|
+
* That may be caused by a user requesting to chang audio tracks.
|
|
191
|
+
* @property {string} type
|
|
192
|
+
* 'audiotrackchanged'
|
|
193
|
+
* @property {shaka.extern.MediaQualityInfo} mediaQuality
|
|
194
|
+
* Information about media quality at the playhead position.
|
|
195
|
+
* @property {number} position
|
|
196
|
+
* The playhead position.
|
|
197
|
+
* @exportDoc
|
|
198
|
+
*/
|
|
199
|
+
|
|
188
200
|
|
|
189
201
|
/**
|
|
190
202
|
* @event shaka.Player.BufferingEvent
|
|
@@ -332,7 +344,7 @@ goog.requireType('shaka.media.PresentationTimeline');
|
|
|
332
344
|
* The time that describes the end of the range of the metadata to which
|
|
333
345
|
* the cue applies.
|
|
334
346
|
* @property {string} metadataType
|
|
335
|
-
* Type of metadata. Eg: org.id3 or
|
|
347
|
+
* Type of metadata. Eg: 'org.id3' or 'com.apple.quicktime.HLS'
|
|
336
348
|
* @property {shaka.extern.MetadataFrame} payload
|
|
337
349
|
* The metadata itself
|
|
338
350
|
* @exportDoc
|
|
@@ -710,6 +722,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
710
722
|
/** @private {?shaka.extern.PlayerConfiguration} */
|
|
711
723
|
this.config_ = this.defaultConfig_();
|
|
712
724
|
|
|
725
|
+
/** @private {?number} */
|
|
726
|
+
this.currentTargetLatency_ = null;
|
|
727
|
+
|
|
728
|
+
/** @private {number} */
|
|
729
|
+
this.rebufferingCount_ = -1;
|
|
730
|
+
|
|
731
|
+
/** @private {?number} */
|
|
732
|
+
this.targetLatencyReached_ = null;
|
|
733
|
+
|
|
713
734
|
/**
|
|
714
735
|
* The TextDisplayerFactory that was last used to make a text displayer.
|
|
715
736
|
* Stored so that we can tell if a new type of text displayer is desired.
|
|
@@ -779,6 +800,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
779
800
|
/** @private {HTMLMediaElement} */
|
|
780
801
|
this.preloadDueAdManagerVideo_ = null;
|
|
781
802
|
|
|
803
|
+
/** @private {boolean} */
|
|
804
|
+
this.preloadDueAdManagerVideoEnded_ = false;
|
|
805
|
+
|
|
782
806
|
/** @private {shaka.util.Timer} */
|
|
783
807
|
this.preloadDueAdManagerTimer_ = new shaka.util.Timer(async () => {
|
|
784
808
|
if (this.preloadDueAdManager_) {
|
|
@@ -786,8 +810,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
786
810
|
await this.attach(
|
|
787
811
|
this.preloadDueAdManagerVideo_, /* initializeMediaSource= */ true);
|
|
788
812
|
await this.load(this.preloadDueAdManager_);
|
|
789
|
-
this.
|
|
813
|
+
if (!this.preloadDueAdManagerVideoEnded_) {
|
|
814
|
+
this.preloadDueAdManagerVideo_.play();
|
|
815
|
+
} else {
|
|
816
|
+
this.preloadDueAdManagerVideo_.pause();
|
|
817
|
+
}
|
|
790
818
|
this.preloadDueAdManager_ = null;
|
|
819
|
+
this.preloadDueAdManagerVideoEnded_ = false;
|
|
791
820
|
}
|
|
792
821
|
});
|
|
793
822
|
|
|
@@ -795,50 +824,43 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
795
824
|
this.adManager_ = shaka.Player.adManagerFactory_();
|
|
796
825
|
this.adManager_.configure(this.config_.ads);
|
|
797
826
|
|
|
798
|
-
// Note: we don't use shaka.ads.
|
|
799
|
-
// optional module in the player.
|
|
827
|
+
// Note: we don't use shaka.ads.Utils.AD_CONTENT_PAUSE_REQUESTED to
|
|
828
|
+
// avoid add a optional module in the player.
|
|
800
829
|
this.adManagerEventManager_.listen(
|
|
801
|
-
this.adManager_, 'ad-
|
|
802
|
-
if (this.config_.ads.supportsMultipleMediaElements) {
|
|
803
|
-
return;
|
|
804
|
-
}
|
|
805
|
-
const event = /** @type {?google.ima.AdEvent} */
|
|
806
|
-
(e['originalEvent']);
|
|
807
|
-
if (!event) {
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
const contentPauseRequested =
|
|
811
|
-
google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED;
|
|
812
|
-
if (event.type != contentPauseRequested) {
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
830
|
+
this.adManager_, 'ad-content-pause-requested', async (e) => {
|
|
815
831
|
this.preloadDueAdManagerTimer_.stop();
|
|
816
832
|
if (!this.preloadDueAdManager_) {
|
|
817
833
|
this.preloadDueAdManagerVideo_ = this.video_;
|
|
818
|
-
this.
|
|
819
|
-
|
|
834
|
+
this.preloadDueAdManagerVideoEnded_ = this.video_.ended;
|
|
835
|
+
const saveLivePosition = /** @type {boolean} */(
|
|
836
|
+
e['saveLivePosition']) || false;
|
|
837
|
+
this.preloadDueAdManager_ = await this.detachAndSavePreload(
|
|
838
|
+
/* keepAdManager= */ true, saveLivePosition);
|
|
820
839
|
}
|
|
821
840
|
});
|
|
822
841
|
|
|
823
|
-
// Note: we don't use shaka.ads.
|
|
824
|
-
// optional module in the player.
|
|
842
|
+
// Note: we don't use shaka.ads.Utils.AD_CONTENT_RESUME_REQUESTED to
|
|
843
|
+
// avoid add a optional module in the player.
|
|
825
844
|
this.adManagerEventManager_.listen(
|
|
826
|
-
this.adManager_, 'ad-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
const event = /** @type {?google.ima.AdEvent} */
|
|
831
|
-
(e['originalEvent']);
|
|
832
|
-
if (!event) {
|
|
833
|
-
return;
|
|
834
|
-
}
|
|
835
|
-
const contentResumeRequested =
|
|
836
|
-
google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED;
|
|
837
|
-
if (event.type != contentResumeRequested) {
|
|
838
|
-
return;
|
|
845
|
+
this.adManager_, 'ad-content-resume-requested', (e) => {
|
|
846
|
+
const offset = /** @type {number} */(e['offset']) || 0;
|
|
847
|
+
if (this.preloadDueAdManager_) {
|
|
848
|
+
this.preloadDueAdManager_.setOffsetToStartTime(offset);
|
|
839
849
|
}
|
|
840
850
|
this.preloadDueAdManagerTimer_.tickAfter(0.1);
|
|
841
851
|
});
|
|
852
|
+
|
|
853
|
+
// Note: we don't use shaka.ads.Utils.AD_CONTENT_ATTACH_REQUESTED to
|
|
854
|
+
// avoid add a optional module in the player.
|
|
855
|
+
this.adManagerEventManager_.listen(
|
|
856
|
+
this.adManager_, 'ad-content-attach-requested', async (e) => {
|
|
857
|
+
if (!this.video_ && this.preloadDueAdManagerVideo_) {
|
|
858
|
+
goog.asserts.assert(this.preloadDueAdManagerVideo_,
|
|
859
|
+
'Must have video');
|
|
860
|
+
await this.attach(this.preloadDueAdManagerVideo_,
|
|
861
|
+
/* initializeMediaSource= */ true);
|
|
862
|
+
}
|
|
863
|
+
});
|
|
842
864
|
}
|
|
843
865
|
|
|
844
866
|
// If the browser comes back online after being offline, then try to play
|
|
@@ -948,14 +970,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
948
970
|
// A PreloadManager can only be used with the Player instance that created
|
|
949
971
|
// it, so all PreloadManagers this Player has created are now useless.
|
|
950
972
|
// Destroy any remaining managers now, to help prevent memory leaks.
|
|
951
|
-
|
|
952
|
-
for (const preloadManager of this.createdPreloadManagers_) {
|
|
953
|
-
if (!preloadManager.isDestroyed()) {
|
|
954
|
-
preloadManagerDestroys.push(preloadManager.destroy());
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
this.createdPreloadManagers_ = [];
|
|
958
|
-
await Promise.all(preloadManagerDestroys);
|
|
973
|
+
await this.destroyAllPreloads();
|
|
959
974
|
|
|
960
975
|
// Tear-down the event managers to ensure handlers stop firing.
|
|
961
976
|
if (this.globalEventManager_) {
|
|
@@ -1462,6 +1477,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1462
1477
|
this.stats_ = new shaka.util.Stats(); // Replace with a clean object.
|
|
1463
1478
|
this.lastTextFactory_ = null;
|
|
1464
1479
|
|
|
1480
|
+
this.targetLatencyReached_ = null;
|
|
1481
|
+
this.currentTargetLatency_ = null;
|
|
1482
|
+
this.rebufferingCount_ = -1;
|
|
1483
|
+
|
|
1465
1484
|
this.externalSrcEqualsThumbnailsStreams_ = [];
|
|
1466
1485
|
|
|
1467
1486
|
this.completionPercent_ = NaN;
|
|
@@ -1473,7 +1492,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1473
1492
|
}
|
|
1474
1493
|
|
|
1475
1494
|
if (initializeMediaSource && shaka.util.Platform.supportsMediaSource() &&
|
|
1476
|
-
!this.mediaSourceEngine_) {
|
|
1495
|
+
!this.mediaSourceEngine_ && this.video_) {
|
|
1477
1496
|
await this.initializeMediaSourceEngineInner_();
|
|
1478
1497
|
}
|
|
1479
1498
|
}
|
|
@@ -1625,8 +1644,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1625
1644
|
// asset.
|
|
1626
1645
|
const shouldUseSrcEquals = !preloadManager;
|
|
1627
1646
|
|
|
1628
|
-
const startTimeOfLoad =
|
|
1629
|
-
preloadManager.getStartTimeOfLoad() : (Date.now() / 1000);
|
|
1647
|
+
const startTimeOfLoad = Date.now() / 1000;
|
|
1630
1648
|
|
|
1631
1649
|
// Stats are for a single playback/load session. Stats must be initialized
|
|
1632
1650
|
// before we allow calls to |updateStateHistory|.
|
|
@@ -1662,6 +1680,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1662
1680
|
this.manifestFilterer_ = preloadManager.getManifestFilterer();
|
|
1663
1681
|
this.parserFactory_ = preloadManager.getParserFactory();
|
|
1664
1682
|
this.parser_ = preloadManager.receiveParser();
|
|
1683
|
+
if (this.parser_ && this.parser_.setMediaElement && this.video_) {
|
|
1684
|
+
this.parser_.setMediaElement(this.video_);
|
|
1685
|
+
}
|
|
1665
1686
|
this.regionTimeline_ = preloadManager.receiveRegionTimeline();
|
|
1666
1687
|
this.qualityObserver_ = preloadManager.getQualityObserver();
|
|
1667
1688
|
this.manifest_ = preloadManager.getManifest();
|
|
@@ -1790,37 +1811,41 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1790
1811
|
* attached to something else.
|
|
1791
1812
|
*
|
|
1792
1813
|
* @param {boolean=} keepAdManager
|
|
1814
|
+
* @param {boolean=} saveLivePosition
|
|
1793
1815
|
* @return {!Promise.<?shaka.media.PreloadManager>}
|
|
1794
1816
|
* @export
|
|
1795
1817
|
*/
|
|
1796
|
-
async detachAndSavePreload(keepAdManager = false) {
|
|
1797
|
-
const preloadManager = await this.savePreload_();
|
|
1818
|
+
async detachAndSavePreload(keepAdManager = false, saveLivePosition = false) {
|
|
1819
|
+
const preloadManager = await this.savePreload_(saveLivePosition);
|
|
1798
1820
|
await this.detach(keepAdManager);
|
|
1799
1821
|
return preloadManager;
|
|
1800
1822
|
}
|
|
1801
1823
|
|
|
1802
1824
|
/**
|
|
1825
|
+
* @param {boolean=} saveLivePosition
|
|
1803
1826
|
* @return {!Promise.<?shaka.media.PreloadManager>}
|
|
1804
1827
|
* @private
|
|
1805
1828
|
*/
|
|
1806
|
-
async savePreload_() {
|
|
1829
|
+
async savePreload_(saveLivePosition = false) {
|
|
1807
1830
|
let preloadManager = null;
|
|
1808
1831
|
if (this.manifest_ && this.parser_ && this.parserFactory_ &&
|
|
1809
1832
|
this.assetUri_) {
|
|
1810
1833
|
let startTime = this.video_.currentTime;
|
|
1811
|
-
if (this.isLive()) {
|
|
1834
|
+
if (this.isLive() && !saveLivePosition) {
|
|
1812
1835
|
startTime = null;
|
|
1813
1836
|
}
|
|
1814
1837
|
// We have enough information to make a PreloadManager!
|
|
1815
|
-
const startTimeOfLoad = Date.now() / 1000;
|
|
1816
1838
|
preloadManager = await this.makePreloadManager_(
|
|
1817
1839
|
this.assetUri_,
|
|
1818
1840
|
startTime,
|
|
1819
1841
|
this.mimeType_,
|
|
1820
|
-
startTimeOfLoad,
|
|
1821
1842
|
/* allowPrefetch= */ true,
|
|
1822
1843
|
/* disableVideo= */ false,
|
|
1823
1844
|
/* allowMakeAbrManager= */ false);
|
|
1845
|
+
this.createdPreloadManagers_.push(preloadManager);
|
|
1846
|
+
if (this.parser_ && this.parser_.setMediaElement) {
|
|
1847
|
+
this.parser_.setMediaElement(/* mediaElement= */ null);
|
|
1848
|
+
}
|
|
1824
1849
|
preloadManager.attachManifest(
|
|
1825
1850
|
this.manifest_, this.parser_, this.parserFactory_);
|
|
1826
1851
|
preloadManager.attachAbrManager(
|
|
@@ -1878,6 +1903,21 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1878
1903
|
return preloadManager;
|
|
1879
1904
|
}
|
|
1880
1905
|
|
|
1906
|
+
/**
|
|
1907
|
+
* Calls |destroy| on each PreloadManager object this player has created.
|
|
1908
|
+
* @export
|
|
1909
|
+
*/
|
|
1910
|
+
async destroyAllPreloads() {
|
|
1911
|
+
const preloadManagerDestroys = [];
|
|
1912
|
+
for (const preloadManager of this.createdPreloadManagers_) {
|
|
1913
|
+
if (!preloadManager.isDestroyed()) {
|
|
1914
|
+
preloadManagerDestroys.push(preloadManager.destroy());
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
this.createdPreloadManagers_ = [];
|
|
1918
|
+
await Promise.all(preloadManagerDestroys);
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1881
1921
|
/**
|
|
1882
1922
|
* @param {string} assetUri
|
|
1883
1923
|
* @param {?number} startTime
|
|
@@ -1889,7 +1929,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1889
1929
|
async preloadInner_(assetUri, startTime, mimeType, standardLoad = false) {
|
|
1890
1930
|
goog.asserts.assert(this.networkingEngine_, 'Should have a net engine!');
|
|
1891
1931
|
goog.asserts.assert(this.config_, 'Config must not be null!');
|
|
1892
|
-
const startTimeOfLoad = Date.now() / 1000;
|
|
1893
1932
|
if (!mimeType) {
|
|
1894
1933
|
mimeType = await this.guessMimeType_(assetUri);
|
|
1895
1934
|
}
|
|
@@ -1915,23 +1954,34 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1915
1954
|
disableVideo = true;
|
|
1916
1955
|
}
|
|
1917
1956
|
}
|
|
1918
|
-
|
|
1919
|
-
assetUri, startTime, mimeType || null,
|
|
1957
|
+
let preloadManagerPromise = this.makePreloadManager_(
|
|
1958
|
+
assetUri, startTime, mimeType || null,
|
|
1920
1959
|
/* allowPrefetch= */ !standardLoad, disableVideo, allowMakeAbrManager);
|
|
1960
|
+
if (!standardLoad) {
|
|
1961
|
+
// We only need to track the PreloadManager if it is not part of a
|
|
1962
|
+
// standard load. If it is, the load() method will handle destroying it.
|
|
1963
|
+
// Adding a standard load PreloadManager to the createdPreloadManagers_
|
|
1964
|
+
// array runs the risk that the user will call destroyAllPreloads and
|
|
1965
|
+
// destroy that PreloadManager mid-load.
|
|
1966
|
+
preloadManagerPromise = preloadManagerPromise.then((preloadManager) => {
|
|
1967
|
+
this.createdPreloadManagers_.push(preloadManager);
|
|
1968
|
+
return preloadManager;
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1971
|
+
return preloadManagerPromise;
|
|
1921
1972
|
}
|
|
1922
1973
|
|
|
1923
1974
|
/**
|
|
1924
1975
|
* @param {string} assetUri
|
|
1925
1976
|
* @param {?number} startTime
|
|
1926
1977
|
* @param {?string} mimeType
|
|
1927
|
-
* @param {number} startTimeOfLoad
|
|
1928
1978
|
* @param {boolean=} allowPrefetch
|
|
1929
1979
|
* @param {boolean=} disableVideo
|
|
1930
1980
|
* @param {boolean=} allowMakeAbrManager
|
|
1931
1981
|
* @return {!Promise.<!shaka.media.PreloadManager>}
|
|
1932
1982
|
* @private
|
|
1933
1983
|
*/
|
|
1934
|
-
async makePreloadManager_(assetUri, startTime, mimeType,
|
|
1984
|
+
async makePreloadManager_(assetUri, startTime, mimeType,
|
|
1935
1985
|
allowPrefetch = true, disableVideo = false, allowMakeAbrManager = true) {
|
|
1936
1986
|
goog.asserts.assert(this.networkingEngine_, 'Must have net engine');
|
|
1937
1987
|
/** @type {?shaka.media.PreloadManager} */
|
|
@@ -2034,6 +2084,31 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2034
2084
|
});
|
|
2035
2085
|
},
|
|
2036
2086
|
getBandwidthEstimate: () => this.abrManager_.getBandwidthEstimate(),
|
|
2087
|
+
onMetadata: (type, startTime, endTime, values) => {
|
|
2088
|
+
let metadataType = type;
|
|
2089
|
+
if (type == 'com.apple.hls.interstitial') {
|
|
2090
|
+
metadataType = 'com.apple.quicktime.HLS';
|
|
2091
|
+
/** @type {shaka.extern.Interstitial} */
|
|
2092
|
+
const interstitial = {
|
|
2093
|
+
startTime,
|
|
2094
|
+
endTime,
|
|
2095
|
+
values,
|
|
2096
|
+
};
|
|
2097
|
+
if (this.adManager_) {
|
|
2098
|
+
goog.asserts.assert(this.video_, 'Must have video');
|
|
2099
|
+
this.adManager_.onInterstitialMetadata(
|
|
2100
|
+
this, this.video_, interstitial);
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
for (const payload of values) {
|
|
2104
|
+
preloadManager.addQueuedOperation(false, () => {
|
|
2105
|
+
this.dispatchMetadataEvent_(
|
|
2106
|
+
startTime, endTime, metadataType, payload);
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
},
|
|
2110
|
+
disableStream: (stream) => this.disableStream(
|
|
2111
|
+
stream, this.config_.streaming.maxDisabledTime),
|
|
2037
2112
|
};
|
|
2038
2113
|
const regionTimeline =
|
|
2039
2114
|
new shaka.media.RegionTimeline(() => this.seekRange());
|
|
@@ -2062,6 +2137,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2062
2137
|
const position = event['position'];
|
|
2063
2138
|
this.onMediaQualityChange_(mediaQualityInfo, position);
|
|
2064
2139
|
});
|
|
2140
|
+
|
|
2141
|
+
qualityObserver.addEventListener('audiotrackchange', (event) => {
|
|
2142
|
+
/** @type {shaka.extern.MediaQualityInfo} */
|
|
2143
|
+
const mediaQualityInfo = event['quality'];
|
|
2144
|
+
/** @type {number} */
|
|
2145
|
+
const position = event['position'];
|
|
2146
|
+
this.onMediaQualityChange_(mediaQualityInfo, position,
|
|
2147
|
+
/* audioTrackChanged= */ true);
|
|
2148
|
+
});
|
|
2065
2149
|
}
|
|
2066
2150
|
let firstEvent = true;
|
|
2067
2151
|
const drmPlayerInterface = {
|
|
@@ -2126,8 +2210,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2126
2210
|
allowMakeAbrManager,
|
|
2127
2211
|
};
|
|
2128
2212
|
preloadManager = new shaka.media.PreloadManager(
|
|
2129
|
-
assetUri, mimeType,
|
|
2130
|
-
this.createdPreloadManagers_.push(preloadManager);
|
|
2213
|
+
assetUri, mimeType, startTime, playerInterface);
|
|
2131
2214
|
return preloadManager;
|
|
2132
2215
|
}
|
|
2133
2216
|
|
|
@@ -2200,11 +2283,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2200
2283
|
// example, Android has native HLS, but we'd prefer our own MediaSource
|
|
2201
2284
|
// version there.
|
|
2202
2285
|
if (MimeUtils.isHlsType(mimeType)) {
|
|
2203
|
-
// Native HLS can be preferred on any platform via this flag:
|
|
2204
|
-
if (this.config_.streaming.preferNativeHls) {
|
|
2205
|
-
return true;
|
|
2206
|
-
}
|
|
2207
|
-
|
|
2208
2286
|
// Native FairPlay HLS can be preferred on Apple platfforms.
|
|
2209
2287
|
if (Platform.isApple() &&
|
|
2210
2288
|
(this.config_.drm.servers['com.apple.fps'] ||
|
|
@@ -2212,11 +2290,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2212
2290
|
return this.config_.streaming.useNativeHlsForFairPlay;
|
|
2213
2291
|
}
|
|
2214
2292
|
|
|
2215
|
-
//
|
|
2216
|
-
|
|
2217
|
-
if (Platform.isApple()) {
|
|
2218
|
-
return this.config_.streaming.useNativeHlsOnSafari;
|
|
2219
|
-
}
|
|
2293
|
+
// Native HLS can be preferred on any platform via this flag:
|
|
2294
|
+
return this.config_.streaming.preferNativeHls;
|
|
2220
2295
|
}
|
|
2221
2296
|
|
|
2222
2297
|
// In all other cases, we prefer MediaSource.
|
|
@@ -2378,28 +2453,40 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2378
2453
|
// Unless the user has already picked a variant, anyway, by calling
|
|
2379
2454
|
// selectVariantTrack before this loading stage.
|
|
2380
2455
|
let initialVariant = prefetchedVariant;
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2456
|
+
let toLazyLoad;
|
|
2457
|
+
let activeVariant;
|
|
2458
|
+
do {
|
|
2459
|
+
activeVariant = this.streamingEngine_.getCurrentVariant();
|
|
2460
|
+
if (!activeVariant && !initialVariant) {
|
|
2461
|
+
initialVariant = this.chooseVariant_(/* initialSelection= */ true);
|
|
2462
|
+
goog.asserts.assert(initialVariant, 'Must choose an initial variant!');
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
// Lazy-load the stream, so we will have enough info to make the playhead.
|
|
2466
|
+
const createSegmentIndexPromises = [];
|
|
2467
|
+
toLazyLoad = activeVariant || initialVariant;
|
|
2468
|
+
for (const stream of [toLazyLoad.video, toLazyLoad.audio]) {
|
|
2469
|
+
if (stream && !stream.segmentIndex) {
|
|
2470
|
+
createSegmentIndexPromises.push(stream.createSegmentIndex());
|
|
2471
|
+
}
|
|
2393
2472
|
}
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2473
|
+
if (createSegmentIndexPromises.length > 0) {
|
|
2474
|
+
// eslint-disable-next-line no-await-in-loop
|
|
2475
|
+
await Promise.all(createSegmentIndexPromises);
|
|
2476
|
+
}
|
|
2477
|
+
} while (!toLazyLoad || toLazyLoad.disabledUntilTime != 0);
|
|
2398
2478
|
|
|
2399
2479
|
if (this.parser_ && this.parser_.onInitialVariantChosen) {
|
|
2400
2480
|
this.parser_.onInitialVariantChosen(toLazyLoad);
|
|
2401
2481
|
}
|
|
2402
2482
|
|
|
2483
|
+
if (this.manifest_.isLowLatency && !this.config_.streaming.lowLatencyMode) {
|
|
2484
|
+
shaka.log.alwaysWarn('Low-latency live stream detected, but ' +
|
|
2485
|
+
'low-latency streaming mode is not enabled in Shaka Player. ' +
|
|
2486
|
+
'Set streaming.lowLatencyMode configuration to true, and see ' +
|
|
2487
|
+
'https://bit.ly/3clctcj for details.');
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2403
2490
|
shaka.Player.applyPlayRange_(this.manifest_.presentationTimeline,
|
|
2404
2491
|
this.config_.playRangeStart,
|
|
2405
2492
|
this.config_.playRangeEnd);
|
|
@@ -2508,9 +2595,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2508
2595
|
|
|
2509
2596
|
const isLive = this.isLive();
|
|
2510
2597
|
|
|
2511
|
-
if ((isLive && (this.config_.streaming.liveSync
|
|
2598
|
+
if ((isLive && ((this.config_.streaming.liveSync &&
|
|
2599
|
+
this.config_.streaming.liveSync.enabled) ||
|
|
2512
2600
|
this.manifest_.serviceDescription ||
|
|
2513
|
-
this.config_.streaming.
|
|
2601
|
+
this.config_.streaming.liveSync.panicMode)) ||
|
|
2514
2602
|
this.config_.streaming.vodDynamicPlaybackRate) {
|
|
2515
2603
|
const onTimeUpdate = () => this.onTimeUpdate_();
|
|
2516
2604
|
this.loadEventManager_.listen(mediaElement, 'timeupdate', onTimeUpdate);
|
|
@@ -2895,8 +2983,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2895
2983
|
|
|
2896
2984
|
const isLive = this.isLive();
|
|
2897
2985
|
|
|
2898
|
-
if ((isLive && (this.config_.streaming.liveSync
|
|
2899
|
-
this.config_.streaming.
|
|
2986
|
+
if ((isLive && ((this.config_.streaming.liveSync &&
|
|
2987
|
+
this.config_.streaming.liveSync.enabled) ||
|
|
2988
|
+
this.config_.streaming.liveSync.panicMode)) ||
|
|
2900
2989
|
this.config_.streaming.vodDynamicPlaybackRate) {
|
|
2901
2990
|
const onTimeUpdate = () => this.onTimeUpdate_();
|
|
2902
2991
|
this.loadEventManager_.listen(mediaElement, 'timeupdate', onTimeUpdate);
|
|
@@ -2985,6 +3074,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2985
3074
|
return;
|
|
2986
3075
|
}
|
|
2987
3076
|
|
|
3077
|
+
/** @type {!Array.<shaka.extern.Interstitial>} */
|
|
3078
|
+
const interstitials = [];
|
|
3079
|
+
|
|
2988
3080
|
for (const cue of track.activeCues) {
|
|
2989
3081
|
this.dispatchMetadataEvent_(cue.startTime, cue.endTime,
|
|
2990
3082
|
cue.type, cue.value);
|
|
@@ -2992,6 +3084,42 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2992
3084
|
if (this.adManager_) {
|
|
2993
3085
|
this.adManager_.onCueMetadataChange(cue.value);
|
|
2994
3086
|
}
|
|
3087
|
+
if (cue.type == 'com.apple.quicktime.HLS' && cue.startTime != null) {
|
|
3088
|
+
let interstitial = interstitials.find((i) => {
|
|
3089
|
+
return i.startTime == cue.startTime && i.endTime == cue.endTime;
|
|
3090
|
+
});
|
|
3091
|
+
if (!interstitial) {
|
|
3092
|
+
interstitial = /** @type {shaka.extern.Interstitial} */ ({
|
|
3093
|
+
startTime: cue.startTime,
|
|
3094
|
+
endTime: cue.endTime,
|
|
3095
|
+
values: [],
|
|
3096
|
+
});
|
|
3097
|
+
interstitials.push(interstitial);
|
|
3098
|
+
}
|
|
3099
|
+
interstitial.values.push(cue.value);
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
for (const interstitial of interstitials) {
|
|
3103
|
+
const isValidInterstitial = interstitial.values.some((value) => {
|
|
3104
|
+
return value.key == 'X-ASSET-URI' || value.key == 'X-ASSET-LIST';
|
|
3105
|
+
});
|
|
3106
|
+
if (!isValidInterstitial) {
|
|
3107
|
+
continue;
|
|
3108
|
+
}
|
|
3109
|
+
if (this.adManager_) {
|
|
3110
|
+
// It seems that CUE is natively omitted, by default we use CUE=ONCE
|
|
3111
|
+
// to avoid repeating them.
|
|
3112
|
+
interstitial.values.push({
|
|
3113
|
+
key: 'CUE',
|
|
3114
|
+
description: '',
|
|
3115
|
+
data: 'ONCE',
|
|
3116
|
+
mimeType: null,
|
|
3117
|
+
pictureType: null,
|
|
3118
|
+
});
|
|
3119
|
+
goog.asserts.assert(this.video_, 'Must have video');
|
|
3120
|
+
this.adManager_.onInterstitialMetadata(
|
|
3121
|
+
this, this.video_, interstitial);
|
|
3122
|
+
}
|
|
2995
3123
|
}
|
|
2996
3124
|
});
|
|
2997
3125
|
|
|
@@ -3215,6 +3343,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3215
3343
|
const stats = getStats();
|
|
3216
3344
|
if (stats) {
|
|
3217
3345
|
stats.addBytesDownloaded(bytesDownloaded);
|
|
3346
|
+
if (type === shaka.net.NetworkingEngine.RequestType.MANIFEST) {
|
|
3347
|
+
stats.setManifestSize(bytesDownloaded);
|
|
3348
|
+
}
|
|
3218
3349
|
}
|
|
3219
3350
|
}
|
|
3220
3351
|
};
|
|
@@ -3526,6 +3657,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3526
3657
|
* @export
|
|
3527
3658
|
*/
|
|
3528
3659
|
configure(config, value) {
|
|
3660
|
+
const Platform = shaka.util.Platform;
|
|
3661
|
+
|
|
3529
3662
|
goog.asserts.assert(this.config_, 'Config must not be null!');
|
|
3530
3663
|
goog.asserts.assert(typeof(config) == 'object' || arguments.length == 2,
|
|
3531
3664
|
'String configs should have values!');
|
|
@@ -3563,6 +3696,121 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3563
3696
|
'streaming.useNativeHlsOnSafari configuration',
|
|
3564
3697
|
'Please Use streaming.useNativeHlsForFairPlay or ' +
|
|
3565
3698
|
'streaming.preferNativeHls instead.');
|
|
3699
|
+
config['streaming']['preferNativeHls'] =
|
|
3700
|
+
config['streaming']['useNativeHlsOnSafari'] && Platform.isApple();
|
|
3701
|
+
delete config['streaming']['useNativeHlsOnSafari'];
|
|
3702
|
+
}
|
|
3703
|
+
|
|
3704
|
+
// Deprecate 'streaming.liveSync' boolean configuration.
|
|
3705
|
+
if (config['streaming'] &&
|
|
3706
|
+
typeof config['streaming']['liveSync'] == 'boolean') {
|
|
3707
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
3708
|
+
'streaming.liveSync',
|
|
3709
|
+
'Please Use streaming.liveSync.enabled instead.');
|
|
3710
|
+
const liveSyncValue = config['streaming']['liveSync'];
|
|
3711
|
+
config['streaming']['liveSync'] = {};
|
|
3712
|
+
config['streaming']['liveSync']['enabled'] = liveSyncValue;
|
|
3713
|
+
}
|
|
3714
|
+
|
|
3715
|
+
// map liveSyncMinLatency and liveSyncMaxLatency to liveSync.targetLatency
|
|
3716
|
+
// if liveSync.targetLatency isn't set.
|
|
3717
|
+
if (config['streaming'] && (!config['streaming']['liveSync'] ||
|
|
3718
|
+
!('targetLatency' in config['streaming']['liveSync'])) &&
|
|
3719
|
+
('liveSyncMinLatency' in config['streaming'] ||
|
|
3720
|
+
'liveSyncMaxLatency' in config['streaming'])) {
|
|
3721
|
+
const min = config['streaming']['liveSyncMinLatency'] || 0;
|
|
3722
|
+
const max = config['streaming']['liveSyncMaxLatency'] || 1;
|
|
3723
|
+
const mid = Math.abs(max - min) / 2;
|
|
3724
|
+
config['streaming']['liveSync'] = config['streaming']['liveSync'] || {};
|
|
3725
|
+
config['streaming']['liveSync']['targetLatency'] = min + mid;
|
|
3726
|
+
config['streaming']['liveSync']['targetLatencyTolerance'] = mid;
|
|
3727
|
+
}
|
|
3728
|
+
// Deprecate 'streaming.liveSyncMaxLatency' configuration.
|
|
3729
|
+
if (config['streaming'] && 'liveSyncMaxLatency' in config['streaming']) {
|
|
3730
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
3731
|
+
'streaming.liveSyncMaxLatency',
|
|
3732
|
+
'Please Use streaming.liveSync.targetLatency and ' +
|
|
3733
|
+
'streaming.liveSync.targetLatencyTolerance instead. ' +
|
|
3734
|
+
'Or, set the values in your DASH manifest');
|
|
3735
|
+
delete config['streaming']['liveSyncMaxLatency'];
|
|
3736
|
+
}
|
|
3737
|
+
// Deprecate 'streaming.liveSyncMinLatency' configuration.
|
|
3738
|
+
if (config['streaming'] && 'liveSyncMinLatency' in config['streaming']) {
|
|
3739
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
3740
|
+
'streaming.liveSyncMinLatency',
|
|
3741
|
+
'Please Use streaming.liveSync.targetLatency and ' +
|
|
3742
|
+
'streaming.liveSync.targetLatencyTolerance instead. ' +
|
|
3743
|
+
'Or, set the values in your DASH manifest');
|
|
3744
|
+
delete config['streaming']['liveSyncMinLatency'];
|
|
3745
|
+
}
|
|
3746
|
+
|
|
3747
|
+
// Deprecate 'streaming.liveSyncTargetLatency' configuration.
|
|
3748
|
+
if (config['streaming'] && 'liveSyncTargetLatency' in config['streaming']) {
|
|
3749
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
3750
|
+
'streaming.liveSyncTargetLatency',
|
|
3751
|
+
'Please Use streaming.liveSync.targetLatency instead.');
|
|
3752
|
+
config['streaming']['liveSync'] = config['streaming']['liveSync'] || {};
|
|
3753
|
+
config['streaming']['liveSync']['targetLatency'] =
|
|
3754
|
+
config['streaming']['liveSyncTargetLatency'];
|
|
3755
|
+
delete config['streaming']['liveSyncTargetLatency'];
|
|
3756
|
+
}
|
|
3757
|
+
|
|
3758
|
+
// Deprecate 'streaming.liveSyncTargetLatencyTolerance' configuration.
|
|
3759
|
+
if (config['streaming'] &&
|
|
3760
|
+
'liveSyncTargetLatencyTolerance' in config['streaming']) {
|
|
3761
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
3762
|
+
'streaming.liveSyncTargetLatencyTolerance',
|
|
3763
|
+
'Please Use streaming.liveSync.targetLatencyTolerance instead.');
|
|
3764
|
+
config['streaming']['liveSync'] = config['streaming']['liveSync'] || {};
|
|
3765
|
+
config['streaming']['liveSync']['targetLatencyTolerance'] =
|
|
3766
|
+
config['streaming']['liveSyncTargetLatencyTolerance'];
|
|
3767
|
+
delete config['streaming']['liveSyncTargetLatencyTolerance'];
|
|
3768
|
+
}
|
|
3769
|
+
|
|
3770
|
+
// Deprecate 'streaming.liveSyncPlaybackRate' configuration.
|
|
3771
|
+
if (config['streaming'] && 'liveSyncPlaybackRate' in config['streaming']) {
|
|
3772
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
3773
|
+
'streaming.liveSyncPlaybackRate',
|
|
3774
|
+
'Please Use streaming.liveSync.maxPlaybackRate instead.');
|
|
3775
|
+
config['streaming']['liveSync'] = config['streaming']['liveSync'] || {};
|
|
3776
|
+
config['streaming']['liveSync']['maxPlaybackRate'] =
|
|
3777
|
+
config['streaming']['liveSyncPlaybackRate'];
|
|
3778
|
+
delete config['streaming']['liveSyncPlaybackRate'];
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3781
|
+
// Deprecate 'streaming.liveSyncMinPlaybackRate' configuration.
|
|
3782
|
+
if (config['streaming'] &&
|
|
3783
|
+
'liveSyncMinPlaybackRate' in config['streaming']) {
|
|
3784
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
3785
|
+
'streaming.liveSyncMinPlaybackRate',
|
|
3786
|
+
'Please Use streaming.liveSync.minPlaybackRate instead.');
|
|
3787
|
+
config['streaming']['liveSync'] = config['streaming']['liveSync'] || {};
|
|
3788
|
+
config['streaming']['liveSync']['minPlaybackRate'] =
|
|
3789
|
+
config['streaming']['liveSyncMinPlaybackRate'];
|
|
3790
|
+
delete config['streaming']['liveSyncMinPlaybackRate'];
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3793
|
+
// Deprecate 'streaming.liveSyncPanicMode' configuration.
|
|
3794
|
+
if (config['streaming'] && 'liveSyncPanicMode' in config['streaming']) {
|
|
3795
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
3796
|
+
'streaming.liveSyncPanicMode',
|
|
3797
|
+
'Please Use streaming.liveSync.panicMode instead.');
|
|
3798
|
+
config['streaming']['liveSync'] = config['streaming']['liveSync'] || {};
|
|
3799
|
+
config['streaming']['liveSync']['panicMode'] =
|
|
3800
|
+
config['streaming']['liveSyncPanicMode'];
|
|
3801
|
+
delete config['streaming']['liveSyncPanicMode'];
|
|
3802
|
+
}
|
|
3803
|
+
|
|
3804
|
+
// Deprecate 'streaming.liveSyncPanicThreshold' configuration.
|
|
3805
|
+
if (config['streaming'] &&
|
|
3806
|
+
'liveSyncPanicThreshold' in config['streaming']) {
|
|
3807
|
+
shaka.Deprecate.deprecateFeature(5,
|
|
3808
|
+
'streaming.liveSyncPanicThreshold',
|
|
3809
|
+
'Please Use streaming.liveSync.panicThreshold instead.');
|
|
3810
|
+
config['streaming']['liveSync'] = config['streaming']['liveSync'] || {};
|
|
3811
|
+
config['streaming']['liveSync']['panicThreshold'] =
|
|
3812
|
+
config['streaming']['liveSyncPanicThreshold'];
|
|
3813
|
+
delete config['streaming']['liveSyncPanicThreshold'];
|
|
3566
3814
|
}
|
|
3567
3815
|
|
|
3568
3816
|
// Deprecate 'mediaSource.sourceBufferExtraFeatures' configuration.
|
|
@@ -4064,6 +4312,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4064
4312
|
}
|
|
4065
4313
|
}
|
|
4066
4314
|
|
|
4315
|
+
/**
|
|
4316
|
+
* Indicates if the player has fully loaded the stream.
|
|
4317
|
+
*
|
|
4318
|
+
* @return {boolean}
|
|
4319
|
+
* @export
|
|
4320
|
+
*/
|
|
4321
|
+
isFullyLoaded() {
|
|
4322
|
+
return this.fullyLoaded_;
|
|
4323
|
+
}
|
|
4324
|
+
|
|
4067
4325
|
/**
|
|
4068
4326
|
* Get the key system currently used by EME. If EME is not being used, this
|
|
4069
4327
|
* will return an empty string. If the player has not loaded content, this
|
|
@@ -4708,9 +4966,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4708
4966
|
* @param {string=} role
|
|
4709
4967
|
* @param {number=} channelsCount
|
|
4710
4968
|
* @param {number=} safeMargin
|
|
4969
|
+
* @param {string=} codec
|
|
4711
4970
|
* @export
|
|
4712
4971
|
*/
|
|
4713
|
-
selectAudioLanguage(language, role, channelsCount = 0, safeMargin = 0
|
|
4972
|
+
selectAudioLanguage(language, role, channelsCount = 0, safeMargin = 0,
|
|
4973
|
+
codec = '') {
|
|
4714
4974
|
if (this.manifest_ && this.playhead_) {
|
|
4715
4975
|
this.currentAdaptationSetCriteria_ =
|
|
4716
4976
|
new shaka.media.PreferenceBasedCriteria(
|
|
@@ -4723,7 +4983,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4723
4983
|
/* audioLabel= */ '',
|
|
4724
4984
|
/* videoLabel= */ '',
|
|
4725
4985
|
this.config_.mediaSource.codecSwitchingStrategy,
|
|
4726
|
-
this.config_.manifest.dash.enableAudioGroups
|
|
4986
|
+
this.config_.manifest.dash.enableAudioGroups,
|
|
4987
|
+
codec);
|
|
4727
4988
|
|
|
4728
4989
|
const diff = (a, b) => {
|
|
4729
4990
|
if (!a.video && !b.video) {
|
|
@@ -4743,6 +5004,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4743
5004
|
this.currentAdaptationSetCriteria_.create(this.manifest_.variants);
|
|
4744
5005
|
let bestVariant = null;
|
|
4745
5006
|
for (const curVariant of set.values()) {
|
|
5007
|
+
if (!shaka.util.StreamUtils.isPlayable(curVariant)) {
|
|
5008
|
+
continue;
|
|
5009
|
+
}
|
|
4746
5010
|
if (!bestVariant ||
|
|
4747
5011
|
diff(bestVariant, active) > diff(curVariant, active)) {
|
|
4748
5012
|
bestVariant = curVariant;
|
|
@@ -5269,10 +5533,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5269
5533
|
this.stats_.setLiveLatency(latency);
|
|
5270
5534
|
}
|
|
5271
5535
|
|
|
5272
|
-
if (this.manifest_
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
this.
|
|
5536
|
+
if (this.manifest_) {
|
|
5537
|
+
this.stats_.setManifestPeriodCount(this.manifest_.periodCount);
|
|
5538
|
+
this.stats_.setManifestGapCount(this.manifest_.gapCount);
|
|
5539
|
+
if (this.manifest_.presentationTimeline) {
|
|
5540
|
+
const maxSegmentDuration =
|
|
5541
|
+
this.manifest_.presentationTimeline.getMaxSegmentDuration();
|
|
5542
|
+
this.stats_.setMaxSegmentDuration(maxSegmentDuration);
|
|
5543
|
+
}
|
|
5276
5544
|
}
|
|
5277
5545
|
|
|
5278
5546
|
const estimate = this.abrManager_.getBandwidthEstimate();
|
|
@@ -6096,6 +6364,30 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6096
6364
|
this.cmcdManager_.setBuffering(isBuffering);
|
|
6097
6365
|
}
|
|
6098
6366
|
this.updateStateHistory_();
|
|
6367
|
+
|
|
6368
|
+
const dynamicTargetLatency =
|
|
6369
|
+
this.config_.streaming.liveSync.dynamicTargetLatency.enabled;
|
|
6370
|
+
const maxAttempts =
|
|
6371
|
+
this.config_.streaming.liveSync.dynamicTargetLatency.maxAttempts;
|
|
6372
|
+
|
|
6373
|
+
|
|
6374
|
+
if (dynamicTargetLatency && isBuffering &&
|
|
6375
|
+
this.rebufferingCount_ < maxAttempts) {
|
|
6376
|
+
const maxLatency =
|
|
6377
|
+
this.config_.streaming.liveSync.dynamicTargetLatency.maxLatency;
|
|
6378
|
+
|
|
6379
|
+
const targetLatencyTolerance =
|
|
6380
|
+
this.config_.streaming.liveSync.targetLatencyTolerance;
|
|
6381
|
+
const rebufferIncrement =
|
|
6382
|
+
this.config_.streaming.liveSync.dynamicTargetLatency
|
|
6383
|
+
.rebufferIncrement;
|
|
6384
|
+
if (this.currentTargetLatency_) {
|
|
6385
|
+
this.currentTargetLatency_ = Math.min(
|
|
6386
|
+
this.currentTargetLatency_ +
|
|
6387
|
+
++this.rebufferingCount_ * rebufferIncrement,
|
|
6388
|
+
maxLatency - targetLatencyTolerance);
|
|
6389
|
+
}
|
|
6390
|
+
}
|
|
6099
6391
|
}
|
|
6100
6392
|
|
|
6101
6393
|
// Surface the buffering event so that the app knows if/when we are
|
|
@@ -6222,52 +6514,78 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6222
6514
|
return;
|
|
6223
6515
|
}
|
|
6224
6516
|
|
|
6225
|
-
let
|
|
6226
|
-
let
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6517
|
+
let targetLatency;
|
|
6518
|
+
let maxLatency;
|
|
6519
|
+
let maxPlaybackRate;
|
|
6520
|
+
let minLatency;
|
|
6521
|
+
let minPlaybackRate;
|
|
6522
|
+
const targetLatencyTolerance =
|
|
6523
|
+
this.config_.streaming.liveSync.targetLatencyTolerance;
|
|
6524
|
+
const dynamicTargetLatency =
|
|
6525
|
+
this.config_.streaming.liveSync.dynamicTargetLatency.enabled;
|
|
6526
|
+
const stabilityThreshold =
|
|
6527
|
+
this.config_.streaming.liveSync.dynamicTargetLatency.stabilityThreshold;
|
|
6528
|
+
|
|
6529
|
+
if (this.config_.streaming.liveSync &&
|
|
6530
|
+
this.config_.streaming.liveSync.enabled) {
|
|
6531
|
+
targetLatency = this.config_.streaming.liveSync.targetLatency;
|
|
6532
|
+
maxLatency = targetLatency + targetLatencyTolerance;
|
|
6533
|
+
minLatency = Math.max(0, targetLatency - targetLatencyTolerance);
|
|
6534
|
+
maxPlaybackRate = this.config_.streaming.liveSync.maxPlaybackRate;
|
|
6535
|
+
minPlaybackRate = this.config_.streaming.liveSync.minPlaybackRate;
|
|
6230
6536
|
} else {
|
|
6231
6537
|
// serviceDescription must override if it is defined in the MPD and
|
|
6232
6538
|
// liveSync configuration is not set.
|
|
6233
6539
|
if (this.manifest_ && this.manifest_.serviceDescription) {
|
|
6234
|
-
|
|
6540
|
+
targetLatency = this.manifest_.serviceDescription.targetLatency;
|
|
6235
6541
|
if (this.manifest_.serviceDescription.targetLatency != null) {
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
this.config_.streaming.liveSyncTargetLatencyTolerance;
|
|
6542
|
+
maxLatency = this.manifest_.serviceDescription.targetLatency +
|
|
6543
|
+
targetLatencyTolerance;
|
|
6239
6544
|
} else if (this.manifest_.serviceDescription.maxLatency != null) {
|
|
6240
|
-
|
|
6545
|
+
maxLatency = this.manifest_.serviceDescription.maxLatency;
|
|
6241
6546
|
}
|
|
6242
|
-
liveSyncPlaybackRate =
|
|
6243
|
-
this.manifest_.serviceDescription.maxPlaybackRate ||
|
|
6244
|
-
this.config_.streaming.liveSyncPlaybackRate;
|
|
6245
|
-
}
|
|
6246
|
-
}
|
|
6247
|
-
|
|
6248
|
-
let liveSyncMinLatency;
|
|
6249
|
-
let liveSyncMinPlaybackRate;
|
|
6250
|
-
if (this.config_.streaming.liveSync) {
|
|
6251
|
-
liveSyncMinLatency = this.config_.streaming.liveSyncMinLatency;
|
|
6252
|
-
liveSyncMinPlaybackRate = this.config_.streaming.liveSyncMinPlaybackRate;
|
|
6253
|
-
} else {
|
|
6254
|
-
// serviceDescription must override if it is defined in the MPD and
|
|
6255
|
-
// liveSync configuration is not set.
|
|
6256
|
-
if (this.manifest_ && this.manifest_.serviceDescription) {
|
|
6257
|
-
liveSyncMinLatency = this.config_.streaming.liveSyncMinLatency;
|
|
6258
6547
|
if (this.manifest_.serviceDescription.targetLatency != null) {
|
|
6259
|
-
|
|
6548
|
+
minLatency = Math.max(0,
|
|
6260
6549
|
this.manifest_.serviceDescription.targetLatency -
|
|
6261
|
-
|
|
6550
|
+
targetLatencyTolerance);
|
|
6262
6551
|
} else if (this.manifest_.serviceDescription.minLatency != null) {
|
|
6263
|
-
|
|
6552
|
+
minLatency = this.manifest_.serviceDescription.minLatency;
|
|
6264
6553
|
}
|
|
6265
|
-
|
|
6554
|
+
maxPlaybackRate =
|
|
6555
|
+
this.manifest_.serviceDescription.maxPlaybackRate ||
|
|
6556
|
+
this.config_.streaming.liveSync.maxPlaybackRate;
|
|
6557
|
+
minPlaybackRate =
|
|
6266
6558
|
this.manifest_.serviceDescription.minPlaybackRate ||
|
|
6267
|
-
this.config_.streaming.
|
|
6559
|
+
this.config_.streaming.liveSync.minPlaybackRate;
|
|
6268
6560
|
}
|
|
6269
6561
|
}
|
|
6270
6562
|
|
|
6563
|
+
if (!this.currentTargetLatency_ && typeof targetLatency === 'number') {
|
|
6564
|
+
this.currentTargetLatency_ = targetLatency;
|
|
6565
|
+
}
|
|
6566
|
+
|
|
6567
|
+
const maxAttempts =
|
|
6568
|
+
this.config_.streaming.liveSync.dynamicTargetLatency.maxAttempts;
|
|
6569
|
+
if (dynamicTargetLatency && this.targetLatencyReached_ &&
|
|
6570
|
+
this.currentTargetLatency_ !== null &&
|
|
6571
|
+
typeof targetLatency === 'number' &&
|
|
6572
|
+
this.rebufferingCount_ < maxAttempts &&
|
|
6573
|
+
(Date.now() - this.targetLatencyReached_) > stabilityThreshold * 1000) {
|
|
6574
|
+
const dynamicMinLatency =
|
|
6575
|
+
this.config_.streaming.liveSync.dynamicTargetLatency.minLatency;
|
|
6576
|
+
const latencyIncrement = (targetLatency - dynamicMinLatency) / 2;
|
|
6577
|
+
this.currentTargetLatency_ = Math.max(
|
|
6578
|
+
this.currentTargetLatency_ - latencyIncrement,
|
|
6579
|
+
// current target latency should be within the tolerance of the min
|
|
6580
|
+
// latency to not overshoot it
|
|
6581
|
+
dynamicMinLatency + targetLatencyTolerance);
|
|
6582
|
+
this.targetLatencyReached_ = Date.now();
|
|
6583
|
+
}
|
|
6584
|
+
if (dynamicTargetLatency && this.currentTargetLatency_ !== null) {
|
|
6585
|
+
maxLatency = this.currentTargetLatency_ + targetLatencyTolerance;
|
|
6586
|
+
minLatency = this.currentTargetLatency_ - targetLatencyTolerance;
|
|
6587
|
+
}
|
|
6588
|
+
|
|
6271
6589
|
const latency = seekRange.end - this.video_.currentTime;
|
|
6272
6590
|
let offset = 0;
|
|
6273
6591
|
// In src= mode, the seek range isn't updated frequently enough, so we need
|
|
@@ -6278,45 +6596,49 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6278
6596
|
const buffered = this.video_.buffered;
|
|
6279
6597
|
if (buffered.length > 0) {
|
|
6280
6598
|
const bufferedEnd = buffered.end(buffered.length - 1);
|
|
6281
|
-
offset = Math.max(
|
|
6599
|
+
offset = Math.max(maxPlaybackRate, bufferedEnd - seekRange.end);
|
|
6282
6600
|
}
|
|
6283
6601
|
}
|
|
6284
6602
|
|
|
6285
|
-
const panicMode = this.config_.streaming.
|
|
6286
|
-
const panicThreshold =
|
|
6603
|
+
const panicMode = this.config_.streaming.liveSync.panicMode;
|
|
6604
|
+
const panicThreshold =
|
|
6605
|
+
this.config_.streaming.liveSync.panicThreshold * 1000;
|
|
6287
6606
|
const timeSinceLastRebuffer =
|
|
6288
6607
|
Date.now() - this.bufferObserver_.getLastRebufferTime();
|
|
6289
|
-
if (panicMode && !
|
|
6290
|
-
|
|
6608
|
+
if (panicMode && !minPlaybackRate) {
|
|
6609
|
+
minPlaybackRate = this.config_.streaming.liveSync.minPlaybackRate;
|
|
6291
6610
|
}
|
|
6292
6611
|
|
|
6293
|
-
if (panicMode &&
|
|
6612
|
+
if (panicMode && minPlaybackRate &&
|
|
6294
6613
|
timeSinceLastRebuffer <= panicThreshold) {
|
|
6295
|
-
if (playbackRate !=
|
|
6614
|
+
if (playbackRate != minPlaybackRate) {
|
|
6296
6615
|
shaka.log.debug('Time since last rebuffer (' +
|
|
6297
6616
|
timeSinceLastRebuffer + 's) ' +
|
|
6298
|
-
'is less than the
|
|
6299
|
-
's). Updating playbackRate to ' +
|
|
6300
|
-
this.trickPlay(
|
|
6301
|
-
}
|
|
6302
|
-
} else if (
|
|
6303
|
-
|
|
6304
|
-
if (playbackRate !=
|
|
6305
|
-
shaka.log.debug('Latency (' + latency + 's) ' +
|
|
6306
|
-
'
|
|
6307
|
-
'Updating playbackRate to ' +
|
|
6308
|
-
this.trickPlay(
|
|
6309
|
-
}
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
'
|
|
6316
|
-
|
|
6317
|
-
|
|
6617
|
+
'is less than the live sync panicThreshold (' + panicThreshold +
|
|
6618
|
+
's). Updating playbackRate to ' + minPlaybackRate);
|
|
6619
|
+
this.trickPlay(minPlaybackRate);
|
|
6620
|
+
}
|
|
6621
|
+
} else if (maxLatency && maxPlaybackRate &&
|
|
6622
|
+
(latency - offset) > maxLatency) {
|
|
6623
|
+
if (playbackRate != maxPlaybackRate) {
|
|
6624
|
+
shaka.log.debug('Latency (' + latency + 's) is greater than ' +
|
|
6625
|
+
'live sync maxLatency (' + maxLatency + 's). ' +
|
|
6626
|
+
'Updating playbackRate to ' + maxPlaybackRate);
|
|
6627
|
+
this.trickPlay(maxPlaybackRate);
|
|
6628
|
+
}
|
|
6629
|
+
this.targetLatencyReached_ = null;
|
|
6630
|
+
} else if (minLatency && minPlaybackRate &&
|
|
6631
|
+
(latency - offset) < minLatency) {
|
|
6632
|
+
if (playbackRate != minPlaybackRate) {
|
|
6633
|
+
shaka.log.debug('Latency (' + latency + 's) is smaller than ' +
|
|
6634
|
+
'live sync minLatency (' + minLatency + 's). ' +
|
|
6635
|
+
'Updating playbackRate to ' + minPlaybackRate);
|
|
6636
|
+
this.trickPlay(minPlaybackRate);
|
|
6637
|
+
}
|
|
6638
|
+
this.targetLatencyReached_ = null;
|
|
6318
6639
|
} else if (playbackRate !== this.playRateController_.getDefaultRate()) {
|
|
6319
6640
|
this.cancelTrickPlay();
|
|
6641
|
+
this.targetLatencyReached_ = Date.now();
|
|
6320
6642
|
}
|
|
6321
6643
|
}
|
|
6322
6644
|
|
|
@@ -6458,6 +6780,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6458
6780
|
return str;
|
|
6459
6781
|
};
|
|
6460
6782
|
|
|
6783
|
+
let shouldStopTimer = true;
|
|
6461
6784
|
for (const variant of this.manifest_.variants) {
|
|
6462
6785
|
if (variant.disabledUntilTime > 0 && variant.disabledUntilTime <= now) {
|
|
6463
6786
|
variant.disabledUntilTime = 0;
|
|
@@ -6465,15 +6788,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6465
6788
|
|
|
6466
6789
|
shaka.log.v2('Re-enabled variant with ' + streamsAsString(variant));
|
|
6467
6790
|
}
|
|
6791
|
+
if (variant.disabledUntilTime > 0) {
|
|
6792
|
+
shouldStopTimer = false;
|
|
6793
|
+
}
|
|
6468
6794
|
}
|
|
6469
6795
|
|
|
6470
|
-
const shouldStopTimer = this.manifest_.variants.every((variant) => {
|
|
6471
|
-
goog.asserts.assert(
|
|
6472
|
-
variant.disabledUntilTime >= 0,
|
|
6473
|
-
'|variant.disableTimeUntilTime| must always be >= 0');
|
|
6474
|
-
return variant.disabledUntilTime === 0;
|
|
6475
|
-
});
|
|
6476
|
-
|
|
6477
6796
|
if (shouldStopTimer) {
|
|
6478
6797
|
this.checkVariantsTimer_.stop();
|
|
6479
6798
|
}
|
|
@@ -6906,7 +7225,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6906
7225
|
const hasAltStream = this.manifest_.variants.some((variant) => {
|
|
6907
7226
|
const altStream = variant[stream.type];
|
|
6908
7227
|
|
|
6909
|
-
if (altStream && altStream.id !== stream.id
|
|
7228
|
+
if (altStream && altStream.id !== stream.id &&
|
|
7229
|
+
!variant.disabledUntilTime) {
|
|
6910
7230
|
if (shaka.util.StreamUtils.isAudio(stream)) {
|
|
6911
7231
|
return stream.language === altStream.language;
|
|
6912
7232
|
}
|
|
@@ -6967,6 +7287,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
6967
7287
|
return;
|
|
6968
7288
|
}
|
|
6969
7289
|
|
|
7290
|
+
if (error.severity === shaka.util.Error.Severity.RECOVERABLE) {
|
|
7291
|
+
this.stats_.addNonFatalError();
|
|
7292
|
+
}
|
|
7293
|
+
|
|
6970
7294
|
let fireError = true;
|
|
6971
7295
|
if (this.fullyLoaded_ && this.manifest_ && this.streamingEngine_ &&
|
|
6972
7296
|
(error.code == shaka.util.Error.Code.VIDEO_ERROR ||
|
|
@@ -7032,10 +7356,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
7032
7356
|
*
|
|
7033
7357
|
* @param {shaka.extern.MediaQualityInfo} mediaQuality
|
|
7034
7358
|
* @param {number} position
|
|
7359
|
+
* @param {boolean} audioTrackChanged This is to specify whether this should
|
|
7360
|
+
* trigger a MediaQualityChangedEvent or an AudioTrackChangedEvent. Defaults
|
|
7361
|
+
* to false to trigger MediaQualityChangedEvent.
|
|
7035
7362
|
*
|
|
7036
7363
|
* @private
|
|
7037
7364
|
*/
|
|
7038
|
-
onMediaQualityChange_(mediaQuality, position) {
|
|
7365
|
+
onMediaQualityChange_(mediaQuality, position, audioTrackChanged = false) {
|
|
7039
7366
|
// Always make a copy to avoid exposing our internal data to the app.
|
|
7040
7367
|
const clone = {
|
|
7041
7368
|
bandwidth: mediaQuality.bandwidth,
|
|
@@ -7048,6 +7375,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
7048
7375
|
channelsCount: mediaQuality.channelsCount,
|
|
7049
7376
|
pixelAspectRatio: mediaQuality.pixelAspectRatio,
|
|
7050
7377
|
width: mediaQuality.width,
|
|
7378
|
+
label: mediaQuality.label,
|
|
7379
|
+
roles: mediaQuality.roles,
|
|
7380
|
+
language: mediaQuality.language,
|
|
7051
7381
|
};
|
|
7052
7382
|
|
|
7053
7383
|
const data = new Map()
|
|
@@ -7055,7 +7385,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
7055
7385
|
.set('position', position);
|
|
7056
7386
|
|
|
7057
7387
|
this.dispatchEvent(shaka.Player.makeEvent_(
|
|
7058
|
-
|
|
7388
|
+
audioTrackChanged ?
|
|
7389
|
+
shaka.util.FakeEvent.EventName.AudioTrackChanged :
|
|
7390
|
+
shaka.util.FakeEvent.EventName.MediaQualityChanged,
|
|
7391
|
+
data));
|
|
7059
7392
|
}
|
|
7060
7393
|
|
|
7061
7394
|
/**
|
|
@@ -7473,7 +7806,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
7473
7806
|
* @export
|
|
7474
7807
|
*/
|
|
7475
7808
|
// eslint-disable-next-line no-useless-concat
|
|
7476
|
-
shaka.Player.version = 'v4.
|
|
7809
|
+
shaka.Player.version = 'v4.10.1' + '-uncompiled'; // x-release-please-version
|
|
7477
7810
|
|
|
7478
7811
|
// Initialize the deprecation system using the version string we just set
|
|
7479
7812
|
// on the player.
|