Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

shaka-player

Package Overview
Dependencies
Maintainers
3
Versions
344
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

shaka-player - npm Package Compare versions

Comparing version 2.1.8 to 2.2.0

demo/app_logo_144.png

43

demo/asset_section.js

@@ -67,2 +67,4 @@ /**

mimeTypes.push('video/mp4');
if (asset.features.indexOf(shakaAssets.Feature.MP2TS) >= 0)
mimeTypes.push('video/mp2t');
if (!mimeTypes.some(

@@ -138,4 +140,17 @@ function(type) { return shakaDemo.support_.media[type]; })) {

var videoRobustness =
document.getElementById('drmSettingsVideoRobustness').value;
var audioRobustness =
document.getElementById('drmSettingsAudioRobustness').value;
var commonDrmSystems =
['com.widevine.alpha', 'com.microsoft.playready', 'com.adobe.primetime'];
var config = /** @type {shakaExtern.PlayerConfiguration} */(
{ abr: {}, streaming: {}, manifest: { dash: {} } });
config.drm = /** @type {shakaExtern.DrmConfiguration} */({
advanced: {}});
commonDrmSystems.forEach(function(system) {
config.drm.advanced[system] =
/** @type {shakaExtern.AdvancedDrmConfiguration} */({});
});
config.manifest.dash.clockSyncUri =

@@ -146,3 +161,7 @@ '//shaka-player-demo.appspot.com/time.txt';

// Use the custom fields.
var licenseServer = document.getElementById('licenseServerInput').value;
var licenseServers = {};
commonDrmSystems.forEach(function(system) {
licenseServers[system] =
document.getElementById('licenseServerInput').value;
});
asset = /** @type {shakaAssets.AssetInfo} */ ({

@@ -153,7 +172,3 @@ manifestUri: document.getElementById('manifestInput').value,

// They will simply fill in a Widevine license server on Chrome, etc.
licenseServers: {
'com.widevine.alpha': licenseServer,
'com.microsoft.playready': licenseServer,
'com.adobe.primetime': licenseServer
}
licenseServers: licenseServers
});

@@ -164,3 +179,3 @@ }

// Add config from this asset.
// Add configuration from this asset.
ShakaDemoUtils.setupAssetMetadata(asset, player);

@@ -172,3 +187,15 @@ shakaDemo.castProxy_.setAppData({

// Add configuration from the UI.
// Add drm configuration from the UI.
if (videoRobustness) {
commonDrmSystems.forEach(function(system) {
config.drm.advanced[system].videoRobustness = videoRobustness;
});
}
if (audioRobustness) {
commonDrmSystems.forEach(function(system) {
config.drm.advanced[system].audioRobustness = audioRobustness;
});
}
// Add other configuration from the UI.
config.preferredAudioLanguage =

@@ -175,0 +202,0 @@ document.getElementById('preferredAudioLanguage').value;

@@ -50,2 +50,6 @@ /**

'change', shakaDemo.onAutoplayChange_);
document.getElementById('drmSettingsVideoRobustness').addEventListener(
'input', shakaDemo.onDrmSettingsChange_);
document.getElementById('drmSettingsAudioRobustness').addEventListener(
'input', shakaDemo.onDrmSettingsChange_);
};

@@ -65,2 +69,12 @@

*/
shakaDemo.onDrmSettingsChange_ = function(event) {
// Change the hash, to mirror this.
shakaDemo.hashShouldChange_();
};
/**
* @param {!Event} event
* @private
*/
shakaDemo.onLogLevelChange_ = function(event) {

@@ -67,0 +81,0 @@ // shaka.log is not set if logging isn't enabled.

@@ -82,7 +82,7 @@ /**

/**
* The registered ID of the v2.1 Chromecast receiver demo.
* The registered ID of the v2.2 Chromecast receiver demo.
* @const {string}
* @private
*/
shakaDemo.CC_APP_ID_ = '658CCD53';
shakaDemo.CC_APP_ID_ = '91580C19';

@@ -222,2 +222,10 @@

shakaDemo.preBrowserCheckParams_ = function(params) {
if ('videoRobustness' in params) {
document.getElementById('drmSettingsVideoRobustness').value =
params['videoRobustness'];
}
if ('audioRobustness' in params) {
document.getElementById('drmSettingsAudioRobustness').value =
params['audioRobustness'];
}
if ('lang' in params) {

@@ -498,2 +506,12 @@ document.getElementById('preferredAudioLanguage').value = params['lang'];

// Store values for drm configuration.
var videoRobustness =
document.getElementById('drmSettingsVideoRobustness').value;
if (videoRobustness)
params.push('videoRobustness=' + videoRobustness);
var audioRobustness =
document.getElementById('drmSettingsAudioRobustness').value;
if (audioRobustness)
params.push('audioRobustness=' + audioRobustness);
var newHash = '#' + params.join(';');

@@ -500,0 +518,0 @@ if (newHash != location.hash) {

32

dist/deps.js

@@ -5,3 +5,3 @@ // This file was autogenerated by third_party/closure/deps/depswriter.py.

goog.addDependency('../../../lib/abr/ewma_bandwidth_estimator.js', ['shaka.abr.EwmaBandwidthEstimator'], ['shaka.abr.Ewma']);
goog.addDependency('../../../lib/abr/simple_abr_manager.js', ['shaka.abr.SimpleAbrManager'], ['goog.asserts', 'shaka.abr.EwmaBandwidthEstimator', 'shaka.log', 'shaka.util.Error', 'shaka.util.ManifestParserUtils', 'shaka.util.StreamUtils']);
goog.addDependency('../../../lib/abr/simple_abr_manager.js', ['shaka.abr.SimpleAbrManager'], ['goog.asserts', 'shaka.abr.EwmaBandwidthEstimator', 'shaka.log', 'shaka.util.Error', 'shaka.util.StreamUtils']);
goog.addDependency('../../../lib/cast/cast_proxy.js', ['shaka.cast.CastProxy'], ['goog.asserts', 'shaka.cast.CastSender', 'shaka.cast.CastUtils', 'shaka.log', 'shaka.util.Error', 'shaka.util.EventManager', 'shaka.util.FakeEvent', 'shaka.util.FakeEventTarget', 'shaka.util.IDestroyable']);

@@ -12,4 +12,4 @@ goog.addDependency('../../../lib/cast/cast_receiver.js', ['shaka.cast.CastReceiver'], ['goog.asserts', 'shaka.cast.CastUtils', 'shaka.log', 'shaka.util.Error', 'shaka.util.FakeEvent', 'shaka.util.FakeEventTarget', 'shaka.util.IDestroyable']);

goog.addDependency('../../../lib/dash/content_protection.js', ['shaka.dash.ContentProtection'], ['goog.asserts', 'shaka.log', 'shaka.util.Error', 'shaka.util.Functional', 'shaka.util.ManifestParserUtils', 'shaka.util.MapUtils', 'shaka.util.Uint8ArrayUtils', 'shaka.util.XmlUtils']);
goog.addDependency('../../../lib/dash/dash_parser.js', ['shaka.dash.DashParser'], ['goog.asserts', 'shaka.dash.ContentProtection', 'shaka.dash.SegmentBase', 'shaka.dash.SegmentList', 'shaka.dash.SegmentTemplate', 'shaka.log', 'shaka.media.DrmEngine', 'shaka.media.ManifestParser', 'shaka.media.PresentationTimeline', 'shaka.media.SegmentReference', 'shaka.media.TextEngine', 'shaka.net.NetworkingEngine', 'shaka.util.Error', 'shaka.util.Functional', 'shaka.util.LanguageUtils', 'shaka.util.ManifestParserUtils', 'shaka.util.StreamUtils', 'shaka.util.StringUtils', 'shaka.util.XmlUtils']);
goog.addDependency('../../../lib/dash/mpd_utils.js', ['shaka.dash.MpdUtils'], ['goog.asserts', 'shaka.log', 'shaka.util.Functional', 'shaka.util.ManifestParserUtils', 'shaka.util.XmlUtils']);
goog.addDependency('../../../lib/dash/dash_parser.js', ['shaka.dash.DashParser'], ['goog.asserts', 'shaka.dash.ContentProtection', 'shaka.dash.SegmentBase', 'shaka.dash.SegmentList', 'shaka.dash.SegmentTemplate', 'shaka.log', 'shaka.media.DrmEngine', 'shaka.media.ManifestParser', 'shaka.media.PresentationTimeline', 'shaka.media.SegmentReference', 'shaka.net.NetworkingEngine', 'shaka.text.TextEngine', 'shaka.util.Error', 'shaka.util.Functional', 'shaka.util.LanguageUtils', 'shaka.util.ManifestParserUtils', 'shaka.util.MimeUtils', 'shaka.util.StringUtils', 'shaka.util.XmlUtils']);
goog.addDependency('../../../lib/dash/mpd_utils.js', ['shaka.dash.MpdUtils'], ['goog.asserts', 'shaka.log', 'shaka.net.NetworkingEngine', 'shaka.util.Functional', 'shaka.util.ManifestParserUtils', 'shaka.util.StringUtils', 'shaka.util.XmlUtils']);
goog.addDependency('../../../lib/dash/segment_base.js', ['shaka.dash.SegmentBase'], ['goog.asserts', 'shaka.dash.MpdUtils', 'shaka.log', 'shaka.media.InitSegmentReference', 'shaka.media.Mp4SegmentIndexParser', 'shaka.media.SegmentIndex', 'shaka.media.WebmSegmentIndexParser', 'shaka.util.Error', 'shaka.util.ManifestParserUtils', 'shaka.util.XmlUtils']);

@@ -24,8 +24,6 @@ goog.addDependency('../../../lib/dash/segment_list.js', ['shaka.dash.SegmentList'], ['goog.asserts', 'shaka.dash.MpdUtils', 'shaka.dash.SegmentBase', 'shaka.log', 'shaka.media.SegmentIndex', 'shaka.media.SegmentReference', 'shaka.util.Error', 'shaka.util.Functional', 'shaka.util.ManifestParserUtils', 'shaka.util.XmlUtils']);

goog.addDependency('../../../lib/hls/manifest_text_parser.js', ['shaka.hls.ManifestTextParser'], ['shaka.hls.Attribute', 'shaka.hls.Playlist', 'shaka.hls.PlaylistType', 'shaka.hls.Segment', 'shaka.hls.Tag', 'shaka.hls.Utils', 'shaka.util.Error', 'shaka.util.StringUtils', 'shaka.util.TextParser']);
goog.addDependency('../../../lib/media/drm_engine.js', ['shaka.media.DrmEngine'], ['goog.asserts', 'shaka.log', 'shaka.net.NetworkingEngine', 'shaka.util.ArrayUtils', 'shaka.util.Error', 'shaka.util.EventManager', 'shaka.util.Functional', 'shaka.util.IDestroyable', 'shaka.util.ManifestParserUtils', 'shaka.util.MapUtils', 'shaka.util.PublicPromise', 'shaka.util.StringUtils', 'shaka.util.Timer', 'shaka.util.Uint8ArrayUtils']);
goog.addDependency('../../../lib/media/drm_engine.js', ['shaka.media.DrmEngine'], ['goog.asserts', 'shaka.log', 'shaka.net.NetworkingEngine', 'shaka.util.ArrayUtils', 'shaka.util.Error', 'shaka.util.EventManager', 'shaka.util.Functional', 'shaka.util.IDestroyable', 'shaka.util.ManifestParserUtils', 'shaka.util.MapUtils', 'shaka.util.MimeUtils', 'shaka.util.PublicPromise', 'shaka.util.StringUtils', 'shaka.util.Timer', 'shaka.util.Uint8ArrayUtils']);
goog.addDependency('../../../lib/media/manifest_parser.js', ['shaka.media.ManifestParser'], ['goog.Uri', 'goog.asserts', 'shaka.log', 'shaka.net.NetworkingEngine', 'shaka.util.Error']);
goog.addDependency('../../../lib/media/media_source_engine.js', ['shaka.media.MediaSourceEngine'], ['goog.asserts', 'shaka.log', 'shaka.media.TextEngine', 'shaka.media.TimeRangesUtils', 'shaka.util.Error', 'shaka.util.EventManager', 'shaka.util.Functional', 'shaka.util.IDestroyable', 'shaka.util.ManifestParserUtils', 'shaka.util.PublicPromise']);
goog.addDependency('../../../lib/media/media_source_engine.js', ['shaka.media.MediaSourceEngine'], ['goog.asserts', 'shaka.log', 'shaka.media.TimeRangesUtils', 'shaka.text.TextEngine', 'shaka.util.Error', 'shaka.util.EventManager', 'shaka.util.Functional', 'shaka.util.IDestroyable', 'shaka.util.ManifestParserUtils', 'shaka.util.MimeUtils', 'shaka.util.PublicPromise']);
goog.addDependency('../../../lib/media/mp4_segment_index_parser.js', ['shaka.media.Mp4SegmentIndexParser'], ['goog.asserts', 'shaka.log', 'shaka.media.SegmentReference', 'shaka.util.Error', 'shaka.util.Mp4Parser']);
goog.addDependency('../../../lib/media/mp4_ttml_parser.js', ['shaka.media.Mp4TtmlParser'], ['shaka.media.TextEngine', 'shaka.media.TtmlTextParser', 'shaka.util.Error', 'shaka.util.Mp4Parser']);
goog.addDependency('../../../lib/media/mp4_vtt_parser.js', ['shaka.media.Mp4VttParser'], ['goog.asserts', 'shaka.log', 'shaka.media.TextEngine', 'shaka.media.VttTextParser', 'shaka.util.DataViewReader', 'shaka.util.Error', 'shaka.util.Functional', 'shaka.util.Mp4Parser', 'shaka.util.StringUtils', 'shaka.util.TextParser']);
goog.addDependency('../../../lib/media/playhead.js', ['shaka.media.Playhead'], ['goog.asserts', 'shaka.log', 'shaka.media.TimeRangesUtils', 'shaka.util.EventManager', 'shaka.util.FakeEvent', 'shaka.util.IDestroyable', 'shaka.util.StreamUtils']);

@@ -36,11 +34,9 @@ goog.addDependency('../../../lib/media/playhead_observer.js', ['shaka.media.PlayheadObserver'], ['goog.asserts', 'shaka.media.TimeRangesUtils', 'shaka.util.ConfigUtils', 'shaka.util.EventManager', 'shaka.util.FakeEvent', 'shaka.util.IDestroyable', 'shaka.util.StreamUtils']);

goog.addDependency('../../../lib/media/segment_reference.js', ['shaka.media.InitSegmentReference', 'shaka.media.SegmentReference'], ['goog.asserts']);
goog.addDependency('../../../lib/media/streaming_engine.js', ['shaka.media.StreamingEngine'], ['goog.asserts', 'shaka.log', 'shaka.media.MediaSourceEngine', 'shaka.media.Playhead', 'shaka.net.NetworkingEngine', 'shaka.util.Error', 'shaka.util.FakeEvent', 'shaka.util.Functional', 'shaka.util.IDestroyable', 'shaka.util.ManifestParserUtils', 'shaka.util.MapUtils', 'shaka.util.Mp4Parser', 'shaka.util.PublicPromise', 'shaka.util.StreamUtils']);
goog.addDependency('../../../lib/media/text_engine.js', ['shaka.media.TextEngine'], ['goog.asserts', 'shaka.log', 'shaka.util.IDestroyable']);
goog.addDependency('../../../lib/media/streaming_engine.js', ['shaka.media.StreamingEngine'], ['goog.asserts', 'shaka.log', 'shaka.media.MediaSourceEngine', 'shaka.media.Playhead', 'shaka.net.Backoff', 'shaka.net.NetworkingEngine', 'shaka.util.Error', 'shaka.util.FakeEvent', 'shaka.util.Functional', 'shaka.util.IDestroyable', 'shaka.util.ManifestParserUtils', 'shaka.util.MapUtils', 'shaka.util.MimeUtils', 'shaka.util.Mp4Parser', 'shaka.util.PublicPromise', 'shaka.util.StreamUtils']);
goog.addDependency('../../../lib/media/time_ranges_utils.js', ['shaka.media.TimeRangesUtils'], []);
goog.addDependency('../../../lib/media/ttml_text_parser.js', ['shaka.media.TtmlTextParser'], ['goog.asserts', 'shaka.media.TextEngine', 'shaka.util.Error', 'shaka.util.StringUtils']);
goog.addDependency('../../../lib/media/vtt_text_parser.js', ['shaka.media.VttTextParser'], ['goog.asserts', 'shaka.log', 'shaka.media.TextEngine', 'shaka.util.Error', 'shaka.util.StringUtils', 'shaka.util.TextParser']);
goog.addDependency('../../../lib/media/webm_segment_index_parser.js', ['shaka.media.WebmSegmentIndexParser'], ['goog.asserts', 'shaka.log', 'shaka.media.SegmentReference', 'shaka.util.EbmlElement', 'shaka.util.EbmlParser', 'shaka.util.Error']);
goog.addDependency('../../../lib/net/backoff.js', ['shaka.net.Backoff'], ['goog.asserts', 'shaka.util.PublicPromise']);
goog.addDependency('../../../lib/net/data_uri_plugin.js', ['shaka.net.DataUriPlugin'], ['shaka.log', 'shaka.net.NetworkingEngine', 'shaka.util.Error', 'shaka.util.StringUtils', 'shaka.util.Uint8ArrayUtils']);
goog.addDependency('../../../lib/net/http_plugin.js', ['shaka.net.HttpPlugin'], ['goog.asserts', 'shaka.log', 'shaka.net.NetworkingEngine', 'shaka.util.Error', 'shaka.util.StringUtils']);
goog.addDependency('../../../lib/net/networking_engine.js', ['shaka.net.NetworkingEngine'], ['goog.Uri', 'goog.asserts', 'shaka.util.ConfigUtils', 'shaka.util.Error', 'shaka.util.Functional', 'shaka.util.IDestroyable', 'shaka.util.PublicPromise']);
goog.addDependency('../../../lib/net/networking_engine.js', ['shaka.net.NetworkingEngine'], ['goog.Uri', 'goog.asserts', 'shaka.net.Backoff', 'shaka.util.ConfigUtils', 'shaka.util.Error', 'shaka.util.Functional', 'shaka.util.IDestroyable']);
goog.addDependency('../../../lib/offline/db_engine.js', ['shaka.offline.DBEngine'], ['goog.asserts', 'shaka.log', 'shaka.offline.IStorageEngine', 'shaka.util.Error', 'shaka.util.Functional', 'shaka.util.PublicPromise']);

@@ -53,3 +49,3 @@ goog.addDependency('../../../lib/offline/download_manager.js', ['shaka.offline.DownloadManager'], ['goog.asserts', 'shaka.net.NetworkingEngine', 'shaka.offline.OfflineUtils', 'shaka.util.Error', 'shaka.util.IDestroyable', 'shaka.util.MapUtils']);

goog.addDependency('../../../lib/offline/storage.js', ['shaka.offline.Storage'], ['goog.asserts', 'shaka.Player', 'shaka.log', 'shaka.media.DrmEngine', 'shaka.media.ManifestParser', 'shaka.offline.DownloadManager', 'shaka.offline.IStorageEngine', 'shaka.offline.OfflineManifestParser', 'shaka.offline.OfflineUtils', 'shaka.util.ConfigUtils', 'shaka.util.Error', 'shaka.util.Functional', 'shaka.util.IDestroyable', 'shaka.util.LanguageUtils', 'shaka.util.ManifestParserUtils', 'shaka.util.StreamUtils']);
goog.addDependency('../../../lib/player.js', ['shaka.Player'], ['goog.asserts', 'shaka.abr.EwmaBandwidthEstimator', 'shaka.abr.SimpleAbrManager', 'shaka.log', 'shaka.media.DrmEngine', 'shaka.media.ManifestParser', 'shaka.media.MediaSourceEngine', 'shaka.media.Playhead', 'shaka.media.PlayheadObserver', 'shaka.media.SegmentReference', 'shaka.media.StreamingEngine', 'shaka.net.NetworkingEngine', 'shaka.util.CancelableChain', 'shaka.util.ConfigUtils', 'shaka.util.Error', 'shaka.util.EventManager', 'shaka.util.FakeEvent', 'shaka.util.FakeEventTarget', 'shaka.util.Functional', 'shaka.util.IDestroyable', 'shaka.util.ManifestParserUtils', 'shaka.util.MapUtils', 'shaka.util.PublicPromise', 'shaka.util.StreamUtils']);
goog.addDependency('../../../lib/player.js', ['shaka.Player'], ['goog.asserts', 'shaka.abr.SimpleAbrManager', 'shaka.log', 'shaka.media.DrmEngine', 'shaka.media.ManifestParser', 'shaka.media.MediaSourceEngine', 'shaka.media.Playhead', 'shaka.media.PlayheadObserver', 'shaka.media.SegmentReference', 'shaka.media.StreamingEngine', 'shaka.net.NetworkingEngine', 'shaka.text.SimpleTextDisplayer', 'shaka.util.CancelableChain', 'shaka.util.ConfigUtils', 'shaka.util.Error', 'shaka.util.EventManager', 'shaka.util.FakeEvent', 'shaka.util.FakeEventTarget', 'shaka.util.Functional', 'shaka.util.IDestroyable', 'shaka.util.ManifestParserUtils', 'shaka.util.MapUtils', 'shaka.util.PublicPromise', 'shaka.util.StreamUtils']);
goog.addDependency('../../../lib/polyfill/all.js', ['shaka.polyfill.installAll', 'shaka.polyfill.register'], []);

@@ -69,2 +65,9 @@ goog.addDependency('../../../lib/polyfill/fullscreen.js', ['shaka.polyfill.Fullscreen'], ['shaka.polyfill.register']);

goog.addDependency('../../../lib/polyfill/vttcue.js', ['shaka.polyfill.VTTCue'], ['shaka.log', 'shaka.polyfill.register']);
goog.addDependency('../../../lib/text/cue.js', ['shaka.text.Cue'], []);
goog.addDependency('../../../lib/text/mp4_ttml_parser.js', ['shaka.text.Mp4TtmlParser'], ['shaka.text.TextEngine', 'shaka.text.TtmlTextParser', 'shaka.util.Error', 'shaka.util.Mp4Parser']);
goog.addDependency('../../../lib/text/mp4_vtt_parser.js', ['shaka.text.Mp4VttParser'], ['goog.asserts', 'shaka.log', 'shaka.text.Cue', 'shaka.text.TextEngine', 'shaka.text.VttTextParser', 'shaka.util.DataViewReader', 'shaka.util.Error', 'shaka.util.Functional', 'shaka.util.Mp4Parser', 'shaka.util.StringUtils', 'shaka.util.TextParser']);
goog.addDependency('../../../lib/text/simple_text_displayer.js', ['shaka.text.SimpleTextDisplayer'], ['shaka.log']);
goog.addDependency('../../../lib/text/text_engine.js', ['shaka.text.TextEngine'], ['goog.asserts', 'shaka.log', 'shaka.util.IDestroyable']);
goog.addDependency('../../../lib/text/ttml_text_parser.js', ['shaka.text.TtmlTextParser'], ['goog.asserts', 'shaka.text.Cue', 'shaka.text.TextEngine', 'shaka.util.ArrayUtils', 'shaka.util.Error', 'shaka.util.StringUtils']);
goog.addDependency('../../../lib/text/vtt_text_parser.js', ['shaka.text.VttTextParser'], ['goog.asserts', 'shaka.log', 'shaka.text.Cue', 'shaka.text.TextEngine', 'shaka.util.Error', 'shaka.util.StringUtils', 'shaka.util.TextParser']);
goog.addDependency('../../../lib/util/array_utils.js', ['shaka.util.ArrayUtils'], []);

@@ -84,2 +87,3 @@ goog.addDependency('../../../lib/util/cancelable_chain.js', ['shaka.util.CancelableChain'], ['goog.asserts', 'shaka.util.Error']);

goog.addDependency('../../../lib/util/map_utils.js', ['shaka.util.MapUtils'], []);
goog.addDependency('../../../lib/util/mime_utils.js', ['shaka.util.MimeUtils'], []);
goog.addDependency('../../../lib/util/mp4_parser.js', ['shaka.util.Mp4Parser'], ['goog.asserts', 'shaka.util.DataViewReader']);

@@ -89,3 +93,3 @@ goog.addDependency('../../../lib/util/multi_map.js', ['shaka.util.MultiMap'], []);

goog.addDependency('../../../lib/util/public_promise.js', ['shaka.util.PublicPromise'], []);
goog.addDependency('../../../lib/util/stream_utils.js', ['shaka.util.StreamUtils'], ['goog.asserts', 'shaka.log', 'shaka.media.DrmEngine', 'shaka.media.MediaSourceEngine', 'shaka.media.TextEngine', 'shaka.util.ArrayUtils', 'shaka.util.Functional', 'shaka.util.LanguageUtils', 'shaka.util.ManifestParserUtils']);
goog.addDependency('../../../lib/util/stream_utils.js', ['shaka.util.StreamUtils'], ['goog.asserts', 'shaka.log', 'shaka.media.DrmEngine', 'shaka.media.MediaSourceEngine', 'shaka.text.TextEngine', 'shaka.util.ArrayUtils', 'shaka.util.Functional', 'shaka.util.LanguageUtils', 'shaka.util.ManifestParserUtils', 'shaka.util.MimeUtils']);
goog.addDependency('../../../lib/util/string_utils.js', ['shaka.util.StringUtils'], ['shaka.log', 'shaka.util.Error']);

@@ -92,0 +96,0 @@ goog.addDependency('../../../lib/util/text_parser.js', ['shaka.util.TextParser'], ['goog.asserts']);

@@ -15,14 +15,14 @@ /**

/** @const */
shaka.util = {};
shaka.net = {};
/** @const */
shaka.polyfill = {};
/** @const */
shaka.media.TextEngine = {};
shaka.util = {};
/** @const */
shaka.util.StringUtils = {};
/** @const */
shaka.net = {};
/** @const */
shaka.util.Uint8ArrayUtils = {};
/** @const */
shaka.text = {};
/** @const */
shaka.cast = {};

@@ -34,2 +34,6 @@ /** @const */

/** @const */
shaka.text.TextEngine = {};
/** @const */
shaka.text.TextEngine.prototype = {};
/** @const */
shaka.media.ManifestParser = {};

@@ -53,15 +57,11 @@

*/
shaka.media.TextEngine.registerParser = function(mimeType, plugin) {};
shaka.text.TextEngine.registerParser = function(mimeType, plugin) {};
/**
* @param {string} mimeType
*/
shaka.media.TextEngine.unregisterParser = function(mimeType) {};
shaka.text.TextEngine.unregisterParser = function(mimeType) {};
/**
* Creates a cue using the best platform-specific interface available.
* @param {number} startTime
* @param {number} endTime
* @param {string} payload
* @return {TextTrackCue} or null if the parameters were invalid.
* @param {shakaExtern.TextDisplayer} displayer
*/
shaka.media.TextEngine.makeCue = function(startTime, endTime, payload) {};
shaka.text.TextEngine.prototype.setDisplayer = function(displayer) {};
/**

@@ -95,2 +95,6 @@ * Creates a new Error.

/**
* @type {boolean}
*/
shaka.util.Error.prototype.handled;
/**
* @enum {number}

@@ -178,2 +182,4 @@ */

'HLS_KEYFORMATS_NOT_SUPPORTED': 4026,
'DASH_UNSUPPORTED_XLINK_ACTUATE': 4027,
'DASH_XLINK_DEPTH_LIMIT': 4028,
'HLS_LIVE_CONTENT_NOT_SUPPORTED': 4029,

@@ -210,3 +216,4 @@ 'INVALID_STREAMS_CHOSEN': 5005,

'NO_INIT_DATA_FOR_OFFLINE': 9007,
'LOCAL_PLAYER_INSTANCE_REQUIRED': 9008
'LOCAL_PLAYER_INSTANCE_REQUIRED': 9008,
'CONTENT_UNSUPPORTED_BY_BROWSER': 9009
};

@@ -310,2 +317,7 @@ /**

/**
* Sets the presentation delay.
* @param {number} delay
*/
shaka.media.PresentationTimeline.prototype.setDelay = function(delay) {};
/**
* Gives PresentationTimeline a Stream's segments so it can size and position

@@ -355,2 +367,7 @@ * the segment availability window, and account for missing segment

/**
* Sets the presentation's current segment availability start time.
* @param {number} time
*/
shaka.media.PresentationTimeline.prototype.setAvailabilityStart = function(time) {};
/**
* Gets the presentation's current segment availability end time. Segments

@@ -529,10 +546,6 @@ * starting after this time should be assumed to be unavailable.

* <p>
* After the initial choice (in chooseStreams), this class will call
* switchCallback() when there is a better choice. switchCallback() will not
* be called more than once per
* ({@link shaka.abr.SimpleAbrManager.SWITCH_INTERVAL_MS}).
* After initial choices are made, this class will call switchCallback() when
* there is a better choice. switchCallback() will not be called more than once
* per ({@link shaka.abr.SimpleAbrManager.SWITCH_INTERVAL_MS}).
* </p>
* <p>
* This does not adapt for text streams, it will always select the first one.
* </p>
* @constructor

@@ -554,3 +567,3 @@ * @struct

*/
shaka.abr.SimpleAbrManager.prototype.chooseStreams = function(mediaTypesToUpdate) {};
shaka.abr.SimpleAbrManager.prototype.chooseVariant = function() {};
/**

@@ -575,10 +588,2 @@ * @override

*/
shaka.abr.SimpleAbrManager.prototype.setDefaultEstimate = function(estimate) {};
/**
* @override
*/
shaka.abr.SimpleAbrManager.prototype.setRestrictions = function(restrictions) {};
/**
* @override
*/
shaka.abr.SimpleAbrManager.prototype.setVariants = function(variants) {};

@@ -588,3 +593,3 @@ /**

*/
shaka.abr.SimpleAbrManager.prototype.setTextStreams = function(streams) {};
shaka.abr.SimpleAbrManager.prototype.configure = function(config) {};
/**

@@ -769,2 +774,37 @@ * Registers a manifest parser by file extension.

/**
* <p>
* This defines the default text displayer plugin. An instance of this
* class is used when no custom displayer is given.
* </p>
* <p>
* This class simply converts shaka.text.Cue objects to
* TextTrackCues and feeds them to the browser.
* </p>
* @param {HTMLMediaElement} video
* @constructor
* @struct
* @implements {shakaExtern.TextDisplayer}
*/
shaka.text.SimpleTextDisplayer = function(video) {};
/**
* @override
*/
shaka.text.SimpleTextDisplayer.prototype.remove = function(start, end) {};
/**
* @override
*/
shaka.text.SimpleTextDisplayer.prototype.append = function(cues) {};
/**
* @override
*/
shaka.text.SimpleTextDisplayer.prototype.destroy = function() {};
/**
* @override
*/
shaka.text.SimpleTextDisplayer.prototype.isTextVisible = function() {};
/**
* @override
*/
shaka.text.SimpleTextDisplayer.prototype.setTextVisibility = function(on) {};
/**
* A work-alike for EventTarget. Only DOM elements may be true EventTargets,

@@ -972,18 +1012,2 @@ * but this can be used as a base class to provide event dispatch to non-DOM

/**
* Return a list of variant and text tracks available for the current Period.
* If there are multiple Periods, then you must seek to the Period before
* being able to switch.
* @return {!Array.<shakaExtern.Track>}
* @deprecated Use getVariantTracks() or getTextTracks()
*/
shaka.Player.prototype.getTracks = function() {};
/**
* Select a specific track. For variant tracks, this disables adaptation.
* Note that AdaptationEvents are not fired for manual track selections.
* @param {shakaExtern.Track} track
* @param {boolean=} opt_clearBuffer
* @deprecated Use selectVariantTrack() or selectTextTrack()
*/
shaka.Player.prototype.selectTrack = function(track, opt_clearBuffer) {};
/**
* Return a list of variant tracks available for the current

@@ -1056,2 +1080,7 @@ * Period. If there are multiple Periods, then you must seek to the Period

/**
* Returns the presentation start time as a Date.
* @return {Date}
*/
shaka.Player.prototype.getPresentationStartTimeAsDate = function() {};
/**
* Return playback and adaptation stats.

@@ -1084,2 +1113,7 @@ * @return {shakaExtern.Stats}

/**
* Retry streaming after a failure. Does nothing if not in a failure state.
* @return {boolean} False if unable to retry.
*/
shaka.Player.prototype.retryStreaming = function() {};
/**
* Creates a SegmentIndex.

@@ -1216,3 +1250,5 @@ * @param {!Array.<!shaka.media.SegmentReference>} references The list of

* Provide your own callback to choose the tracks you want to store.
* @param {shakaExtern.OfflineConfiguration} config
* @param {!Object} config This should follow the form of
* {@link shakaExtern.OfflineConfiguration}, but you may omit any field you do
* not wish to change.
*/

@@ -1225,4 +1261,4 @@ shaka.offline.Storage.prototype.configure = function(config) {};

* @param {string} manifestUri The URI of the manifest to store.
* @param {!Object} appMetadata An arbitrary object from the application that
* will be stored along-side the offline content. Use this for any
* @param {!Object=} opt_appMetadata An arbitrary object from the application
* that will be stored along-side the offline content. Use this for any
* application-specific metadata you need associated with the stored content.

@@ -1237,3 +1273,3 @@ * For details on the data types that can be stored here, please refer to

*/
shaka.offline.Storage.prototype.store = function(manifestUri, appMetadata, opt_manifestParserFactory) {};
shaka.offline.Storage.prototype.store = function(manifestUri, opt_appMetadata, opt_manifestParserFactory) {};
/**

@@ -1255,2 +1291,87 @@ * Removes the given stored content.

/**
* Creates a Cue object.
* @param {number} startTime
* @param {number} endTime
* @param {!string} payload
* @constructor
* @struct
*/
shaka.text.Cue = function(startTime, endTime, payload) {};
/**
* @enum {string}
*/
shaka.text.Cue.positionAlign = {
LEFT: 'line-left',
RIGHT: 'line-right',
CENTER: 'center',
AUTO: 'auto'
};
/**
* @enum {string}
*/
shaka.text.Cue.textAlign = {
LEFT: 'left',
RIGHT: 'right',
CENTER: 'center',
START: 'start',
END: 'end'
};
/**
* @enum {string}
*/
shaka.text.Cue.displayAlign = {
BEFORE: 'before',
CENTER: 'center',
AFTER: 'after'
};
/**
* @enum {number}
*/
shaka.text.Cue.writingDirection = {
HORIZONTAL_LEFT_TO_RIGHT: 0,
HORIZONTAL_RIGHT_TO_LEFT: 1,
VERTICAL_LEFT_TO_RIGHT: 2,
VERTICAL_RIGHT_TO_LEFT: 3
};
/**
* @enum {number}
*/
shaka.text.Cue.lineInterpretation = {
LINE_NUMBER: 0,
PERCENTAGE: 1
};
/**
* @enum {string}
*/
shaka.text.Cue.lineAlign = {
CENTER: 'center',
START: 'start',
END: 'end'
};
/**
* In CSS font weight can be a number, where 400 is normal
* and 700 is bold. Use these values for the enum for consistency.
* @enum {number}
*/
shaka.text.Cue.fontWeight = {
NORMAL: 400,
BOLD: 700
};
/**
* @enum {string}
*/
shaka.text.Cue.fontStyle = {
NORMAL: 'normal',
ITALIC: 'italic',
OBLIQUE: 'oblique'
};
/**
* @enum {string}
*/
shaka.text.Cue.textDecoration = {
UNDERLINE: 'underline',
LINE_THROUGH: 'lineThrough',
OVERLINE: 'overline'
};
/**
* A proxy to switch between local and remote playback for Chromecast in a way

@@ -1257,0 +1378,0 @@ * that is transparent to the app's controls.

@@ -15,14 +15,14 @@ /**

/** @const */
shaka.util = {};
shaka.net = {};
/** @const */
shaka.polyfill = {};
/** @const */
shaka.media.TextEngine = {};
shaka.util = {};
/** @const */
shaka.util.StringUtils = {};
/** @const */
shaka.net = {};
/** @const */
shaka.util.Uint8ArrayUtils = {};
/** @const */
shaka.text = {};
/** @const */
shaka.cast = {};

@@ -34,2 +34,6 @@ /** @const */

/** @const */
shaka.text.TextEngine = {};
/** @const */
shaka.text.TextEngine.prototype = {};
/** @const */
shaka.media.ManifestParser = {};

@@ -53,15 +57,11 @@

*/
shaka.media.TextEngine.registerParser = function(mimeType, plugin) {};
shaka.text.TextEngine.registerParser = function(mimeType, plugin) {};
/**
* @param {string} mimeType
*/
shaka.media.TextEngine.unregisterParser = function(mimeType) {};
shaka.text.TextEngine.unregisterParser = function(mimeType) {};
/**
* Creates a cue using the best platform-specific interface available.
* @param {number} startTime
* @param {number} endTime
* @param {string} payload
* @return {TextTrackCue} or null if the parameters were invalid.
* @param {shakaExtern.TextDisplayer} displayer
*/
shaka.media.TextEngine.makeCue = function(startTime, endTime, payload) {};
shaka.text.TextEngine.prototype.setDisplayer = function(displayer) {};
/**

@@ -95,2 +95,6 @@ * Creates a new Error.

/**
* @type {boolean}
*/
shaka.util.Error.prototype.handled;
/**
* @enum {number}

@@ -178,2 +182,4 @@ */

'HLS_KEYFORMATS_NOT_SUPPORTED': 4026,
'DASH_UNSUPPORTED_XLINK_ACTUATE': 4027,
'DASH_XLINK_DEPTH_LIMIT': 4028,
'HLS_LIVE_CONTENT_NOT_SUPPORTED': 4029,

@@ -210,3 +216,4 @@ 'INVALID_STREAMS_CHOSEN': 5005,

'NO_INIT_DATA_FOR_OFFLINE': 9007,
'LOCAL_PLAYER_INSTANCE_REQUIRED': 9008
'LOCAL_PLAYER_INSTANCE_REQUIRED': 9008,
'CONTENT_UNSUPPORTED_BY_BROWSER': 9009
};

@@ -310,2 +317,7 @@ /**

/**
* Sets the presentation delay.
* @param {number} delay
*/
shaka.media.PresentationTimeline.prototype.setDelay = function(delay) {};
/**
* Gives PresentationTimeline a Stream's segments so it can size and position

@@ -355,2 +367,7 @@ * the segment availability window, and account for missing segment

/**
* Sets the presentation's current segment availability start time.
* @param {number} time
*/
shaka.media.PresentationTimeline.prototype.setAvailabilityStart = function(time) {};
/**
* Gets the presentation's current segment availability end time. Segments

@@ -529,10 +546,6 @@ * starting after this time should be assumed to be unavailable.

* <p>
* After the initial choice (in chooseStreams), this class will call
* switchCallback() when there is a better choice. switchCallback() will not
* be called more than once per
* ({@link shaka.abr.SimpleAbrManager.SWITCH_INTERVAL_MS}).
* After initial choices are made, this class will call switchCallback() when
* there is a better choice. switchCallback() will not be called more than once
* per ({@link shaka.abr.SimpleAbrManager.SWITCH_INTERVAL_MS}).
* </p>
* <p>
* This does not adapt for text streams, it will always select the first one.
* </p>
* @constructor

@@ -554,3 +567,3 @@ * @struct

*/
shaka.abr.SimpleAbrManager.prototype.chooseStreams = function(mediaTypesToUpdate) {};
shaka.abr.SimpleAbrManager.prototype.chooseVariant = function() {};
/**

@@ -575,10 +588,2 @@ * @override

*/
shaka.abr.SimpleAbrManager.prototype.setDefaultEstimate = function(estimate) {};
/**
* @override
*/
shaka.abr.SimpleAbrManager.prototype.setRestrictions = function(restrictions) {};
/**
* @override
*/
shaka.abr.SimpleAbrManager.prototype.setVariants = function(variants) {};

@@ -588,3 +593,3 @@ /**

*/
shaka.abr.SimpleAbrManager.prototype.setTextStreams = function(streams) {};
shaka.abr.SimpleAbrManager.prototype.configure = function(config) {};
/**

@@ -769,2 +774,37 @@ * Registers a manifest parser by file extension.

/**
* <p>
* This defines the default text displayer plugin. An instance of this
* class is used when no custom displayer is given.
* </p>
* <p>
* This class simply converts shaka.text.Cue objects to
* TextTrackCues and feeds them to the browser.
* </p>
* @param {HTMLMediaElement} video
* @constructor
* @struct
* @implements {shakaExtern.TextDisplayer}
*/
shaka.text.SimpleTextDisplayer = function(video) {};
/**
* @override
*/
shaka.text.SimpleTextDisplayer.prototype.remove = function(start, end) {};
/**
* @override
*/
shaka.text.SimpleTextDisplayer.prototype.append = function(cues) {};
/**
* @override
*/
shaka.text.SimpleTextDisplayer.prototype.destroy = function() {};
/**
* @override
*/
shaka.text.SimpleTextDisplayer.prototype.isTextVisible = function() {};
/**
* @override
*/
shaka.text.SimpleTextDisplayer.prototype.setTextVisibility = function(on) {};
/**
* A work-alike for EventTarget. Only DOM elements may be true EventTargets,

@@ -972,18 +1012,2 @@ * but this can be used as a base class to provide event dispatch to non-DOM

/**
* Return a list of variant and text tracks available for the current Period.
* If there are multiple Periods, then you must seek to the Period before
* being able to switch.
* @return {!Array.<shakaExtern.Track>}
* @deprecated Use getVariantTracks() or getTextTracks()
*/
shaka.Player.prototype.getTracks = function() {};
/**
* Select a specific track. For variant tracks, this disables adaptation.
* Note that AdaptationEvents are not fired for manual track selections.
* @param {shakaExtern.Track} track
* @param {boolean=} opt_clearBuffer
* @deprecated Use selectVariantTrack() or selectTextTrack()
*/
shaka.Player.prototype.selectTrack = function(track, opt_clearBuffer) {};
/**
* Return a list of variant tracks available for the current

@@ -1056,2 +1080,7 @@ * Period. If there are multiple Periods, then you must seek to the Period

/**
* Returns the presentation start time as a Date.
* @return {Date}
*/
shaka.Player.prototype.getPresentationStartTimeAsDate = function() {};
/**
* Return playback and adaptation stats.

@@ -1084,2 +1113,7 @@ * @return {shakaExtern.Stats}

/**
* Retry streaming after a failure. Does nothing if not in a failure state.
* @return {boolean} False if unable to retry.
*/
shaka.Player.prototype.retryStreaming = function() {};
/**
* Creates a SegmentIndex.

@@ -1216,3 +1250,5 @@ * @param {!Array.<!shaka.media.SegmentReference>} references The list of

* Provide your own callback to choose the tracks you want to store.
* @param {shakaExtern.OfflineConfiguration} config
* @param {!Object} config This should follow the form of
* {@link shakaExtern.OfflineConfiguration}, but you may omit any field you do
* not wish to change.
*/

@@ -1225,4 +1261,4 @@ shaka.offline.Storage.prototype.configure = function(config) {};

* @param {string} manifestUri The URI of the manifest to store.
* @param {!Object} appMetadata An arbitrary object from the application that
* will be stored along-side the offline content. Use this for any
* @param {!Object=} opt_appMetadata An arbitrary object from the application
* that will be stored along-side the offline content. Use this for any
* application-specific metadata you need associated with the stored content.

@@ -1237,3 +1273,3 @@ * For details on the data types that can be stored here, please refer to

*/
shaka.offline.Storage.prototype.store = function(manifestUri, appMetadata, opt_manifestParserFactory) {};
shaka.offline.Storage.prototype.store = function(manifestUri, opt_appMetadata, opt_manifestParserFactory) {};
/**

@@ -1255,2 +1291,87 @@ * Removes the given stored content.

/**
* Creates a Cue object.
* @param {number} startTime
* @param {number} endTime
* @param {!string} payload
* @constructor
* @struct
*/
shaka.text.Cue = function(startTime, endTime, payload) {};
/**
* @enum {string}
*/
shaka.text.Cue.positionAlign = {
LEFT: 'line-left',
RIGHT: 'line-right',
CENTER: 'center',
AUTO: 'auto'
};
/**
* @enum {string}
*/
shaka.text.Cue.textAlign = {
LEFT: 'left',
RIGHT: 'right',
CENTER: 'center',
START: 'start',
END: 'end'
};
/**
* @enum {string}
*/
shaka.text.Cue.displayAlign = {
BEFORE: 'before',
CENTER: 'center',
AFTER: 'after'
};
/**
* @enum {number}
*/
shaka.text.Cue.writingDirection = {
HORIZONTAL_LEFT_TO_RIGHT: 0,
HORIZONTAL_RIGHT_TO_LEFT: 1,
VERTICAL_LEFT_TO_RIGHT: 2,
VERTICAL_RIGHT_TO_LEFT: 3
};
/**
* @enum {number}
*/
shaka.text.Cue.lineInterpretation = {
LINE_NUMBER: 0,
PERCENTAGE: 1
};
/**
* @enum {string}
*/
shaka.text.Cue.lineAlign = {
CENTER: 'center',
START: 'start',
END: 'end'
};
/**
* In CSS font weight can be a number, where 400 is normal
* and 700 is bold. Use these values for the enum for consistency.
* @enum {number}
*/
shaka.text.Cue.fontWeight = {
NORMAL: 400,
BOLD: 700
};
/**
* @enum {string}
*/
shaka.text.Cue.fontStyle = {
NORMAL: 'normal',
ITALIC: 'italic',
OBLIQUE: 'oblique'
};
/**
* @enum {string}
*/
shaka.text.Cue.textDecoration = {
UNDERLINE: 'underline',
LINE_THROUGH: 'lineThrough',
OVERLINE: 'overline'
};
/**
* A proxy to switch between local and remote playback for Chromecast in a way

@@ -1257,0 +1378,0 @@ * that is transparent to the app's controls.

@@ -55,4 +55,4 @@ # Configuration

bufferingGoal: 10
failureCallback: Function
ignoreTextStreamFailures: false
infiniteRetriesForLiveStreams: true
jumpLargeGaps: false

@@ -59,0 +59,0 @@ rebufferingGoal: 2

@@ -86,5 +86,5 @@ # Frequently Asked Questions

**A:** If your HLS manifest describes MPEG2-TS content, the only browsers
capable of playing it are Edge, Chromecast and Safari. You will get an
`UNPLAYABLE_PERIOD` error on other browsers due to their lack of TS support.
We are planning to implement transmuxing TS files to fMP4 so they're
capable of playing it are Edge, Chromecast and Safari. You will get a
`CONTENT_UNSUPPORTED_BY_BROWSER` error on other browsers due to their lack of TS
support. We are planning to implement transmuxing TS files to fMP4 so they're
supported across all browsers. Please subscibe to issue [#887][887] to

@@ -96,4 +96,4 @@ get updates on the progress.

Please file an issue if your TS content isn't playing in Chromecast or Edge
and your MP4 content - on any browser.
Please file an issue if your TS content fails in Chromecast or Edge or if your
MP4 content fails anywhere.

@@ -100,0 +100,0 @@ <hr>

@@ -18,6 +18,7 @@ [

"children": {
"upgrade-v1": { "title": "Upgrade Guide, v1 => v2.1" },
"upgrade-v2-0": { "title": "Upgrade Guide, v2.0 => v2.1" }
"upgrade-v1": { "title": "Upgrade Guide, v1 => v2" },
"upgrade-v2-0": { "title": "Upgrade Guide, v2.0 => v2.2" },
"upgrade-v2-1": { "title": "Upgrade Guide, v2.1 => v2.2" }
}
} }
]

@@ -42,2 +42,9 @@ # Plugins and Customizing the Build

__Subtitle/caption displayers__
- Configured at runtime on a Player instance
- Use {@link player.configure} and set the `textDisplayFactory` field
- Must implement the {@link shakaExtern.TextDisplayer} interface
- Default TextDisplayer implementation:
{@linksource shaka.text.SimpleTextDisplayer}
__Networking plugins__

@@ -52,3 +59,3 @@ - Selected by URI scheme (http, https, etc.)

- Configured at runtime on a Player instance
- Use {@link player.configure} and set the `abr.manager` field
- Use {@link player.configure} and set the `abrFactory` field
- Must implement the {@link shakaExtern.AbrManager} interface

@@ -55,0 +62,0 @@ - Default AbrManager implementation: {@linksource shaka.abr.SimpleAbrManager}

@@ -26,2 +26,12 @@ # Shaka Upgrade Guide, v1.x => v2

- Simpler, mobile-friendly demo app
- Basic HLS support
- DASH trick mode support
- Support for jumping gaps in the timeline
- Additional stats and events from Player
- Indication of critical errors vs recoverable errors
- Allowing applications to render their own text tracks
- Making the default ABR manager more configurable
- Adding channel count and bandwidth info to variant tracks
- Xlink support in DASH
- New option for offline protected content without persistent licensing

@@ -351,7 +361,4 @@

var player = new shaka.Player(video);
var customAbrManager = new MyCustomAbrManager();
player.configure({
abr: {
manager: customAbrManager
}
abrFactory: MyCustomAbrManager
});

@@ -541,5 +548,6 @@ player.load(manifestUri);

timestamp: number // seconds, when the stream was selected
id: number // stream ID
id: number // track ID
type: string // 'variant' or 'text'
fromAdaptation: boolean // distinguishes between ABR and manual choices
bandwidth: ?number // track's bandwidth (null for text tracks)
stateChange: Array of Objects

@@ -546,0 +554,0 @@ timestamp: number // seconds, when the state changed

@@ -1,4 +0,4 @@

# Shaka Upgrade Guide, v2.0 => v2.1
# Shaka Upgrade Guide, v2.0 => v2.2
This is a detailed guide for upgrading from Shaka Player v2.0 to v2.1.
This is a detailed guide for upgrading from Shaka Player v2.0 to v2.2.
Feel free to skim or to search for the class and method names you are using in

@@ -8,5 +8,5 @@ your application.

#### What's New in v2.1?
#### What's New in v2.1 and v2.2?
Shaka v2.1 introduces several improvements over v2.0, including:
Shaka v2.2 introduces several improvements over v2.0, including:
- Basic HLS support

@@ -18,2 +18,10 @@ - DASH trick mode support

- Indication of critical errors vs recoverable errors
- Allowing applications to render their own text tracks
- Allowing applications to define their own retry logic after streaming
failures
- Making the default ABR manager more configurable
- Adding channel count and bandwidth info to variant tracks
- Xlink support in DASH
- Stricter runtime type-checking of EME cert configuration
- New option for offline protected content without persistent licensing

@@ -35,3 +43,3 @@

In Shaka v2.1, audio and video tracks are combined into a variant track. It is
In Shaka v2.2, audio and video tracks are combined into a variant track. It is
not possible to select individual audio/video streams, you can only select a

@@ -46,3 +54,3 @@ specific variant as specified by the manifest. This was necessary for us to

```js
// v2.1:
// v2.2:
var variantTracks = player.getVariantTracks();

@@ -53,8 +61,2 @@ var i = /* choose an index somehow */;

The v2.0 methods `getTracks()` and `selectTrack()` are still present in v2.1,
but they are deprecated and will be removed in v2.2. However, they are not
completely backward compatible because of the `type` field. If you are looking
for `'video'` or `'audio'` in the `type` field, your application will need to
be updated to handle `'variant'` instead.
See also the {@link shakaExtern.Track} structure which is used for all track

@@ -64,2 +66,74 @@ types (variant and text).

#### Setting and configuring ABR manager
In Shaka v2.0, a custom ABR manager could be set through:
```js
player.configure({
abr.manager: customAbrManager
});
```
In v2.2, it's done through:
```js
player.configure({
abrFactory: customAbrManager
});
```
The API for AbrManager has also changed.
In v2.0, default bandwidth estimate and restrictions were set through
`setDefaultEstimate()` and `setRestrictions()` methods.
In v2.2, they are set through `configure()` method which accepts a
{@link shakaExtern.AbrConfiguration} structure. The new method is more general,
and allows for the configuration of bandwidth upgrade and downgrade targets
as well.
```js
// v2.0:
abrManager.setDefaultEstimate(defaultBandwidthEstimate);
abrManager.setRestrictions(restrictions);
// v2.2:
abrManager.configure(abrConfigurations);
```
In v2.0, AbrManager had a `chooseStreams()` method for the player to prompt for
a stream selection, and a `switch()` callback to send unsolicited changes from
AbrManager to player. In v2.2, `chooseStreams()` has been replaced with
`chooseVariant()`, and the `switch()` callback takes a variant instead of a map
of streams.
```js
// v2.0:
var map = abrManager.chooseStreams(['audio', 'video']);
console.log(map['video'], map['audio']);
MyAbrManager.prototype.makeDecision_ = function() {
var video = this.computeBestVideo_(this.bandwidth_);
var audio = this.computeBestAudio_(this.bandwidth_);
var map = {
'audio': audio,
'video': video
};
this.switch_(map);
};
// v2.2:
var variant = abrManager.chooseVariant();
console.log(variant, variant.video, variant.audio);
MyAbrManager.prototype.makeDecision_ = function() {
var variant = this.computeBestVariant_(this.bandwidth_);
this.switch_(variant);
};
```
In v2.2, the v2.0 interfaces are still supported, but are deprecated. Support
will be removed in v2.3.
#### Selecting tracks and adaptation settings

@@ -78,6 +152,6 @@

In v2.1, any change in ABR state must be made explicitly if desired.
In v2.2, any change in ABR state must be made explicitly if desired.
```js
// v2.1
// v2.2
// To explicitly disable:

@@ -103,3 +177,3 @@ player.configure({abr: {enabled: false}});

In Shaka v2.1, language selection during playback is explicit and separate from
In Shaka v2.2, language selection during playback is explicit and separate from
the configuration. Configuration only affects the next call to `load()`, and

@@ -113,3 +187,3 @@ will not change languages during playback.

```js
// v2.1:
// v2.2:
player.configure({ preferredAudioLanguage: 'fr-CA' });

@@ -150,4 +224,3 @@ player.load(manifestUri); // Canadian French preferred for initial playback

In v2.1, the v2.0 interfaces for text and manifest parsers are still supported,
but are deprecated. Support will be removed in v2.2.
The v2.0 interfaces for text and manifest are no longer supported.

@@ -180,3 +253,3 @@

while (parserState.more()) {
cues.push(parserState.nextCueOrThrow(periodOffset));
cues.push(new VTTCue(...));
}

@@ -187,9 +260,11 @@ return cues;

In Shaka v2.1, the text parser interface is now a constructor. The interface
In Shaka v2.2, the text parser interface is now a constructor. The interface
now has explicit methods for init segments and media segments, and parameters
related to time offsets have been grouped together into one `TimeContext`
parameter.
Also text parser plugins now return shaka.text.Cue objects instead of VTTCue or
TextTrackCue objects.
```js
// v2.1
// v2.2
/** @constructor */

@@ -203,6 +278,7 @@ function MyTextParser() {}

/**
* @param {!ArrayBuffer} data
* @param {shakaExtern.TextParser.TimeContext} timeContext
* @return {!Array.<!TextTrackCue>}
* @return {!Array.<!shaka.text.Cue>}
*/

@@ -213,3 +289,3 @@ MyTextParser.prototype.parseMedia = function(data, timeContext) {

while (parserState.more()) {
cues.push(parserState.nextCueOrThrow(timeContext.periodStart));
cues.push(new shaka.text.Cue(...));
}

@@ -220,6 +296,31 @@ return cues;

For more information, see the {@link shakaExtern.TextParser.TimeContext} and
{@link shakaExtern.TextParser} definitions in the API docs.
All application-specific text-parsing plugins MUST to be updated,
v2.2 does not have backward compatibility on this!
The `Shaka.text.Cue` class contains the same information about a text cue as
the VTTCue class, plus extra information about text style.
For more information, see the {@link shakaExtern.TextParser.TimeContext},
{@link shaka.text.Cue} and {@link shakaExtern.TextParser} definitions in
the API docs.
#### Customizing subtitle display
Shaka v2 gave applications an opportunity to have a custom text parser, but
all the displaying was handled by the browser. Shaka v2.2 adds the
possibility to have custom logic for displaying text. By default the
rendering will still be done by the {@linksource shaka.text.SimpleTextDisplayer}
class.
A custom text display factory can be specified by calling player.configure().
```js
player.configure({
textDisplayFactory: customTextDisplayerClass
});
```
See {@linksource shakaExtern.TextDisplayer} for details.
#### Manifest parser plugin changes

@@ -299,3 +400,3 @@

Shaka v2.1 also adds two new methods to the manifest parser interface:
Shaka v2.2 also adds two new methods to the manifest parser interface:
`update()` and `onExpirationUpdated()`.

@@ -308,3 +409,3 @@

```js
// v2.1
// v2.2
MyManifestParser.prototype.update = function() {

@@ -322,3 +423,3 @@ // Trigger an update now!

```js
// v2.1
// v2.2
MyManifestParser.prototype.onExpirationUpdated =

@@ -334,1 +435,66 @@ function(sessionId, expiration) {

and {@link shakaExtern.ManifestParser} definitions in the API docs.
#### Retry after streaming failure
In v2.0, after a network error and all network retries were exhausted, streaming
would continue to retry those requests. The only way to stop this process was
to `unload()` or `destroy()` the Player.
In v2.2, we introduced new retry behavior. The default is as it was in v2.1.3
(retry on live, but not VOD), but applications can now customize the behavior
through a callback:
```js
player.configure({
streaming: {
failureCallback: function(error) {
// Always retry, as in v2.0.0 - v2.1.2:
player.retryStreaming();
}
}
});
```
The new `player.retryStreaming()` method can be used to retry after a failure.
You can base the decision on `player.isLive()`, `error.code`, or anything else.
Because you can call `retryStreaming()` at any time, you can also delay the
decision until you get feedback from the user, the browser is back online, etc.
A few more examples of possible failure callbacks:
```js
function neverRetryCallback(error) {}
function retryLiveOnFailureCallback(error) {
if (player.isLive()) {
player.retryStreaming();
}
}
function retryOnSpecificHttpErrorsCallback(error) {
if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS) {
var statusCode = error.data[1];
var retryCodes = [ 502, 503, 504, 520 ];
if (retryCodes.indexOf(statusCode >= 0)) {
player.retryStreaming();
}
}
}
```
If you choose to react to `error` events instead of the failure callback, you
can use `event.preventDefault()` to avoid the callback completely:
```js
player.addEventListener('error', function(event) {
// Custom logic for error events
if (player.isLive() &&
event.error.code == shaka.util.Error.Code.BAD_HTTP_STATUS) {
player.retryStreaming();
}
// Do not invoke the failure callback for this event
event.preventDefault();
});
```

@@ -6,1 +6,3 @@ # Shaka Player Upgrade Guide

If you are upgrading from v2.0, please see {@tutorial upgrade-v2-0}.
If you are upgrading from v2.1, please see {@tutorial upgrade-v2-1}.

@@ -42,6 +42,6 @@ /**

/**
* A callback from the Player that should be called when the AbrManager decides
* it's time to change to a different set of streams.
* A callback into the Player that should be called when the AbrManager decides
* it's time to change to a different variant.
*
* The first argument is a map of content types to chosen streams.
* The first argument is a variant to switch to.
*

@@ -51,3 +51,3 @@ * The second argument is an optional boolean. If true, all data will be

*
* @typedef {function(!Object.<string, !shakaExtern.Stream>, boolean=)}
* @typedef {function(shakaExtern.Variant, boolean=)}
* @exportDoc

@@ -59,2 +59,11 @@ */

/**
* A factory for creating the abr manager. This will be called with 'new'.
*
* @typedef {function(new:shakaExtern.AbrManager)}
* @exportDoc
*/
shakaExtern.AbrManager.Factory;
/**
* Initializes the AbrManager.

@@ -87,30 +96,15 @@ *

/**
* Updates manager's text streams collection.
*
* @param {!Array.<!shakaExtern.Stream>} streams
* Chooses one variant to switch to. Called by the Player.
* @return {shakaExtern.Variant}
* @exportDoc
*/
shakaExtern.AbrManager.prototype.setTextStreams = function(streams) {};
shakaExtern.AbrManager.prototype.chooseVariant = function() {};
/**
* Chooses one Stream from each media type in mediaTypesToUpdate to switch to.
* All Variants and Streams must be from the same Period.
* Enables automatic Variant choices from the last ones passed to setVariants.
* After this, the AbrManager may call switchCallback() at any time.
*
* @param {!Array.<!string>} mediaTypesToUpdate
* @return {!Object.<string, shakaExtern.Stream>}
* @exportDoc
*/
// TODO: Consider breaking down into chooseVariant() and chooseText()
shakaExtern.AbrManager.prototype.chooseStreams =
function(mediaTypesToUpdate) {};
/**
* Enables automatic Stream choices from the last StreamSets passed to
* chooseStreams(). After this, the AbrManager may call switchCallback() at any
* time.
*
* @exportDoc
*/
shakaExtern.AbrManager.prototype.enable = function() {};

@@ -152,16 +146,7 @@

/**
* Sets the default bandwidth estimate to use if there is not enough data.
* Sets the abr configurations.
*
* @param {number} estimate The default bandwidth estimate, in bit/sec.
* @param {shakaExtern.AbrConfiguration} config
* @exportDoc
*/
shakaExtern.AbrManager.prototype.setDefaultEstimate = function(estimate) {};
/**
* Sets the restrictions that AbrManager will use when choosing streams.
*
* @param {shakaExtern.Restrictions} restrictions
* @exportDoc
*/
shakaExtern.AbrManager.prototype.setRestrictions = function(restrictions) {};
shakaExtern.AbrManager.prototype.configure = function(config) {};

@@ -41,3 +41,4 @@ /**

* networkingEngine: !shaka.net.NetworkingEngine,
* filterPeriod: function(shakaExtern.Period),
* filterNewPeriod: function(shakaExtern.Period),
* filterAllPeriods: function(!Array.<!shakaExtern.Period>),
* onTimelineRegionAdded: function(shakaExtern.TimelineRegionInfo),

@@ -56,4 +57,6 @@ * onEvent: function(!Event),

* The networking engine to use for network requests.
* @property {function(shakaExtern.Period)} filterPeriod
* Should be called on all new Periods so that they can be filtered.
* @property {function(shakaExtern.Period)} filterNewPeriod
* Should be called on a new Period so that it can be filtered.
* @property {function(!Array.<!shakaExtern.Period>)} filterAllPeriods
* Should be called on all Periods so that they can be filtered.
* @property {function(shakaExtern.TimelineRegionInfo)} onTimelineRegionAdded

@@ -60,0 +63,0 @@ * Should be called when a new timeline region is added.

@@ -305,3 +305,4 @@ /**

* containsEmsgBoxes: boolean,
* roles: !Array.<string>
* roles: !Array.<string>,
* channelsCount: ?number
* }}

@@ -397,4 +398,6 @@ *

* e.g. 'main', 'caption', or 'commentary'.
* @property {?number} channelsCount
* The channel count information for the audio stream.
* @exportDoc
*/
shakaExtern.Stream;

@@ -41,3 +41,4 @@ /**

* function(!Array.<shakaExtern.Track>):!Array.<shakaExtern.Track>,
* progressCallback: function(shakaExtern.StoredContent,number)
* progressCallback: function(shakaExtern.StoredContent,number),
* usePersistentLicense: boolean
* }}

@@ -54,2 +55,8 @@ *

* the current manifest being stored and the progress of it being stored.
* @property {boolean} usePersistentLicense
* If true, store protected content with a persistent license so that no
* network is required to view.
* If false, store protected content without a persistent license. A network
* will be required to retrieve a temporary license to view.
* Defaults to true.
* @exportDoc

@@ -56,0 +63,0 @@ */

@@ -27,3 +27,4 @@ /**

* type: string,
* fromAdaptation: boolean
* fromAdaptation: boolean,
* bandwidth: ?number
* }}

@@ -37,9 +38,11 @@ *

* @property {string} type
* The type of stream chosen ('variant' or 'text')
* The type of track chosen ('variant' or 'text')
* @property {boolean} fromAdaptation
* True if the choice was made by AbrManager for adaptation; false if it
* was made by the application through selectTrack.
* @property {?number} bandwidth
* The bandwidth of the chosen track (null for text).
* @exportDoc
*/
shakaExtern.StreamChoice;
shakaExtern.TrackChoice;

@@ -83,3 +86,3 @@

*
* switchHistory: !Array.<shakaExtern.StreamChoice>,
* switchHistory: !Array.<shakaExtern.TrackChoice>,
* stateHistory: !Array.<shakaExtern.StateChange>

@@ -118,3 +121,3 @@ * }}

*
* @property {!Array.<shakaExtern.StreamChoice>} switchHistory
* @property {!Array.<shakaExtern.TrackChoice>} switchHistory
* A history of the stream changes.

@@ -149,3 +152,6 @@ * @property {!Array.<shakaExtern.StateChange>} stateHistory

* videoId: ?number,
* audioId: ?number
* audioId: ?number,
* channelsCount: ?number,
* audioBandwidth: ?number,
* videoBandwidth: ?number
* }}

@@ -190,3 +196,3 @@ *

* The audio codecs string provided in the manifest, if present.
* @property {?string} videoCodec
* @property {?string} videoCodec
* The video codecs string provided in the manifest, if present.

@@ -205,2 +211,8 @@ * @property {boolean} primary

* (only for variant tracks) The audio stream id.
* @property {?number} channelsCount
* The count of the audio track channels.
* @property {?number} audioBandwidth
* (only for variant tracks) The audio stream's bandwidth if known.
* @property {?number} videoBandwidth
* (only for variant tracks) The video stream's bandwidth if known.
* @exportDoc

@@ -394,4 +406,6 @@ */

* @property {Uint8Array} serverCertificate
* <i>Defaults to null, i.e., certificate will be requested from the license
* server if required.</i> <br>
* <i>Defaults to null.</i> <br>
* <i>An empty certificate (byteLength 0) will be treated as null.</i> <br>
* <i>A certificate will be requested from the license server if
* required.</i> <br>
* A key-system-specific server certificate used to encrypt license requests.

@@ -442,3 +456,4 @@ * Its use is optional and is meant as an optimization to avoid a round-trip

* clockSyncUri: string,
* ignoreDrmInfo: boolean
* ignoreDrmInfo: boolean,
* xlinkFailGracefully: boolean
* }}

@@ -458,2 +473,7 @@ *

* system and contained no init data. Defaults to false if not provided.
* @property {boolean} xlinkFailGracefully
* If true, xlink-related errors will result in a fallback to the tag's
* existing contents. If false, xlink-related errors will be propagated
* to the application and will result in a playback failure. Defaults to
* false if not provided.
*

@@ -502,3 +522,3 @@ * @exportDoc

* retryParameters: shakaExtern.RetryParameters,
* infiniteRetriesForLiveStreams: boolean,
* failureCallback: function(!shaka.util.Error),
* rebufferingGoal: number,

@@ -518,5 +538,5 @@ * bufferingGoal: number,

* Retry parameters for segment requests.
* @property {boolean} infiniteRetriesForLiveStreams
* If true, will retry infinitely on network errors, for live streams only.
* Defaults to true.
* @property {function(!shaka.util.Error)} failureCallback
* A callback to decide what to do on a streaming failure. Default behavior
* is to retry on live streams and not on VOD.
* @property {number} rebufferingGoal

@@ -559,10 +579,10 @@ * The minimum number of seconds of content that the StreamingEngine must

* @typedef {{
* manager: shakaExtern.AbrManager,
* enabled: boolean,
* defaultBandwidthEstimate: number,
* restrictions: shakaExtern.Restrictions
* restrictions: shakaExtern.Restrictions,
* switchInterval: number,
* bandwidthUpgradeTarget: number,
* bandwidthDowngradeTarget: number
* }}
*
* @property {shakaExtern.AbrManager} manager
* The AbrManager instance.
* @property {boolean} enabled

@@ -577,2 +597,11 @@ * If true, enable adaptation by the current AbrManager. Defaults to true.

* they can still be chosen by the application)
* @property {number} switchInterval
* The minimum amount of time that must pass between switches, in
* seconds. This keeps us from changing too often and annoying the user.
* @property {number} bandwidthUpgradeTarget
* The fraction of the estimated bandwidth which we should try to use when
* upgrading.
* @property {number} bandwidthDowngradeTarget
* The largest fraction of the estimated bandwidth we should use. We should
* downgrade to avoid this.
* @exportDoc

@@ -588,6 +617,10 @@ */

* streaming: shakaExtern.StreamingConfiguration,
* abrFactory: shakaExtern.AbrManager.Factory,
* abr: shakaExtern.AbrConfiguration,
* preferredAudioLanguage: string,
* preferredTextLanguage: string,
* restrictions: shakaExtern.Restrictions
* restrictions: shakaExtern.Restrictions,
* playRangeStart: number,
* playRangeEnd: number,
* textDisplayFactory: shakaExtern.TextDisplayer.Factory
* }}

@@ -601,2 +634,4 @@ *

* Streaming configuration and settings.
* @property {shakaExtern.AbrManager.Factory} abrFactory
* A factory to construct an abr manager.
* @property {shakaExtern.AbrConfiguration} abr

@@ -616,4 +651,12 @@ * ABR configuration and settings.

* meet all the restrictions to be playable.
* @property {number} playRangeStart
* Optional playback and seek start time in seconds. Defaults to 0 if
* not provided.
* @property {number} playRangeEnd
* Optional playback and seek end time in seconds. Defaults to the end of
* the presentation if not provided.
* @property {shakaExtern.TextDisplayer.Factory} textDisplayFactory
* A factory to construct text displayer.
* @exportDoc
*/
shakaExtern.PlayerConfiguration;

@@ -73,3 +73,3 @@ /**

* for each cue.
* @return {!Array.<!TextTrackCue>}
* @return {!Array.<!shaka.text.Cue>}
*

@@ -85,1 +85,64 @@ * @exportDoc

shakaExtern.TextParserPlugin;
/**
* An interface for plugins that display text.
*
* @interface
* @extends {shaka.util.IDestroyable}
* @exportDoc
*/
shakaExtern.TextDisplayer = function() {};
/**
* Append given text cues to the list of cues to be displayed.
*
* @param {!Array.<!shaka.text.Cue>} cues
* Text cues to be appended.
*
* @exportDoc
*/
shakaExtern.TextDisplayer.prototype.append = function(cues) {};
/**
* Remove cues in a given time range.
*
* @param {number} start
* @param {number} end
* @return {boolean}
*
* @exportDoc
*/
shakaExtern.TextDisplayer.prototype.remove = function(start, end) {};
/**
* Returns true if text is currently visible.
*
* @return {boolean}
*
* @exportDoc
*/
shakaExtern.TextDisplayer.prototype.isTextVisible = function() {};
/**
* Set text visibility.
*
* @param {boolean} on
*
* @exportDoc
*/
shakaExtern.TextDisplayer.prototype.setTextVisibility = function(on) {};
/**
* A factory for creating a TextDisplayer.
*
* @typedef {function(new:shakaExtern.TextDisplayer)}
* @exportDoc
*/
shakaExtern.TextDisplayer.Factory;

@@ -21,3 +21,41 @@ /**

var fs = require('fs');
var path = require('path');
var rimraf = require('rimraf');
var which = require('which');
module.exports = function(config) {
var SHAKA_LOG_MAP = {
none: 0,
error: 1,
warning: 2,
info: 3,
debug: 4,
v1: 5,
v2: 6
};
var KARMA_LOG_MAP = {
'disable': config.LOG_DISABLE,
'error': config.LOG_ERROR,
'warn': config.LOG_WARN,
'info': config.LOG_INFO,
'debug': config.LOG_DEBUG
};
// Find the settings JSON object in the command arguments
var args = process.argv;
var settingsIndex = args.indexOf('--settings')
var settings = settingsIndex >= 0 ? JSON.parse(args[settingsIndex + 1]) : {};
if (settings.browsers && settings.browsers.length == 1 &&
settings.browsers[0] == 'help') {
console.log('Available browsers:');
console.log('===================');
allUsableBrowserLaunchers(config).forEach(function(name) {
console.log(' ' + name);
});
process.exit(1);
}
config.set({

@@ -31,3 +69,2 @@ // base path that will be used to resolve all patterns (eg. files, exclude)

'jasmine-ajax', 'jasmine',
'sprintf-js',
],

@@ -37,3 +74,2 @@

'karma-*', // default
frameworkPluginForModule('sprintf-js'),
],

@@ -50,3 +86,9 @@

// requirejs next
// sprintf module next
// Since we don't use require to load the sprintf module, this must be
// loaded before requirejs is loaded! Otherwise, it tries to use
// requirejs instead of loading directly into the window.
'node_modules/sprintf-js/src/sprintf.js',
// requirejs module next
'node_modules/requirejs/require.js',

@@ -61,3 +103,3 @@

// list of test assets next
'demo/assets.js',
'demo/common/assets.js',

@@ -91,27 +133,64 @@ // unit tests last

browserNoActivityTimeout: 5 * 60 * 1000, // disconnect after 5m silence
captureTimeout: 1 * 60 * 1000, // give up if startup takes 1m
captureTimeout: settings.capture_timeout,
// https://support.saucelabs.com/customer/en/portal/articles/2440724
client: {
// don't capture the client's console logs
captureConsole: false,
// Only capture the client's logs if the settings want logging.
captureConsole: !!settings.logging && settings.logging != 'none',
// |args| must be an array; pass a key-value map as the sole client
// argument.
args: [{}],
args: [{
// Run Player integration tests against external assets.
// Skipped by default.
external: !!settings.external,
// Run Player integration tests against DRM license servers.
// Skipped by default.
drm: !!settings.drm,
// Run quarantined tests which do not consistently pass.
// Skipped by default.
quarantined: !!settings.quarantined,
// Run Player integration tests with uncompiled code for debugging.
uncompiled: !!settings.uncompiled,
// Limit which tests to run. If undefined, all tests should run.
specFilter: settings.filter,
// Set what level of logs for the player to print.
logLevel: SHAKA_LOG_MAP[settings.logging]
}],
},
// enable / disable colors in the output (reporters and logs)
colors: true,
// Specify the hostname to be used when capturing browsers.
hostname: settings.hostname,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR ||
// config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_WARN,
// Specify the port where the server runs.
port: settings.port,
// do not execute tests whenever any file changes
autoWatch: false,
// Set which browsers to run on. If this is null, then Karma will wait for
// an incoming connection.
browsers: settings.browsers,
// do a single run of the tests on captured browsers and then quit
singleRun: true,
// Enable / disable colors in the output (reporters and logs). Defaults
// to true.
colors: settings.colors,
// Set Karma's level of logging.
logLevel: KARMA_LOG_MAP[settings.log_level],
// Should Karma xecute tests whenever a file changes?
autoWatch: settings.auto_watch,
// Do a single run of the tests on captured browsers and then quit.
// Defaults to true.
singleRun: settings.single_run,
// Set the time limit (ms) that should be used to identify slow tests.
reportSlowerThan: settings.report_slower_than,
// Force failure when running empty test-suites.
failOnEmptyTestSuite: true,
coverageReporter: {

@@ -129,9 +208,31 @@ includeAllSources: true,

if (flagPresent('html-coverage-report')) {
function getClientArgs() {
return config.client.args[0];
}
if (!settings.quick) {
// If --quick is present, we don't serve integration tests.
config.files.push('test/**/*_integration.js');
// We just modified the config in-place. No need for config.set().
}
var reporters = [];
if (settings.reporters) {
// Explicit reporters, use these.
reporters.push.apply(reporters, settings.reporters);
} else if (settings.logging && settings.logging != 'none') {
// With logging, default to 'spec', which makes logs easier to associate
// with individual tests.
reporters.push('spec');
} else {
// Without logging, default to 'progress'.
reporters.push('progress');
}
if (settings.html_coverage_report) {
// Wipe out any old coverage reports to avoid confusion.
var rimraf = require('rimraf');
rimraf.sync('coverage', {}); // Like rm -rf
config.set({
reporters: [ 'coverage', 'progress' ],
coverageReporter: {

@@ -144,122 +245,74 @@ reporters: [

});
}
if (!flagPresent('quick')) {
// If --quick is present, we don't serve integration tests.
var files = config.files;
files.push('test/**/*_integration.js');
// We just modified the config in-place. No need for config.set().
// The report requires the 'coverage' reporter to be added to the list.
reporters.push('coverage');
}
var logLevel = getFlagValue('enable-logging');
if (logLevel !== null) {
if (logLevel === '')
logLevel = 3; // INFO
config.set({reporters: reporters});
config.set({
reporters: ['spec'],
});
// Setting |config.client| using config.set will remove the
// |config.client.args| member.
config.client.captureConsole = true;
setClientArg(config, 'logLevel', logLevel);
}
if (settings.random) {
// If --seed was specified use that value, else generate a seed so that the
// exact order can be reproduced if it catches an issue.
var seed = settings.seed == null ? new Date().getTime() : settings.seed;
if (flagPresent('external')) {
// Run Player integration tests against external assets.
// Skipped by default.
setClientArg(config, 'external', true);
}
if (flagPresent('drm')) {
// Run Player integration tests against DRM license servers.
// Skipped by default.
setClientArg(config, 'drm', true);
}
if (flagPresent('quarantined')) {
// Run quarantined tests which do not consistently pass.
// Skipped by default.
setClientArg(config, 'quarantined', true);
}
if (flagPresent('uncompiled')) {
// Run Player integration tests with uncompiled code for debugging.
setClientArg(config, 'uncompiled', true);
}
if (flagPresent('random')) {
// Run tests in a random order.
setClientArg(config, 'random', true);
getClientArgs().random = true;
getClientArgs().seed = seed;
// If --seed was specified use that value, else generate a seed so that the
// exact order can be reproduced if it catches an issue.
var seed = getFlagValue('seed') || new Date().getTime();
setClientArg(config, 'seed', seed);
console.log("Using a random test order (--random) with --seed=" + seed);
}
if (flagPresent('specFilter')) {
setClientArg(config, 'specFilter', getFlagValue('specFilter'));
}
};
// Sets the value of an argument passed to the client.
function setClientArg(config, name, value) {
config.client.args[0][name] = value;
}
// Determines which launchers and customLaunchers can be used and returns an
// array of strings.
function allUsableBrowserLaunchers(config) {
var browsers = [];
// Find a custom command-line flag that has a value (e.g. --option=12).
// Returns:
// * string value --option=12
// * empty string --option= or --option
// * null not present
function getFlagValue(name) {
var re = /^--([^=]+)(?:=(.*))?$/;
for (var i = 0; i < process.argv.length; i++) {
var match = re.exec(process.argv[i]);
if (match && match[1] == name) {
if (match[2] !== undefined)
return match[2];
else
return '';
}
}
// Load all launcher plugins.
// The format of the items in this list is something like:
// {
// 'launcher:foo1': ['type', Function],
// 'launcher:foo2': ['type', Function],
// }
// Where the launchers grouped together into one item were defined by a single
// plugin, and the Functions in the inner array are the constructors for those
// launchers.
var plugins = require('karma/lib/plugin').resolve(['karma-*-launcher']);
plugins.forEach(function(map) {
Object.keys(map).forEach(function(name) {
// Launchers should all start with 'launcher:', but occasionally we also
// see 'test' come up for some reason.
if (!name.startsWith('launcher:')) return;
return null;
}
var browserName = name.split(':')[1];
var pluginConstructor = map[name][1];
// Find custom command-line flags.
function flagPresent(name) {
return getFlagValue(name) !== null;
}
// Most launchers requiring configuration through customLaunchers have
// no DEFAULT_CMD. Some launchers have DEFAULT_CMD, but not for this
// platform. Finally, WebDriver has DEFAULT_CMD, but still requires
// configuration, so we simply blacklist it by name.
var DEFAULT_CMD = pluginConstructor.prototype.DEFAULT_CMD;
if (!DEFAULT_CMD || !DEFAULT_CMD[process.platform]) return;
if (browserName == 'WebDriver') return;
// Construct framework plugins on-the-fly for arbitrary node modules.
// A call to this must be placed in the config in the 'plugins' array,
// and the module name must be added to the config in the 'frameworks' array.
function frameworkPluginForModule(name) {
// The framework injects files into the client which runs the tests.
var framework = function(files) {
// Locate the main file for the node module.
var path = require('path');
var mainFile = path.resolve(require.resolve(name));
// Now that we've filtered out the browsers that can't be launched without
// custom config or that can't be launched on this platform, we filter out
// the browsers you don't have installed.
var ENV_CMD = pluginConstructor.prototype.ENV_CMD;
var browserPath = process.env[ENV_CMD] || DEFAULT_CMD[process.platform];
// Add a file entry to the list of files to be served.
// This follows the same syntax as above in config.set({files: ...}).
files.unshift({
pattern: mainFile, included: true, served: true, watched: false
if (!fs.existsSync(browserPath) &&
!which.sync(browserPath, {nothrow: true})) return;
browsers.push(browserName);
});
};
});
// The framework factory function takes one argument, which is the list of
// files from the karma config.
framework.$inject = ['config.files'];
// Once we've found the names of all the standard launchers, add to that list
// the names of any custom launcher configurations.
if (config.customLaunchers) {
browsers.push.apply(browsers, Object.keys(config.customLaunchers));
}
// This is the plugin interface to register a new framework. Adding this to
// the list of plugins makes the named module available as a framework. That
// framework then injects the module into the client.
var obj = {};
obj['framework:' + name] = ['factory', framework];
return obj;
return browsers;
}

@@ -53,8 +53,2 @@ /**

/**
* Initial estimate used when there is not enough data.
* @see shaka.abr.EwmaBandwidthEstimator.DEFAULT_ESTIMATE
* @private {number}
*/
this.defaultEstimate_ = shaka.abr.EwmaBandwidthEstimator.DEFAULT_ESTIMATE;

@@ -87,13 +81,2 @@ /**

/**
* Contains the default estimate to use when there is not enough data.
* This is a relatively safe default, since 3G cell connections are faster than
* this. For slower connections, such as 2G, the default estimate may be too
* high. This default can be changed at runtime using
* {@link shaka.Player#configure} and {@link shakaExtern.AbrConfiguration}.
* @const {number}
*/
shaka.abr.EwmaBandwidthEstimator.DEFAULT_ESTIMATE = 500e3; // 500kbps
/**
* Takes a bandwidth sample.

@@ -122,20 +105,11 @@ *

/**
* Sets the default bandwidth estimate to use if there is not enough data.
*
* @param {number} estimate The default bandwidth estimate, in bit/sec.
*/
shaka.abr.EwmaBandwidthEstimator.prototype.setDefaultEstimate = function(
estimate) {
this.defaultEstimate_ = estimate;
};
/**
* Gets the current bandwidth estimate.
*
* @param {number} defaultEstimate
* @return {number} The bandwidth estimate in bits per second.
*/
shaka.abr.EwmaBandwidthEstimator.prototype.getBandwidthEstimate = function() {
shaka.abr.EwmaBandwidthEstimator.prototype.getBandwidthEstimate =
function(defaultEstimate) {
if (this.bytesSampled_ < this.minTotalBytes_) {
return this.defaultEstimate_;
return defaultEstimate;
}

@@ -142,0 +116,0 @@

@@ -24,3 +24,2 @@ /**

goog.require('shaka.util.Error');
goog.require('shaka.util.ManifestParserUtils');
goog.require('shaka.util.StreamUtils');

@@ -42,10 +41,6 @@

* <p>
* After the initial choice (in chooseStreams), this class will call
* switchCallback() when there is a better choice. switchCallback() will not
* be called more than once per
* ({@link shaka.abr.SimpleAbrManager.SWITCH_INTERVAL_MS}).
* After initial choices are made, this class will call switchCallback() when
* there is a better choice. switchCallback() will not be called more than once
* per ({@link shaka.abr.SimpleAbrManager.SWITCH_INTERVAL_MS}).
* </p>
* <p>
* This does not adapt for text streams, it will always select the first one.
* </p>
*

@@ -73,8 +68,2 @@ * @constructor

/**
* A filtered list of text streams to choose from.
* @private {!Array.<!shakaExtern.Stream>}
*/
this.textStreams_ = [];
/** @private {boolean} */

@@ -84,4 +73,3 @@ this.startupComplete_ = false;

/**
* The last wall-clock time, in milliseconds, when Streams were chosen via
* chooseStreams() or switch_().
* The last wall-clock time, in milliseconds, when streams were chosen.
*

@@ -92,13 +80,4 @@ * @private {?number}

/** @private {shakaExtern.Restrictions} */
this.restrictions_ = {
minWidth: 0,
maxWidth: Infinity,
minHeight: 0,
maxHeight: Infinity,
minPixels: 0,
maxPixels: Infinity,
minBandwidth: 0,
maxBandwidth: Infinity
};
/** @private {?shakaExtern.AbrConfiguration} */
this.config_ = null;
};

@@ -108,31 +87,2 @@

/**
* The minimum amount of time that must pass between switches, in milliseconds.
* This keeps us from changing too often and annoying the user.
*
* @const {number}
*/
shaka.abr.SimpleAbrManager.SWITCH_INTERVAL_MS = 8000;
/**
* The fraction of the estimated bandwidth which we should try to use when
* upgrading.
*
* @private
* @const {number}
*/
shaka.abr.SimpleAbrManager.BANDWIDTH_UPGRADE_TARGET_ = 0.85;
/**
* The largest fraction of the estimated bandwidth we should use. We should
* downgrade to avoid this.
*
* @private
* @const {number}
*/
shaka.abr.SimpleAbrManager.BANDWIDTH_DOWNGRADE_TARGET_ = 0.95;
/**
* @override

@@ -145,3 +95,2 @@ * @export

this.variants_ = [];
this.textStreams_ = [];
this.lastTimeChosenMs_ = null;

@@ -167,22 +116,37 @@

*/
shaka.abr.SimpleAbrManager.prototype.chooseStreams = function(
mediaTypesToUpdate) {
var ContentType = shaka.util.ManifestParserUtils.ContentType;
// Choose streams for the specific types requested.
var chosen = {};
shaka.abr.SimpleAbrManager.prototype.chooseVariant = function() {
// Alias.
var SimpleAbrManager = shaka.abr.SimpleAbrManager;
if (mediaTypesToUpdate.indexOf(ContentType.AUDIO) > -1 ||
mediaTypesToUpdate.indexOf(ContentType.VIDEO) > -1) {
// Choose a new Variant
var variant = this.chooseVariant_(this.variants_);
if (variant && variant.video)
chosen[ContentType.VIDEO] = variant.video;
// Get sorted Variants.
var sortedVariants = SimpleAbrManager.filterAndSortVariants_(
this.config_.restrictions, this.variants_);
var currentBandwidth = this.bandwidthEstimator_.getBandwidthEstimate(
this.config_.defaultBandwidthEstimate);
if (variant && variant.audio)
chosen[ContentType.AUDIO] = variant.audio;
if (this.variants_.length && !sortedVariants.length) {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.RESTRICTIONS_CANNOT_BE_MET);
}
if (mediaTypesToUpdate.indexOf(ContentType.TEXT) > -1) {
// We don't adapt text, so just choose stream 0.
chosen[ContentType.TEXT] = this.textStreams_[0];
// Start by assuming that we will use the first Stream.
var chosen = sortedVariants[0] || null;
for (var i = 0; i < sortedVariants.length; ++i) {
var variant = sortedVariants[i];
var nextVariant = sortedVariants[i + 1] || {bandwidth: Infinity};
var minBandwidth = variant.bandwidth /
this.config_.bandwidthDowngradeTarget;
var maxBandwidth = nextVariant.bandwidth /
this.config_.bandwidthUpgradeTarget;
shaka.log.v2('Bandwidth ranges:',
(variant.bandwidth / 1e6).toFixed(3),
(minBandwidth / 1e6).toFixed(3),
(maxBandwidth / 1e6).toFixed(3));
if (currentBandwidth >= minBandwidth && currentBandwidth <= maxBandwidth)
chosen = variant;
}

@@ -221,3 +185,5 @@

'deltaTimeMs=' + deltaTimeMs,
'numBytes=' + numBytes);
'numBytes=' + numBytes,
'lastTimeChosenMs=' + this.lastTimeChosenMs_,
'enabled=' + this.enabled_);
goog.asserts.assert(deltaTimeMs >= 0, 'expected a non-negative duration');

@@ -236,3 +202,4 @@ this.bandwidthEstimator_.sample(deltaTimeMs, numBytes);

shaka.abr.SimpleAbrManager.prototype.getBandwidthEstimate = function() {
return this.bandwidthEstimator_.getBandwidthEstimate();
return this.bandwidthEstimator_.getBandwidthEstimate(
this.config_.defaultBandwidthEstimate);
};

@@ -245,20 +212,2 @@

*/
shaka.abr.SimpleAbrManager.prototype.setDefaultEstimate = function(estimate) {
this.bandwidthEstimator_.setDefaultEstimate(estimate);
};
/**
* @override
* @export
*/
shaka.abr.SimpleAbrManager.prototype.setRestrictions = function(restrictions) {
this.restrictions_ = restrictions;
};
/**
* @override
* @export
*/
shaka.abr.SimpleAbrManager.prototype.setVariants = function(variants) {

@@ -273,4 +222,4 @@ this.variants_ = variants;

*/
shaka.abr.SimpleAbrManager.prototype.setTextStreams = function(streams) {
this.textStreams_ = streams;
shaka.abr.SimpleAbrManager.prototype.configure = function(config) {
this.config_ = config;
};

@@ -289,4 +238,2 @@

var ContentType = shaka.util.ManifestParserUtils.ContentType;
if (!this.startupComplete_) {

@@ -303,3 +250,3 @@ // Check if we've got enough data yet.

var delta = now - this.lastTimeChosenMs_;
if (delta < shaka.abr.SimpleAbrManager.SWITCH_INTERVAL_MS) {
if (delta < this.config_.switchInterval * 1000) {
shaka.log.v2('Still within switch interval...');

@@ -310,5 +257,7 @@ return;

var chosen = this.chooseStreams([ContentType.AUDIO, ContentType.VIDEO]);
var currentBandwidthKbps =
Math.round(this.bandwidthEstimator_.getBandwidthEstimate() / 1000.0);
var chosenVariant = this.chooseVariant();
var bandwidthEstimate = this.bandwidthEstimator_.getBandwidthEstimate(
this.config_.defaultBandwidthEstimate);
var currentBandwidthKbps = Math.round(bandwidthEstimate / 1000.0);
shaka.log.debug(

@@ -318,3 +267,3 @@ 'Calling switch_(), bandwidth=' + currentBandwidthKbps + ' kbps');

// out before passing the choices on to StreamingEngine.
this.switch_(chosen);
this.switch_(chosenVariant);
};

@@ -324,49 +273,2 @@

/**
* Chooses a Variant with an optimal bandwidth.
*
* @param {!Array.<shakaExtern.Variant>} variants
* @return {shakaExtern.Variant}
* @private
*/
shaka.abr.SimpleAbrManager.prototype.chooseVariant_ = function(variants) {
// Alias.
var SimpleAbrManager = shaka.abr.SimpleAbrManager;
// Get sorted Streams.
var sortedVariants = SimpleAbrManager.filterAndSortVariants_(
this.restrictions_, variants);
var currentBandwidth = this.bandwidthEstimator_.getBandwidthEstimate();
if (variants.length && !sortedVariants.length) {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.RESTRICTIONS_CANNOT_BE_MET);
}
// Start by assuming that we will use the first Stream.
var chosen = sortedVariants[0];
for (var i = 0; i < sortedVariants.length; ++i) {
var variant = sortedVariants[i];
var nextVariant = sortedVariants[i + 1] || {bandwidth: Infinity};
var minBandwidth = variant.bandwidth /
SimpleAbrManager.BANDWIDTH_DOWNGRADE_TARGET_;
var maxBandwidth = nextVariant.bandwidth /
SimpleAbrManager.BANDWIDTH_UPGRADE_TARGET_;
shaka.log.v2('Bandwidth ranges:',
(variant.bandwidth / 1e6).toFixed(3),
(minBandwidth / 1e6).toFixed(3),
(maxBandwidth / 1e6).toFixed(3));
if (currentBandwidth >= minBandwidth && currentBandwidth <= maxBandwidth)
chosen = variant;
}
return chosen;
};
/**
* @param {shakaExtern.Restrictions} restrictions

@@ -373,0 +275,0 @@ * @param {!Array.<shakaExtern.Variant>} variants

@@ -406,3 +406,3 @@ /**

* read with quoted properties.
* @param {cast.receiver.CastMessageBus.Event} event
* @param {!cast.receiver.CastMessageBus.Event} event
* @private

@@ -473,3 +473,3 @@ */

/**
* @param {cast.receiver.CastMessageBus.Event} event
* @param {!cast.receiver.CastMessageBus.Event} event
* @private

@@ -476,0 +476,0 @@ */

@@ -119,5 +119,5 @@ /**

'getPlayheadTimeAsDate',
'getPresentationStartTimeAsDate',
'getTextLanguages',
'getTextTracks',
'getTracks',
'getStats',

@@ -164,6 +164,6 @@ 'getVariantTracks',

'resetConfiguration',
'retryStreaming',
'selectAudioLanguage',
'selectTextLanguage',
'selectTextTrack',
'selectTrack',
'selectVariantTrack',

@@ -170,0 +170,0 @@ 'setTextTrackVisibility',

@@ -129,4 +129,2 @@ /**

var defaultInit = null;
/** @type {?string} */
var defaultKeyId = null;
/** @type {!Array.<shakaExtern.DrmInfo>} */

@@ -138,6 +136,5 @@ var drmInfos = [];

var keyIds = parsed.map(function(elem) { return elem.keyId; })
.filter(Functional.isNotNull);
if (keyIds.length > 0) {
defaultKeyId = keyIds[0];
if (keyIds.some(Functional.isNotEqualFunc(defaultKeyId))) {
.filter(Functional.isNotNull);
if (keyIds.length) {
if (keyIds.filter(Functional.isNotDuplicate).length > 1) {
throw new shaka.util.Error(

@@ -164,3 +161,3 @@ shaka.util.Error.Severity.CRITICAL,

if (parsedNonCenc.length > 0) {
if (parsedNonCenc.length) {
drmInfos = ContentProtection.convertElements_(

@@ -179,3 +176,3 @@ defaultInit, callback, parsedNonCenc);

// key-systems are supported.
if (parsed.length > 0 && (ignoreDrmInfo || !parsedNonCenc.length)) {
if (parsed.length && (ignoreDrmInfo || !parsedNonCenc.length)) {
var keySystems = ContentProtection.defaultKeySystems_;

@@ -189,2 +186,5 @@ drmInfos =

/** @type {?string} */
var defaultKeyId = keyIds[0] || null;
// attach the default keyId, if it exists, to every initData

@@ -191,0 +191,0 @@ if (defaultKeyId) {

@@ -30,4 +30,4 @@ /**

goog.require('shaka.media.SegmentReference');
goog.require('shaka.media.TextEngine');
goog.require('shaka.net.NetworkingEngine');
goog.require('shaka.text.TextEngine');
goog.require('shaka.util.Error');

@@ -37,3 +37,3 @@ goog.require('shaka.util.Functional');

goog.require('shaka.util.ManifestParserUtils');
goog.require('shaka.util.StreamUtils');
goog.require('shaka.util.MimeUtils');
goog.require('shaka.util.StringUtils');

@@ -130,3 +130,4 @@ goog.require('shaka.util.XmlUtils');

* containsEmsgBoxes: boolean,
* id: string
* id: string,
* numChannels: ?number
* }}

@@ -162,2 +163,4 @@ *

* The ID of the element.
* @property {?number} numChannels
* The number of audio channels, or null if unknown.
*/

@@ -417,30 +420,38 @@ shaka.dash.DashParser.InheritanceFrame;

var Error = shaka.util.Error;
var Functional = shaka.util.Functional;
var XmlUtils = shaka.util.XmlUtils;
var ManifestParserUtils = shaka.util.ManifestParserUtils;
var MpdUtils = shaka.dash.MpdUtils;
var string = shaka.util.StringUtils.fromUTF8(data);
var parser = new DOMParser();
var xml = null;
var mpd = null;
try {
xml = parser.parseFromString(string, 'text/xml');
} catch (exception) {}
if (xml) {
// parseFromString returns a Document object. A Document is a Node but not
// an Element, so it cannot be used in XmlUtils (technically it can but the
// types don't match). The |documentElement| member defines the top-level
// element in the document.
if (xml.documentElement.tagName == 'MPD')
mpd = xml.documentElement;
}
if (mpd && mpd.getElementsByTagName('parsererror').length > 0)
mpd = null; // It had a parser error in it.
var mpd = MpdUtils.parseXml(data, 'MPD');
if (!mpd) {
throw new Error(
Error.Severity.CRITICAL, Error.Category.MANIFEST,
Error.Code.DASH_INVALID_XML);
Error.Code.DASH_INVALID_XML, finalManifestUri);
}
// Process the mpd to account for xlink connections.
var failGracefully = this.config_.dash.xlinkFailGracefully;
var xlinkPromise = MpdUtils.processXlinks(
mpd, this.config_.retryParameters, failGracefully, finalManifestUri,
this.playerInterface_.networkingEngine);
return xlinkPromise.then(function(finalMpd) {
return this.processManifest_(finalMpd, finalManifestUri);
}.bind(this));
};
/**
* Taked a formatted MPD and converts it into a manifest.
*
* @param {!Element} mpd
* @param {string} finalManifestUri The final manifest URI, which may
* differ from this.manifestUri_ if there has been a redirect.
* @return {!Promise}
* @throws shaka.util.Error When there is a parsing error.
* @private
*/
shaka.dash.DashParser.prototype.processManifest_ =
function(mpd, finalManifestUri) {
var Functional = shaka.util.Functional;
var XmlUtils = shaka.util.XmlUtils;
var ManifestParserUtils = shaka.util.ManifestParserUtils;
// Get any Location elements. This will update the manifest location and

@@ -477,2 +488,3 @@ // the base URI.

/** @type {!shaka.media.PresentationTimeline} */
var presentationTimeline;

@@ -569,3 +581,2 @@ if (this.manifest_) {

context, baseUris, mpd) {
var Functional = shaka.util.Functional;
var XmlUtils = shaka.util.XmlUtils;

@@ -625,13 +636,18 @@ var presentationDuration = XmlUtils.parseAttr(

// If there are any new periods, call the callback and add them to the
// manifest. If this is the first parse, it will see all of them as new.
// If the period ID is new, add it to the list. This must be done for both
// the initial manifest parse and for updates.
// See https://github.com/google/shaka-player/issues/963
var periodId = context.period.id;
if (this.periodIds_.every(Functional.isNotEqualFunc(periodId))) {
this.playerInterface_.filterPeriod(period);
if (this.periodIds_.indexOf(periodId) == -1) {
this.periodIds_.push(periodId);
if (this.manifest_)
// If this is an update, call filterNewPeriod and add it to the manifest.
// If this is the first parse of the manifest (this.manifest_ == null),
// filterAllPeriods will be called later.
if (this.manifest_) {
this.playerInterface_.filterNewPeriod(period);
this.manifest_.periods.push(period);
}
}
if (periodDuration == null) {

@@ -652,2 +668,7 @@ if (i != periodNodes.length - 1) {

prevEnd = start + periodDuration;
} // end of period parsing loop
// Call filterAllPeriods if this is the initial parse.
if (this.manifest_ == null) {
this.playerInterface_.filterAllPeriods(periods);
}

@@ -1128,3 +1149,4 @@

containsEmsgBoxes: context.representation.containsEmsgBoxes,
roles: roles
roles: roles,
channelsCount: context.representation.numChannels
};

@@ -1221,3 +1243,4 @@ };

containsEmsgBoxes: false,
frameRate: undefined
frameRate: undefined,
numChannels: null
});

@@ -1237,2 +1260,6 @@ baseUris = baseUris || parent.baseUris;

!!XmlUtils.findChildren(elem, 'InbandEventStream').length;
var audioChannelConfigs =
XmlUtils.findChildren(elem, 'AudioChannelConfiguration');
var numChannels =
this.parseAudioChannels_(audioChannelConfigs) || parent.numChannels;

@@ -1256,3 +1283,4 @@ if (!contentType) {

containsEmsgBoxes: containsEmsgBoxes || parent.containsEmsgBoxes,
id: elem.getAttribute('id')
id: elem.getAttribute('id'),
numChannels: numChannels
};

@@ -1263,2 +1291,63 @@ };

/**
* @param {!Array.<!Element>} audioChannelConfigs an array of
* AudioChannelConfiguration elements
* @return {?number} the number of audio channels, or null if unknown
* @private
*/
shaka.dash.DashParser.prototype.parseAudioChannels_ =
function(audioChannelConfigs) {
for (var i = 0; i < audioChannelConfigs.length; ++i) {
var elem = audioChannelConfigs[i];
var scheme = elem.getAttribute('schemeIdUri');
if (!scheme) continue;
var value = elem.getAttribute('value');
if (!value) continue;
switch (scheme) {
case 'urn:mpeg:dash:outputChannelPositionList:2012':
// A space-separated list of speaker positions, so num channels is the
// length of this list.
return value.trim().split(/ +/).length;
case 'urn:mpeg:dash:23003:3:audio_channel_configuration:2011':
case 'urn:dts:dash:audio_channel_configuration:2012':
// As far as we can tell, this is a number of channels.
var intValue = parseInt(value, 10);
if (!intValue) { // 0 or NaN
shaka.log.warning('Channel parsing failure! ' +
'Ignoring scheme and value', scheme, value);
continue;
}
return intValue;
case 'tag:dolby.com,2014:dash:audio_channel_configuration:2011':
case 'urn:dolby:dash:audio_channel_configuration:2011':
// A hex-encoded 16-bit integer, in which each bit represents a channel.
var hexValue = parseInt(value, 16);
if (!hexValue) { // 0 or NaN
shaka.log.warning('Channel parsing failure! ' +
'Ignoring scheme and value', scheme, value);
continue;
}
// Count the 1-bits in hexValue.
var numBits = 0;
while (hexValue) {
if (hexValue & 1) ++numBits;
hexValue >>= 1;
}
return numBits;
default:
shaka.log.warning('Unrecognized audio channel scheme:', scheme, value);
continue;
}
}
return null;
};
/**
* Verifies that a Representation has exactly one Segment* element. Prints

@@ -1498,5 +1587,5 @@ * warnings if there is a problem.

shaka.dash.DashParser.guessContentType_ = function(mimeType, codecs) {
var fullMimeType = shaka.util.StreamUtils.getFullMimeType(mimeType, codecs);
var fullMimeType = shaka.util.MimeUtils.getFullType(mimeType, codecs);
if (shaka.media.TextEngine.isTypeSupported(fullMimeType)) {
if (shaka.text.TextEngine.isTypeSupported(fullMimeType)) {
// If it's supported by TextEngine, it's definitely text.

@@ -1503,0 +1592,0 @@ // We don't check MediaSourceEngine, because that would report support

@@ -22,4 +22,6 @@ /**

goog.require('shaka.log');
goog.require('shaka.net.NetworkingEngine');
goog.require('shaka.util.Functional');
goog.require('shaka.util.ManifestParserUtils');
goog.require('shaka.util.StringUtils');
goog.require('shaka.util.XmlUtils');

@@ -396,1 +398,191 @@

};
/**
* Parse some UTF8 data and return the resulting root element if
* it was valid XML.
* @param {ArrayBuffer} data
* @param {!string} expectedRootElemName
* @return {Element|undefined}
*/
shaka.dash.MpdUtils.parseXml = function(data, expectedRootElemName) {
var parser = new DOMParser();
var rootElem;
var xml;
try {
var string = shaka.util.StringUtils.fromUTF8(data);
xml = parser.parseFromString(string, 'text/xml');
} catch (exception) {}
if (xml) {
// The top-level element in the loaded xml should have the
// same type as the element linking.
if (xml.documentElement.tagName == expectedRootElemName)
rootElem = xml.documentElement;
}
if (rootElem && rootElem.getElementsByTagName('parsererror').length > 0)
return null; // It had a parser error in it.
return rootElem;
};
/**
* Follow the xlink link contained in the given element.
* It also strips the xlink properties off of the element,
* even if the process fails.
*
* @param {!Element} element
* @param {!shakaExtern.RetryParameters} retryParameters
* @param {boolean} failGracefully
* @param {!string} baseUri
* @param {!shaka.net.NetworkingEngine} networkingEngine
* @param {number} linkDepth
* @return {!Promise.<!Element>}
* @private
*/
shaka.dash.MpdUtils.handleXlinkInElement_ =
function(element, retryParameters, failGracefully, baseUri,
networkingEngine, linkDepth) {
var MpdUtils = shaka.dash.MpdUtils;
var Error = shaka.util.Error;
var ManifestParserUtils = shaka.util.ManifestParserUtils;
var xlinkHref = element.getAttribute('xlink:href');
var xlinkActuate = element.getAttribute('xlink:actuate') || 'onRequest';
// Remove the xlink properties, so it won't download again
// when re-processed.
for (var i = 0; i < element.attributes.length; i++) {
var attribute = element.attributes[i].nodeName;
if (attribute.indexOf('xlink:') != -1) {
element.removeAttribute(attribute);
i -= 1;
}
}
if (linkDepth >= 5) {
return Promise.reject(new Error(
Error.Severity.CRITICAL, Error.Category.MANIFEST,
Error.Code.DASH_XLINK_DEPTH_LIMIT));
}
if (xlinkActuate != 'onLoad') {
// Only xlink:actuate="onLoad" is supported.
// When no value is specified, the assumed value is "onRequest".
return Promise.reject(new Error(
Error.Severity.CRITICAL, Error.Category.MANIFEST,
Error.Code.DASH_UNSUPPORTED_XLINK_ACTUATE));
}
// Resolve the xlink href, in case it's a relative URL.
var uris = ManifestParserUtils.resolveUris([baseUri], [xlinkHref]);
// Load in the linked elements.
var requestType = shaka.net.NetworkingEngine.RequestType.MANIFEST;
var request = shaka.net.NetworkingEngine.makeRequest(
uris, retryParameters);
var requestPromise = networkingEngine.request(requestType, request);
return requestPromise.then(function(response) {
// This only supports the case where the loaded xml has a single
// top-level element. If there are multiple roots, it will be rejected.
var rootElem = MpdUtils.parseXml(response.data, element.tagName);
if (!rootElem) {
// It was not valid XML.
return Promise.reject(new Error(
Error.Severity.CRITICAL, Error.Category.MANIFEST,
Error.Code.DASH_INVALID_XML, xlinkHref));
}
// Now that there is no other possibility of the process erroring,
// the element can be changed further.
// Remove the current contents of the node.
while (element.childNodes.length)
element.removeChild(element.childNodes[0]);
// Move the children of the loaded xml into the current element.
while (rootElem.childNodes.length) {
var child = rootElem.childNodes[0];
rootElem.removeChild(child);
element.appendChild(child);
}
// Move the attributes of the loaded xml into the current element.
for (var i = 0; i < rootElem.attributes.length; i++) {
var attribute = rootElem.attributes[i].nodeName;
var attributeValue = rootElem.getAttribute(attribute);
element.setAttribute(attribute, attributeValue);
}
return shaka.dash.MpdUtils.processXlinks(
element, retryParameters, failGracefully, uris[0], networkingEngine,
linkDepth + 1);
}.bind(element));
};
/**
* Filter the contents of a node recursively, replacing xlink links
* with their associated online data.
*
* @param {!Element} element
* @param {!shakaExtern.RetryParameters} retryParameters
* @param {boolean} failGracefully
* @param {!string} baseUri
* @param {!shaka.net.NetworkingEngine} networkingEngine
* @param {number=} opt_linkDepth
* @return {!Promise.<!Element>}
*/
shaka.dash.MpdUtils.processXlinks =
function(element, retryParameters, failGracefully, baseUri,
networkingEngine, opt_linkDepth) {
var MpdUtils = shaka.dash.MpdUtils;
opt_linkDepth = opt_linkDepth || 0;
if (element.getAttribute('xlink:href')) {
var handled = MpdUtils.handleXlinkInElement_(
element, retryParameters, failGracefully, baseUri,
networkingEngine, opt_linkDepth);
if (failGracefully) {
// Catch any error and go on.
handled = handled.catch(function() {
// handleXlinkInElement_ strips the xlink properties off of the element
// even if it fails, so calling processXlinks again will handle whatever
// contents the element natively has.
return MpdUtils.processXlinks(element, retryParameters, failGracefully,
baseUri, networkingEngine,
opt_linkDepth);
});
}
return handled;
}
// Filter out any children that should be nulled.
for (var i = 0; i < element.childNodes.length; i++) {
var child = element.childNodes[i];
if (child instanceof Element) {
var resolveToZeroString = 'urn:mpeg:dash:resolve-to-zero:2013';
if (child.getAttribute('xlink:href') == resolveToZeroString) {
// This is a 'resolve to zero' code; it means the element should
// be removed, as specified by the mpeg-dash rules for xlink.
element.removeChild(child);
i -= 1;
}
}
}
var childPromises = [];
for (var i = 0; i < element.childNodes.length; i++) {
var child = element.childNodes[i];
if (child.nodeType == Node.ELEMENT_NODE) {
// Replace the child with its processed form.
var childPromise = shaka.dash.MpdUtils.processXlinks(
/** @type {!Element} */ (child), retryParameters, failGracefully,
baseUri, networkingEngine, opt_linkDepth);
childPromises.push(childPromise);
}
}
return Promise.all(childPromises).then(function() {
return element;
});
};

@@ -66,2 +66,9 @@ /**

/**
* A map from media playlists' uris to stream infos
* representing the playlists.
* @private {!Object.<string, shaka.hls.HlsParser.StreamInfo>}
*/
this.uriToStreamInfosMap_ = {};
/** @private {?shaka.media.PresentationTimeline} */

@@ -75,2 +82,20 @@ this.presentationTimeline_ = null;

this.manifestTextParser_ = new shaka.hls.ManifestTextParser();
/**
* The update period in seconds; or null for no updates.
* @private {?number}
*/
this.updatePeriod_ = null;
/** @private {?number} */
this.updateTimer_ = null;
/** @private {boolean} */
this.isLive_ = false;
/** @private {?shakaExtern.Manifest} */
this.manifest_ = null;
/** @private {number} */
this.maxTargetDuration_ = 0;
};

@@ -84,3 +109,4 @@

* drmInfos: !Array.<shakaExtern.DrmInfo>,
* relativeUri: !string
* relativeUri: !string,
* lastSegmentSeen: !shaka.media.SegmentReference
* }}

@@ -99,2 +125,4 @@ *

* The uri associated with the stream, relative to the manifest.
* @property {!shaka.media.SegmentReference} lastSegmentSeen
* Last segment of the stream seen so far.
*/

@@ -122,3 +150,6 @@ shaka.hls.HlsParser.StreamInfo;

return this.requestManifest_(uri).then(function(response) {
return this.parseManifest_(response.data, uri);
return this.parseManifest_(response.data, uri).then(function() {
this.setUpdateTimer_(this.updatePeriod_);
return this.manifest_;
}.bind(this));
}.bind(this));

@@ -136,2 +167,3 @@ };

this.mediaTagsToStreamInfosMap_ = {};
this.manifest_ = null;

@@ -147,3 +179,15 @@ return Promise.resolve();

shaka.hls.HlsParser.prototype.update = function() {
// TODO: Implement support for live content.
if (!this.isLive_)
return;
var promises = [];
var uris = Object.keys(this.uriToStreamInfosMap_);
for (var i = 0; i < uris.length; i++) {
var uri = uris[i];
var streamInfo = this.uriToStreamInfosMap_[uri];
promises.push(this.updateStream_(streamInfo, uri));
}
return Promise.all(promises);
};

@@ -153,2 +197,89 @@

/**
* Updates a stream.
*
* @param {!shaka.hls.HlsParser.StreamInfo} streamInfo
* @param {string} uri
* @throws shaka.util.Error
* @private
*/
shaka.hls.HlsParser.prototype.updateStream_ = function(streamInfo, uri) {
this.requestManifest_(uri).then(function(response) {
var Utils = shaka.hls.Utils;
var playlistData = response.data;
var playlist = this.manifestTextParser_.parsePlaylist(playlistData,
response.uri);
if (playlist.type != shaka.hls.PlaylistType.MEDIA) {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.HLS_INVALID_PLAYLIST_HIERARCHY);
}
var mediaSequenceTag = Utils.getFirstTagWithName(playlist.tags,
'EXT-X-MEDIA-SEQUENCE');
var startPosition = mediaSequenceTag ? Number(mediaSequenceTag.value) : 0;
var segments = this.createSegments_(playlist, startPosition);
segments = this.adjustSegments_(segments, streamInfo.lastSegmentSeen);
streamInfo.segmentIndex.merge(segments);
if (segments.length)
streamInfo.lastSegmentSeen = segments[segments.length - 1];
// Once the last segment has been added to the playlist, #EXT-X-ENDLIST tag
// will be appended. If that happened, treat the rest of the presentation
// as VOD.
var endlistTag = Utils.getFirstTagWithName(playlist.tags, 'EXT-X-ENDLIST');
if (endlistTag) {
goog.asserts.assert(streamInfo.lastSegmentSeen != null,
'Should not be null!');
var endTime = streamInfo.lastSegmentSeen.endTime;
this.setLive_(false);
this.presentationTimeline_.setDuration(endTime);
}
}.bind(this));
};
/**
* The manifest doesn't specify segments' start and end times.
* We assume the first segment starts at 0 and base the following
* segments on this assumption (each segment's starts when previous ends).
* This method adjusts new segments' (added on update) timeline with
* respect to previously appended segments.
*
* @param {!Array.<!shaka.media.SegmentReference>} segments
* @param {!shaka.media.SegmentReference} lastSegmentSeen
* @return {!Array.<!shaka.media.SegmentReference>}
* @private
*/
shaka.hls.HlsParser.prototype.adjustSegments_ =
function(segments, lastSegmentSeen) {
var adjusted = [];
var offset = lastSegmentSeen.endTime;
for (var i = 0; i < segments.length; i++) {
var segment = segments[i];
if (segment.position > lastSegmentSeen.position) {
var duration = segment.endTime - segment.startTime;
var startTime = offset;
var endTime = offset + duration;
offset += duration;
var adjustedSegment =
new shaka.media.SegmentReference(segment.position,
startTime,
endTime,
segment.getUris,
segment.startByte,
segment.endByte);
adjusted.push(adjustedSegment);
}
}
return adjusted;
};
/**
* @override

@@ -168,4 +299,4 @@ * @exportInterface

* @param {string} uri
* @return {!Promise.<!shakaExtern.Manifest>}
* @throws shaka.util.Error When there is a parsing error.
* @return {!Promise}
* @private

@@ -185,9 +316,16 @@ */

// TODO: Implement support for live content.
this.presentationTimeline_ = new shaka.media.PresentationTimeline(null, 0);
return this.createPeriod_(playlist).then(function(period) {
// HLS has no notion of periods. We're treating the whole presentation as
// one period.
this.playerInterface_.filterPeriod(period);
return {
this.playerInterface_.filterAllPeriods([period]);
// Update presentationDelay with the largest target duration
// across all variants.
if (this.isLive_)
this.presentationTimeline_.setDelay(this.maxTargetDuration_ * 3);
goog.asserts.assert(this.presentationTimeline_ != null,
'presentationTimeline should already be created!');
this.manifest_ = {
presentationTimeline: this.presentationTimeline_,

@@ -234,3 +372,4 @@ periods: [period],

var variants = allVariants.reduce(Functional.collapseArrays, []);
this.fitSegments_(variants);
if (!this.isLive_)
this.fitSegments_(variants);
return {

@@ -269,2 +408,3 @@ startTime: 0,

/** @type {!Array.<string>} */
var codecs = tag.getAttributeValue('CODECS', defaultCodecs).split(',');

@@ -545,8 +685,12 @@ var resolutionAttr = tag.getAttribute('RESOLUTION');

// https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-4.3.4.1
var channelsAttribute = tag.getAttributeValue('CHANNELS');
var channelsCount = type == 'audio' ?
this.getChannelsCount_(channelsAttribute) : null;
var uri = HlsParser.getRequiredAttributeValue_(tag, 'URI');
uri = shaka.hls.Utils.constructAbsoluteUri(this.manifestUri_, uri);
var primary = !!defaultAttr || !!autoselectAttr;
return this.createStreamInfo_(uri, allCodecs, type, timeOffset, language,
primary, label).then(function(streamInfo) {
return this.createStreamInfo_(uri, allCodecs, type, timeOffset,
language, primary, label, channelsCount).then(function(streamInfo) {
this.mediaTagsToStreamInfosMap_[tag.id] = streamInfo;
this.uriToStreamInfosMap_[uri] = streamInfo;
return streamInfo;

@@ -558,2 +702,22 @@ }.bind(this));

/**
* Get the channels count information for HLS audio track.
* The channels value is a string that specifies an ordered, "/" separated list
* of parameters. If the type is audio, the first parameter will be a decimal
* integer, as the number of independent, simultaneous audio channels.
* No other channels parameters are currently defined.
*
* @param {?string} channels
*
* @return {?number} channelcount
* @private
*/
shaka.hls.HlsParser.prototype.getChannelsCount_ = function(channels) {
if (!channels) return null;
var channelscountstring = channels.split('/')[0];
var count = parseInt(channelscountstring, 10);
return count;
};
/**
* Parse EXT-X-STREAM-INF media tag into a Stream object.

@@ -574,5 +738,10 @@ *

var uri = shaka.hls.HlsParser.getRequiredAttributeValue_(tag, 'URI');
uri = shaka.hls.Utils.constructAbsoluteUri(this.manifestUri_, uri);
return this.createStreamInfo_(uri, allCodecs, type, timeOffset,
/* language */ 'und', /* primary */ false,
/* label */ null);
/* label */ null, /* channelcount */ null).then(
function(streamInfo) {
this.uriToStreamInfosMap_[uri] = streamInfo;
return streamInfo;
}.bind(this));
};

@@ -589,2 +758,3 @@

* @param {?string} label
* @param {?number} channelsCount
* @return {!Promise.<shaka.hls.HlsParser.StreamInfo>}

@@ -594,4 +764,4 @@ * @throws shaka.util.Error

*/
shaka.hls.HlsParser.prototype.createStreamInfo_ =
function(uri, allCodecs, type, timeOffset, language, primary, label) {
shaka.hls.HlsParser.prototype.createStreamInfo_ = function(uri, allCodecs,
type, timeOffset, language, primary, label, channelsCount) {
var Utils = shaka.hls.Utils;

@@ -618,14 +788,20 @@ var ContentType = shaka.util.ManifestParserUtils.ContentType;

var presentationTypeTag = Utils.getFirstTagWithName(playlist.tags,
'EXT-X-PLAYLIST-TYPE');
var endlistTag = Utils.getFirstTagWithName(playlist.tags, 'EXT-X-ENDLIST');
var isVod = endlistTag || (presentationTypeTag &&
presentationTypeTag.value == 'VOD');
if (!isVod) {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.HLS_LIVE_CONTENT_NOT_SUPPORTED);
var mediaSequenceTag = Utils.getFirstTagWithName(playlist.tags,
'EXT-X-MEDIA-SEQUENCE');
var startPosition = mediaSequenceTag ? Number(mediaSequenceTag.value) : 0;
var segments = this.createSegments_(playlist, startPosition);
var segmentIndex = new shaka.media.SegmentIndex(segments);
this.setPresentationType_(playlist);
if (!this.presentationTimeline_) {
// The presentation started last available segment's end time ago.
// All variants should be in sync in terms of timeline, so just grab
// this from an arbitrary stream.
this.createPresentationTimeline_(segments[segments.length - 1].endTime);
}
// Time offset can be specified on either Master or Media Playlist.

@@ -643,15 +819,14 @@ // If Media Playlist provides it's own value, use that.

}
var mediaSequenceTag = Utils.getFirstTagWithName(playlist.tags,
'EXT-X-MEDIA-SEQUENCE');
var startPosition = mediaSequenceTag ? Number(mediaSequenceTag.value) : 0;
var segments = this.createSegments_(playlist, startPosition);
this.presentationTimeline_.notifySegments(0, segments);
this.presentationTimeline_.notifySegments(0, segments);
var duration =
segments[segments.length - 1].endTime - segments[0].startTime;
var presentationDuration = this.presentationTimeline_.getDuration();
if (presentationDuration == Infinity || presentationDuration < duration) {
this.presentationTimeline_.setDuration(duration);
if (!this.isLive_) {
var duration =
segments[segments.length - 1].endTime - segments[0].startTime;
var presentationDuration = this.presentationTimeline_.getDuration();
if (presentationDuration == Infinity || presentationDuration < duration) {
this.presentationTimeline_.setDuration(duration);
}
}
var codecs = this.guessCodecs_(type, allCodecs);

@@ -666,4 +841,2 @@

var segmentIndex = new shaka.media.SegmentIndex(segments);
var drmTags = [];

@@ -734,3 +907,4 @@ playlist.segments.forEach(function(segment) {

bandwidth: undefined,
roles: []
roles: [],
channelsCount: channelsCount
};

@@ -744,7 +918,50 @@

drmInfos: drmInfos,
relativeUri: relativeUri
relativeUri: relativeUri,
lastSegmentSeen: segments[segments.length - 1]
};
}.bind(this));
}.bind(this));
};
/**
* @param {!shaka.hls.Playlist} playlist
* @throws shaka.util.Error
* @private
*/
shaka.hls.HlsParser.prototype.setPresentationType_ = function(playlist) {
var Utils = shaka.hls.Utils;
var presentationTypeTag = Utils.getFirstTagWithName(playlist.tags,
'EXT-X-PLAYLIST-TYPE');
var endlistTag = Utils.getFirstTagWithName(playlist.tags, 'EXT-X-ENDLIST');
var isVod = endlistTag || (presentationTypeTag &&
presentationTypeTag.value == 'VOD');
if (isVod) {
this.setLive_(false);
} else if (!presentationTypeTag) {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.HLS_LIVE_CONTENT_NOT_SUPPORTED);
} else {
// presentation type EVENT
var targetDurationTag = this.getRequiredTag_(playlist.tags,
'EXT-X-TARGETDURATION');
var targetDuration = Number(targetDurationTag.value);
// According to HLS spec, updates should not happen more often than
// once in targetDuration. It also requires to only update the active
// variant. We might implement that later, but for now every variant
// will be updated. To get the update period, choose the smallest
// targetDuration value across all playlists.
if (!this.updatePeriod_) {
this.setLive_(true);
this.updatePeriod_ = targetDuration;
} else if (this.updatePeriod_ > targetDuration) {
this.updatePeriod_ = targetDuration;
}
// Update longest target duration if need be to use as a presentation
// delay later.
this.maxTargetDuration_ = Math.max(targetDuration, this.maxTargetDuration_);
}
};

@@ -754,2 +971,24 @@

/**
* @param {number} endTime
* @throws shaka.util.Error
* @private
*/
shaka.hls.HlsParser.prototype.createPresentationTimeline_ = function(endTime) {
var presentationStartTime = null;
var delay = 0;
if (this.isLive_) {
presentationStartTime = (Date.now() / 1000) - endTime;
// We should have a delay of at least 3 target durations.
delay = this.maxTargetDuration_ * 3;
}
this.presentationTimeline_ = new shaka.media.PresentationTimeline(
presentationStartTime, delay);
this.presentationTimeline_.setStatic(!this.isLive_);
};
/**
* @param {!shaka.hls.Playlist} playlist

@@ -1180,2 +1419,74 @@ * @return {shaka.media.InitSegmentReference}

/**
* Called when the update timer ticks.
*
* @private
*/
shaka.hls.HlsParser.prototype.onUpdate_ = function() {
goog.asserts.assert(this.updateTimer_, 'Should only be called by timer');
goog.asserts.assert(this.updatePeriod_ != null,
'There should be an update period');
shaka.log.info('Updating manifest...');
// Detect a call to stop()
if (!this.playerInterface_)
return;
this.updateTimer_ = null;
this.update().then(function() {
this.setUpdateTimer_(this.updatePeriod_);
}.bind(this)).catch(function(error) {
goog.asserts.assert(error instanceof shaka.util.Error,
'Should only receive a Shaka error');
// Try updating again, but ensure we haven't been destroyed.
if (this.playerInterface_) {
// We will retry updating, so override the severity of the error.
error.severity = shaka.util.Error.Severity.RECOVERABLE;
this.playerInterface_.onError(error);
this.setUpdateTimer_(0);
}
}.bind(this));
};
/**
* Sets the update timer.
*
* @param {?number} time in seconds
* @private
*/
shaka.hls.HlsParser.prototype.setUpdateTimer_ = function(time) {
if (this.updatePeriod_ == null || time == null)
return;
goog.asserts.assert(this.updateTimer_ == null,
'Timer should not be already set');
var callback = this.onUpdate_.bind(this);
this.updateTimer_ = window.setTimeout(callback, time * 1000);
};
/**
* @param {boolean} live
* @private
*/
shaka.hls.HlsParser.prototype.setLive_ = function(live) {
this.isLive_ = live;
if (this.presentationTimeline_)
this.presentationTimeline_.setStatic(!live);
if (!live) {
if (this.updateTimer_ != null) {
window.clearTimeout(this.updateTimer_);
this.updateTimer_ = null;
this.updatePeriod_ = null;
}
}
};
/**
* @const {!Object.<string, shaka.hls.HlsParser.DrmParser_>}

@@ -1182,0 +1493,0 @@ * @private

@@ -93,3 +93,3 @@ /**

var segmentsData = lines.splice(i, lines.length - i);
var segments = this.parseSegments_(segmentsData);
var segments = this.parseSegments_(segmentsData, tags);
return new shaka.hls.Playlist(uri, playlistType, tags, segments);

@@ -118,2 +118,3 @@ }

* @param {!Array.<string>} lines
* @param {!Array.<!shaka.hls.Tag>} playlistTags
* @return {!Array.<shaka.hls.Segment>}

@@ -123,9 +124,17 @@ * @private

*/
shaka.hls.ManifestTextParser.prototype.parseSegments_ = function(lines) {
shaka.hls.ManifestTextParser.prototype.parseSegments_ =
function(lines, playlistTags) {
/** @type {!Array.<shaka.hls.Segment>} */
var segments = [];
var tags = [];
/** @type {!Array.<shaka.hls.Tag>} */
var segmentTags = [];
lines.forEach(function(line) {
if (/^(#EXT)/.test(line)) {
var tag = this.parseTag_(line);
tags.push(tag);
if (shaka.hls.ManifestTextParser.MEDIA_PLAYLIST_TAGS
.indexOf(tag.name) >= 0) {
playlistTags.push(tag);
} else {
segmentTags.push(tag);
}
} else if (shaka.hls.Utils.isComment(line)) {

@@ -137,8 +146,7 @@ // Skip comments

// Uri appears after all the tags describing the segment.
var segment = new shaka.hls.Segment(uri, tags);
var segment = new shaka.hls.Segment(uri, segmentTags);
segments.push(segment);
tags = [];
segmentTags = [];
}
}.bind(this));
return segments;

@@ -230,3 +238,4 @@ };

'EXT-X-MAP',
'EXT-X-I-FRAMES-ONLY'
'EXT-X-I-FRAMES-ONLY',
'EXT-X-ENDLIST'
];

@@ -233,0 +242,0 @@

@@ -30,2 +30,3 @@ /**

goog.require('shaka.util.MapUtils');
goog.require('shaka.util.MimeUtils');
goog.require('shaka.util.PublicPromise');

@@ -281,3 +282,4 @@ goog.require('shaka.util.StringUtils');

var setServerCertificate = null;
if (this.currentDrmInfo_.serverCertificate) {
if (this.currentDrmInfo_.serverCertificate &&
this.currentDrmInfo_.serverCertificate.length) {
setServerCertificate = this.mediaKeys_.setServerCertificate(

@@ -507,6 +509,4 @@ this.currentDrmInfo_.serverCertificate).then(function(supported) {

var fullMimeType = stream.mimeType;
if (stream.codecs) {
fullMimeType += '; codecs="' + stream.codecs + '"';
}
var fullMimeType = shaka.util.MimeUtils.getFullType(
stream.mimeType, stream.codecs);

@@ -513,0 +513,0 @@ capabilities.push({

@@ -22,4 +22,4 @@ /**

goog.require('shaka.log');
goog.require('shaka.media.TextEngine');
goog.require('shaka.media.TimeRangesUtils');
goog.require('shaka.text.TextEngine');
goog.require('shaka.util.Error');

@@ -30,2 +30,3 @@ goog.require('shaka.util.EventManager');

goog.require('shaka.util.ManifestParserUtils');
goog.require('shaka.util.MimeUtils');
goog.require('shaka.util.PublicPromise');

@@ -45,3 +46,3 @@

* 'open' state.
* @param {TextTrack} textTrack The TextTrack to use for subtitles/captions.
* @param {shakaExtern.TextDisplayer} textDisplayer
*

@@ -52,3 +53,3 @@ * @struct

*/
shaka.media.MediaSourceEngine = function(video, mediaSource, textTrack) {
shaka.media.MediaSourceEngine = function(video, mediaSource, textDisplayer) {
goog.asserts.assert(mediaSource.readyState == 'open',

@@ -63,4 +64,4 @@ 'The MediaSource should be in the \'open\' state.');

/** @private {TextTrack} */
this.textTrack_ = textTrack;
/** @private {shakaExtern.TextDisplayer} */
this.textDisplayer_ = textDisplayer;

@@ -71,3 +72,3 @@ /** @private {!Object.<shaka.util.ManifestParserUtils.ContentType,

/** @private {shaka.media.TextEngine} */
/** @private {shaka.text.TextEngine} */
this.textEngine_ = null;

@@ -107,8 +108,12 @@

*
* @param {string} mimeType
* @param {shakaExtern.Stream} stream
* @return {boolean}
*/
shaka.media.MediaSourceEngine.isTypeSupported = function(mimeType) {
return shaka.media.TextEngine.isTypeSupported(mimeType) ||
MediaSource.isTypeSupported(mimeType);
shaka.media.MediaSourceEngine.isStreamSupported = function(stream) {
var fullMimeType = shaka.util.MimeUtils.getFullType(
stream.mimeType, stream.codecs);
var extendedMimeType = shaka.util.MimeUtils.getExtendedType(stream);
return shaka.text.TextEngine.isTypeSupported(fullMimeType) ||
MediaSource.isTypeSupported(extendedMimeType);
};

@@ -123,3 +128,3 @@

shaka.media.MediaSourceEngine.isBrowserSupported = function() {
return !!window.MediaSource && !!window.MediaSource.isTypeSupported;
return !!window.MediaSource && !!MediaSource.isTypeSupported;
};

@@ -141,3 +146,6 @@

'video/mp4; codecs="avc3.42E01E"',
'video/mp4; codecs="hev1.1.6.L93.90"',
'video/mp4; codecs="hvc1.1.6.L93.90"',
'video/mp4; codecs="hev1.2.4.L153.B0"; eotf="smpte2084"', // HDR HEVC
'video/mp4; codecs="hvc1.2.4.L153.B0"; eotf="smpte2084"', // HDR HEVC
'video/mp4; codecs="vp9"',

@@ -173,3 +181,5 @@ 'video/mp4; codecs="vp09.00.10.08"',

testMimeTypes.forEach(function(type) {
support[type] = shaka.media.MediaSourceEngine.isTypeSupported(type);
support[type] =
shaka.text.TextEngine.isTypeSupported(type) ||
MediaSource.isTypeSupported(type);
var basicType = type.split(';')[0];

@@ -221,4 +231,4 @@ support[basicType] = support[basicType] || support[type];

this.mediaSource_ = null;
this.textTrack_ = null;
this.textEngine_ = null;
this.textDisplayer_ = null;
this.sourceBuffers_ = {};

@@ -243,7 +253,6 @@ if (!COMPILED) {

*
* @param {!Object.<shaka.util.ManifestParserUtils.ContentType, string>}
* typeConfig A map of content types to full MIME types.
* For example: { 'audio': 'audio/webm; codecs="vorbis"',
* 'video': 'video/webm; codecs="vp9"', 'text': 'text/vtt' }.
* All types given must be supported.
* @param {!Object.<shaka.util.ManifestParserUtils.ContentType,
shakaExtern.Stream>} streamsByType
* A map of content types to streams. All streams must be supported according
* to MediaSourceEngine.isStreamSupported.
*

@@ -254,11 +263,13 @@ * @throws InvalidAccessError if blank MIME types are given

*/
shaka.media.MediaSourceEngine.prototype.init = function(typeConfig) {
shaka.media.MediaSourceEngine.prototype.init = function(streamsByType) {
var ContentType = shaka.util.ManifestParserUtils.ContentType;
for (var contentType in typeConfig) {
var mimeType = typeConfig[contentType];
for (var contentType in streamsByType) {
var stream = streamsByType[contentType];
goog.asserts.assert(
shaka.media.MediaSourceEngine.isTypeSupported(mimeType),
shaka.media.MediaSourceEngine.isStreamSupported(stream),
'Type negotiation should happen before MediaSourceEngine.init!');
var mimeType = shaka.util.MimeUtils.getFullType(
stream.mimeType, stream.codecs);
if (contentType == ContentType.TEXT) {

@@ -285,3 +296,3 @@ this.reinitText(mimeType);

if (!this.textEngine_) {
this.textEngine_ = new shaka.media.TextEngine(this.textTrack_);
this.textEngine_ = new shaka.text.TextEngine(this.textDisplayer_);
}

@@ -288,0 +299,0 @@ this.textEngine_.initParser(mimeType);

@@ -63,2 +63,5 @@ /**

this.static_ = true;
/** @private {number} */
this.segmentAvailabilityStart_ = 0;
};

@@ -160,2 +163,14 @@

/**
* Sets the presentation delay.
*
* @param {number} delay
* @export
*/
shaka.media.PresentationTimeline.prototype.setDelay = function(delay) {
goog.asserts.assert(delay >= 0, 'delay must be >= 0');
this.presentationDelay_ = delay;
};
/**
* Gives PresentationTimeline a Stream's segments so it can size and position

@@ -253,7 +268,7 @@ * the segment availability window, and account for missing segment

if (this.segmentAvailabilityDuration_ == Infinity)
return 0;
return this.segmentAvailabilityStart_;
var end = this.getSegmentAvailabilityEnd();
var start = Math.min(end - this.segmentAvailabilityDuration_ + offset, end);
return Math.max(0, start);
return Math.max(this.segmentAvailabilityStart_, start);
};

@@ -263,2 +278,14 @@

/**
* Sets the presentation's current segment availability start time.
*
* @param {number} time
* @export
*/
shaka.media.PresentationTimeline.prototype.setAvailabilityStart =
function(time) {
this.segmentAvailabilityStart_ = time;
};
/**
* Gets the presentation's current segment availability end time. Segments

@@ -265,0 +292,0 @@ * starting after this time should be assumed to be unavailable.

@@ -22,2 +22,3 @@ /**

goog.require('goog.asserts');
goog.require('shaka.net.Backoff');
goog.require('shaka.util.ConfigUtils');

@@ -27,3 +28,2 @@ goog.require('shaka.util.Error');

goog.require('shaka.util.IDestroyable');
goog.require('shaka.util.PublicPromise');

@@ -191,14 +191,8 @@

* @return {shakaExtern.RetryParameters}
*
* NOTE: The implementation moved to shaka.net.Backoff to avoid a circular
* dependency between the two classes.
*/
shaka.net.NetworkingEngine.defaultRetryParameters = function() {
// Use a function rather than a constant member so the calling code can
// modify the values without affecting other call results.
return {
maxAttempts: 2,
baseDelay: 1000,
backoffFactor: 2,
fuzzFactor: 0.5,
timeout: 0
};
};
shaka.net.NetworkingEngine.defaultRetryParameters =
shaka.net.Backoff.defaultRetryParameters;

@@ -298,20 +292,9 @@

var filterTimeMs = (Date.now() - filterStartMs);
var retry = request.retryParameters || {};
var maxAttempts = retry.maxAttempts || 1;
var backoffFactor = retry.backoffFactor || 2.0;
var delay = (retry.baseDelay == null ? 1000 : retry.baseDelay);
var p = this.send_(type, request, 0, filterTimeMs);
for (var i = 1; i < maxAttempts; i++) {
var index = i % request.uris.length;
p = p.catch(function(delay, index, err) {
if (err && err.severity == shaka.util.Error.Severity.RECOVERABLE)
return this.resend_(type, request, delay, index, filterTimeMs);
throw err;
}.bind(this, delay, index));
delay *= backoffFactor;
}
return p;
var backoff = new shaka.net.Backoff(request.retryParameters);
var index = 0;
// Every call to send_ must have an associated attempt() so that the
// accounting in backoff is correct.
return backoff.attempt().then(function() {
return this.send_(type, request, backoff, index, filterTimeMs);
}.bind(this));
}.bind(this));

@@ -346,6 +329,7 @@

/**
* Sends the given request to the correct plugin. This does not handle retry.
* Sends the given request to the correct plugin and retry using Backoff.
*
* @param {shaka.net.NetworkingEngine.RequestType} type
* @param {shakaExtern.Request} request
* @param {!shaka.net.Backoff} backoff
* @param {number} index

@@ -357,3 +341,3 @@ * @param {number} requestFilterTime

shaka.net.NetworkingEngine.prototype.send_ = function(
type, request, index, requestFilterTime) {
type, request, backoff, index, requestFilterTime) {
// Retries sent after destroy is called are rejected.

@@ -421,31 +405,20 @@ if (this.destroyed_)

});
}.bind(this));
};
}.bind(this)).catch(function(error) {
if (error && error.severity == shaka.util.Error.Severity.RECOVERABLE) {
// Move to the next URI.
index = (index + 1) % request.uris.length;
return backoff.attempt().then(function() {
// Delay has passed. Try again.
return this.send_(type, request, backoff, index, requestFilterTime);
}.bind(this), function() {
// No more attempts are allowed. Fail with the most recent error.
throw error;
});
}
/**
* Resends the request after applying a delay. This does not handle retry.
*
* @param {shaka.net.NetworkingEngine.RequestType} type
* @param {shakaExtern.Request} request
* @param {number} delayMs The current base delay.
* @param {number} index
* @param {number} requestFilterTime
* @return {!Promise.<shakaExtern.Response>}
* @private
*/
shaka.net.NetworkingEngine.prototype.resend_ =
function(type, request, delayMs, index, requestFilterTime) {
var p = new shaka.util.PublicPromise();
// Fuzz the delay to avoid tons of clients hitting the server at once
// after it recovers from whatever is causing it to fail.
var retry = request.retryParameters || {};
var fuzzFactor = (retry.fuzzFactor == null ? 0.5 : retry.fuzzFactor);
var negToPosOne = (Math.random() * 2.0) - 1.0;
var negToPosFuzzFactor = negToPosOne * fuzzFactor;
var fuzzedDelay = delayMs * (1.0 + negToPosFuzzFactor);
shaka.net.NetworkingEngine.setTimeout_(p.resolve, fuzzedDelay);
return p.then(this.send_.bind(this, type, request, index, requestFilterTime));
// The error was not recoverable, so do not try again.
// Rethrow the error so the Promise chain stays rejected.
throw error;
}.bind(this));
};

@@ -464,15 +437,1 @@

};
/**
* This is here only for testability. Mocking global setTimeout can lead to
* unintended interactions with other tests. So instead, we mock this.
*
* @param {Function} fn The callback to invoke when the timeout expires.
* @param {number} timeoutMs The timeout in milliseconds.
* @return {number} The timeout ID.
* @private
*/
shaka.net.NetworkingEngine.setTimeout_ = function(fn, timeoutMs) {
return window.setTimeout(fn, timeoutMs);
};

@@ -153,2 +153,3 @@ /**

shaka.offline.DBEngine.prototype.get = function(storeName, key) {
/** @type {!IDBRequest} */
var request;

@@ -155,0 +156,0 @@ return this.createTransaction_(storeName, 'readonly', function(store) {

@@ -260,3 +260,4 @@ /**

containsEmsgBoxes: false,
roles: []
roles: [],
channelsCount: null
};

@@ -263,0 +264,0 @@ };

@@ -152,3 +152,5 @@ /**

*
* @param {shakaExtern.OfflineConfiguration} config
* @param {!Object} config This should follow the form of
* {@link shakaExtern.OfflineConfiguration}, but you may omit any field you do
* not wish to change.
* @export

@@ -169,4 +171,4 @@ */

* @param {string} manifestUri The URI of the manifest to store.
* @param {!Object} appMetadata An arbitrary object from the application that
* will be stored along-side the offline content. Use this for any
* @param {!Object=} opt_appMetadata An arbitrary object from the application
* that will be stored along-side the offline content. Use this for any
* application-specific metadata you need associated with the stored content.

@@ -183,3 +185,3 @@ * For details on the data types that can be stored here, please refer to

shaka.offline.Storage.prototype.store = function(
manifestUri, appMetadata, opt_manifestParserFactory) {
manifestUri, opt_appMetadata, opt_manifestParserFactory) {
if (this.storeInProgress_) {

@@ -227,3 +229,4 @@ return Promise.reject(new shaka.util.Error(

this.duration_ = 0;
manifestDb = this.createOfflineManifest_(manifestUri, appMetadata);
manifestDb =
this.createOfflineManifest_(manifestUri, opt_appMetadata || {});
return this.downloadManager_.downloadAndStore(manifestDb);

@@ -305,3 +308,4 @@ })

drmEngine.configure(this.player_.getConfiguration().drm);
return drmEngine.init(manifest, true /* isOffline */);
var isOffline = this.config_.usePersistentLicense || false;
return drmEngine.init(manifest, isOffline);
})

@@ -417,3 +421,3 @@ .bind(this)).then(function() {

networkingEngine: netEngine,
filterPeriod: this.filterPeriod_.bind(this),
filterNewPeriod: this.filterPeriod_.bind(this),
onTimelineRegionAdded: function() {},

@@ -431,3 +435,4 @@ onEvent: function() {},

drmEngine.configure(config.drm);
return drmEngine.init(manifest, true /* isOffline */);
var isOffline = this.config_.usePersistentLicense || false;
return drmEngine.init(manifest, isOffline);
}.bind(this))

@@ -577,3 +582,4 @@ .then(function() {

if (storedContent || percent) return null;
}
},
usePersistentLicense: true
};

@@ -629,3 +635,3 @@ };

}
StreamUtils.filterPeriod(this.drmEngine_, activeStreams, period);
StreamUtils.filterNewPeriod(this.drmEngine_, activeStreams, period);
StreamUtils.applyRestrictions(

@@ -718,3 +724,3 @@ period, this.player_.getConfiguration().restrictions,

periods: periods,
sessionIds: sessions,
sessionIds: this.config_.usePersistentLicense ? sessions : [],
drmInfo: drmInfo,

@@ -721,0 +727,0 @@ appMetadata: appMetadata

@@ -37,41 +37,42 @@ /**

// MediaSource bugs are difficult to detect without checking for the affected
// platform. SourceBuffer is not always exposed on window, for example, and
// instances are only accessible after setting up MediaSource on a video
// element. Because of this, we use UA detection and other platform detection
// tricks to decide which patches to install.
if (!window.MediaSource) {
shaka.log.info('No MSE implementation available.');
return;
}
} else if (window.cast && cast.__platform__ &&
cast.__platform__.canDisplayType) {
shaka.log.info('Patching Chromecast MSE bugs.');
// Chromecast cannot make accurate determinations via isTypeSupported.
shaka.polyfill.MediaSource.patchCastIsTypeSupported_();
} else if (navigator.vendor && navigator.vendor.indexOf('Apple') >= 0) {
var version = navigator.appVersion;
// Detection is complicated by the fact that Safari does not expose
// SourceBuffer on window. So we can't detect missing features by accessing
// SourceBuffer.prototype. That is why we use navigator to detect Safari and
// particular versions of it.
var vendor = navigator.vendor;
var version = navigator.appVersion;
if (!vendor || !version || vendor.indexOf('Apple') < 0) {
shaka.log.info('Using native MSE as-is.');
return;
}
if (version.indexOf('Version/8') >= 0) {
// Safari 8 does not implement appendWindowEnd. If we ignore the
// incomplete MSE implementation, some content (especially multi-period)
// will fail to play correctly. The best we can do is blacklist Safari 8.
shaka.log.info('Blacklisting Safari 8 MSE.');
shaka.polyfill.MediaSource.blacklist_();
} else if (version.indexOf('Version/9') >= 0) {
shaka.log.info('Patching Safari 9 MSE bugs.');
// Safari 9 does not correctly implement abort() on SourceBuffer.
// Calling abort() causes a decoder failure, rather than resetting the
// decode timestamp as called for by the spec.
// Bug filed: http://goo.gl/UZ2rPp
shaka.polyfill.MediaSource.stubAbort_();
} else if (version.indexOf('Version/10') >= 0) {
shaka.log.info('Patching Safari 10 MSE bugs.');
// Safari 10 does not correctly implement abort() on SourceBuffer.
// Calling abort() before appending a segment causes that segment to be
// incomplete in buffer.
// Bug filed: https://goo.gl/rC3CLj
shaka.polyfill.MediaSource.stubAbort_();
// Safari 10 fires spurious 'updateend' events after endOfStream().
// Bug filed: https://goo.gl/qCeTZr
shaka.polyfill.MediaSource.patchEndOfStreamEvents_();
if (version.indexOf('Version/8') >= 0) {
// Safari 8 does not implement appendWindowEnd. If we ignore the
// incomplete MSE implementation, some content (especially multi-period)
// will fail to play correctly. The best we can do is blacklist Safari 8.
shaka.log.info('Blacklisting Safari 8 MSE.');
shaka.polyfill.MediaSource.blacklist_();
} else if (version.indexOf('Version/9') >= 0) {
shaka.log.info('Patching Safari 9 MSE bugs.');
// Safari 9 does not correctly implement abort() on SourceBuffer.
// Calling abort() causes a decoder failure, rather than resetting the
// decode timestamp as called for by the spec.
// Bug filed: http://goo.gl/UZ2rPp
shaka.polyfill.MediaSource.stubAbort_();
} else if (version.indexOf('Version/10') >= 0) {
shaka.log.info('Patching Safari 10 MSE bugs.');
// Safari 10 does not correctly implement abort() on SourceBuffer.
// Calling abort() before appending a segment causes that segment to be
// incomplete in buffer.
// Bug filed: https://goo.gl/rC3CLj
shaka.polyfill.MediaSource.stubAbort_();
// Safari 10 fires spurious 'updateend' events after endOfStream().
// Bug filed: https://goo.gl/qCeTZr
shaka.polyfill.MediaSource.patchEndOfStreamEvents_();
}
} else {

@@ -144,2 +145,3 @@ shaka.log.info('Using native MSE as-is.');

var cleanUpHandlerInstalled = false;
var addSourceBuffer = MediaSource.prototype.addSourceBuffer;

@@ -150,7 +152,7 @@ MediaSource.prototype.addSourceBuffer = function() {

var sourceBuffer = addSourceBuffer.apply(this, arguments);
sourceBuffer.mediaSource_ = this;
sourceBuffer['mediaSource_'] = this;
sourceBuffer.addEventListener('updateend',
shaka.polyfill.MediaSource.ignoreUpdateEnd_, false);
if (!this.cleanUpHandlerInstalled_) {
if (!cleanUpHandlerInstalled) {
// If we haven't already, install an event listener to allow us to clean

@@ -160,3 +162,3 @@ // up listeners when MediaSource is torn down.

shaka.polyfill.MediaSource.cleanUpListeners_, false);
this.cleanUpHandlerInstalled_ = true;
cleanUpHandlerInstalled = true;
}

@@ -178,3 +180,3 @@ return sourceBuffer;

var sourceBuffer = event.target;
var mediaSource = sourceBuffer.mediaSource_;
var mediaSource = sourceBuffer['mediaSource_'];

@@ -213,3 +215,3 @@ if (mediaSource.ignoreUpdateEnd_) {

shaka.polyfill.MediaSource.cleanUpListeners_ = function(event) {
var mediaSource = event.target;
var mediaSource = /** @type {!MediaSource} */ (event.target);
for (var i = 0; i < mediaSource.sourceBuffers.length; ++i) {

@@ -225,2 +227,79 @@ var buffer = mediaSource.sourceBuffers[i];

/**
* Patch isTypeSupported() to parse for HDR-related clues and chain to a private
* API on the Chromecast which can query for support.
*
* @private
*/
shaka.polyfill.MediaSource.patchCastIsTypeSupported_ = function() {
var originalIsTypeSupported = MediaSource.isTypeSupported;
// Docs from Dolby detailing profile names: https://goo.gl/LVVXrS
var dolbyVisionRegex = /^dv(?:he|av)\./;
MediaSource.isTypeSupported = function(mimeType) {
// Parse the basic MIME type from its parameters.
var pieces = mimeType.split(/ *; */);
var basicMimeType = pieces[0];
// Parse the parameters into key-value pairs.
/** @type {Object.<string, string>} */
var parameters = {};
for (var i = 1; i < pieces.length; ++i) {
var kv = pieces[i].split('=');
var k = kv[0];
var v = kv[1].replace(/"(.*)"/, '$1');
parameters[k] = v;
}
var codecs = parameters['codecs'];
if (!codecs) {
return originalIsTypeSupported(mimeType);
}
var isHDR = false;
var isDolbyVision = false;
var codecList = codecs.split(',').filter(function(codec) {
// Remove Dolby Vision codec strings. These are not understood on
// Chromecast, even though the content can still be played.
if (dolbyVisionRegex.test(codec)) {
isDolbyVision = true;
// Return false to remove this string from the list.
return false;
}
// We take this string as a signal for HDR, but don't remove it.
if (/^(hev|hvc)1\.2/.test(codec)) {
isHDR = true;
}
// Keep all other strings in the list.
return true;
});
// If the content uses Dolby Vision, we take this as a sign that the content
// is not HDR after all.
if (isDolbyVision) isHDR = false;
// Reconstruct the "codecs" parameter from the results of the filter.
parameters['codecs'] = codecList.join(',');
// If the content is HDR, we add this additional parameter so that the Cast
// platform will check for HDR support.
if (isHDR) {
parameters['eotf'] = 'smpte2084';
}
// Reconstruct the MIME type, possibly with additional parameters.
var extendedMimeType = basicMimeType;
for (var k in parameters) {
var v = parameters[k];
extendedMimeType += '; ' + k + '="' + v + '"';
}
return cast.__platform__.canDisplayType(extendedMimeType);
};
};
shaka.polyfill.register(shaka.polyfill.MediaSource.install);

@@ -379,6 +379,6 @@ /**

// Promises that are resolved later
/** @private {Promise} */
/** @private {shaka.util.PublicPromise} */
this.generateRequestPromise_ = null;
/** @private {Promise} */
/** @private {shaka.util.PublicPromise} */
this.updatePromise_ = null;

@@ -398,3 +398,3 @@

/** @type {!MediaKeyStatusMap} */
/** @type {!shaka.polyfill.PatchedMediaKeysMs.MediaKeyStatusMap} */
this.keyStatuses =

@@ -401,0 +401,0 @@ new shaka.polyfill.PatchedMediaKeysMs.MediaKeyStatusMap();

@@ -440,3 +440,4 @@ /**

var event2 = document.createEvent('CustomEvent');
var event2 =
/** @type {!CustomEvent} */ (document.createEvent('CustomEvent'));
event2.initCustomEvent('encrypted', false, false, null);

@@ -580,3 +581,3 @@

/** @type {!MediaKeyStatusMap} */
/** @type {!shaka.polyfill.PatchedMediaKeysWebkit.MediaKeyStatusMap} */
this.keyStatuses =

@@ -583,0 +584,0 @@ new shaka.polyfill.PatchedMediaKeysWebkit.MediaKeyStatusMap();

@@ -34,3 +34,3 @@ /**

* @struct
* @param {function(function(*), function(*))=} opt_callback
* @param {function(function(*=), function(*=))=} opt_callback
* @template T

@@ -162,10 +162,10 @@ */

/**
* @param {*} value
* @param {*=} opt_value
* @return {!shaka.polyfill.Promise}
*/
shaka.polyfill.Promise.resolve = function(value) {
shaka.polyfill.Promise.resolve = function(opt_value) {
var p = new shaka.polyfill.Promise();
p.resolve_(undefined);
return p.then(function() {
return value;
return opt_value;
});

@@ -176,8 +176,8 @@ };

/**
* @param {*} reason
* @param {*=} opt_reason
* @return {!shaka.polyfill.Promise}
*/
shaka.polyfill.Promise.reject = function(reason) {
shaka.polyfill.Promise.reject = function(opt_reason) {
var p = new shaka.polyfill.Promise();
p.reject_(reason);
p.reject_(opt_reason);
return p;

@@ -295,9 +295,9 @@ };

/**
* @param {*} value
* @param {*=} opt_value
* @private
*/
shaka.polyfill.Promise.prototype.resolve_ = function(value) {
shaka.polyfill.Promise.prototype.resolve_ = function(opt_value) {
// Ignore resolve calls if we aren't still pending.
if (this.state_ == shaka.polyfill.Promise.State.PENDING) {
this.value_ = value;
this.value_ = opt_value;
this.state_ = shaka.polyfill.Promise.State.RESOLVED;

@@ -315,9 +315,9 @@ // Schedule calls to all of the chained callbacks.

/**
* @param {*} reason
* @param {*=} opt_reason
* @private
*/
shaka.polyfill.Promise.prototype.reject_ = function(reason) {
shaka.polyfill.Promise.prototype.reject_ = function(opt_reason) {
// Ignore reject calls if we aren't still pending.
if (this.state_ == shaka.polyfill.Promise.State.PENDING) {
this.value_ = reason;
this.value_ = opt_reason;
this.state_ = shaka.polyfill.Promise.State.REJECTED;

@@ -324,0 +324,0 @@ // Schedule calls to all of the chained callbacks.

@@ -71,1 +71,13 @@ /**

/**
* Remove given element from array (assumes no duplicates).
* @param {!Array.<T>} array
* @param {T} element
* @template T
*/
shaka.util.ArrayUtils.remove = function(array, element) {
var index = array.indexOf(element);
if (index > -1)
array.splice(index, 1);
};

@@ -48,12 +48,2 @@ /**

/**
* @type {boolean}
* If true, simply copy the object over and don't verify.
*/
var copyObject = !!({
'.abr.manager': true
})[subPath] || !!({
'serverCertificate': true
})[k];
// The order of these checks is important.

@@ -67,13 +57,29 @@ if (!ignoreKeys && !(k in destination)) {

if (subTemplate === undefined || ignoreKeys) {
// There is nothing in the template, so delete.
delete destination[k];
} else {
// There is something in the template, so go back to that.
destination[k] = subTemplate;
}
} else if (copyObject) {
destination[k] = source[k];
} else if (typeof destination[k] == 'object' &&
typeof source[k] == 'object') {
} else if (subTemplate.constructor == Object &&
source[k] &&
source[k].constructor == Object) {
// These are plain Objects with no other constructor.
if (!destination[k]) {
// The only way we should see a null destination is when ignoreKeys is
// true, so assert that it is.
goog.asserts.assert(ignoreKeys, 'Null destination without ignoreKeys!');
// Initialize the destination with the template so that normal merging
// and type-checking can happen.
destination[k] = subTemplate;
}
shaka.util.ConfigUtils.mergeConfigObjects(
destination[k], source[k], subTemplate, overrides, subPath);
} else if (typeof source[k] != typeof subTemplate) {
} else if (typeof source[k] != typeof subTemplate ||
source[k] == null ||
source[k].constructor != subTemplate.constructor) {
// The source is the wrong type. This check allows objects to be nulled,
// but does not allow null for any non-object fields.
shaka.log.error('Invalid config, wrong type for ' + subPath);

@@ -80,0 +86,0 @@ } else if (typeof destination[k] == 'function' &&

@@ -40,2 +40,3 @@ /**

this.data = Array.prototype.slice.call(arguments, 3);
this.handled = false;

@@ -110,2 +111,9 @@ // This improves formatting of Errors in failure messages in the tests.

/**
* @type {boolean}
* @expose
*/
shaka.util.Error.prototype.handled;
/**
* @return {string}

@@ -254,2 +262,3 @@ * @override

* elements for TTML.
* <br> error.data[0] is the URI associated with the XML.
*/

@@ -533,2 +542,14 @@ 'INVALID_XML': 2005,

/**
* The manifest parser only supports xlink links with
* xlink:actuate="onLoad".
*/
'DASH_UNSUPPORTED_XLINK_ACTUATE': 4027,
/**
* The manifest parser has hit its depth limit on
* xlink link chains.
*/
'DASH_XLINK_DEPTH_LIMIT': 4028,
/**
* HLS parser encountered a live playlist.

@@ -766,3 +787,9 @@ */

*/
'LOCAL_PLAYER_INSTANCE_REQUIRED': 9008
'LOCAL_PLAYER_INSTANCE_REQUIRED': 9008,
/**
* When the manifest contains no period playable streams, it means the
* manifest is unsupported by the browser.
*/
'CONTENT_UNSUPPORTED_BY_BROWSER': 9009
};

@@ -79,28 +79,2 @@ /**

/**
* Creates a function that returns whether the given value is equal to the given
* value.
*
* @param {T} compare
* @return {function(T):boolean}
* @template T
*/
shaka.util.Functional.isEqualFunc = function(compare) {
return function(a) { return a == compare; };
};
/**
* Creates a function that returns whether the given value is not equal to the
* given value.
*
* @param {T} compare
* @return {function(T):boolean}
* @template T
*/
shaka.util.Functional.isNotEqualFunc = function(compare) {
return function(a) { return a != compare; };
};
/**
* Used to filter out duplicates in an array.

@@ -107,0 +81,0 @@ * Returns true the first time the element is encountered. Returns false

@@ -24,3 +24,3 @@ /**

goog.require('shaka.media.MediaSourceEngine');
goog.require('shaka.media.TextEngine');
goog.require('shaka.text.TextEngine');
goog.require('shaka.util.ArrayUtils');

@@ -30,5 +30,12 @@ goog.require('shaka.util.Functional');

goog.require('shaka.util.ManifestParserUtils');
goog.require('shaka.util.MimeUtils');
/**
* @namespace shaka.util.StreamUtils
* @summary A set of utility functions for dealing with Streams and Manifests.
*/
/**
* @param {shakaExtern.Variant} variant

@@ -98,3 +105,3 @@ * @param {shakaExtern.Restrictions} restrictions

*/
shaka.util.StreamUtils.filterPeriod = function(
shaka.util.StreamUtils.filterNewPeriod = function(
drmEngine, activeStreams, period) {

@@ -123,5 +130,5 @@ var StreamUtils = shaka.util.StreamUtils;

var stream = period.textStreams[i];
var fullMimeType = StreamUtils.getFullMimeType(
var fullMimeType = shaka.util.MimeUtils.getFullType(
stream.mimeType, stream.codecs);
if (!shaka.media.TextEngine.isTypeSupported(fullMimeType)) {
if (!shaka.text.TextEngine.isTypeSupported(fullMimeType)) {
shaka.log.debug('Dropping text stream. Is not supported by the ' +

@@ -160,6 +167,6 @@ 'platform.', stream);

// Check if stream can be played by the platform
var fullMimeType = shaka.util.StreamUtils.getFullMimeType(
var fullMimeType = shaka.util.MimeUtils.getFullType(
stream.mimeType, stream.codecs);
if (!shaka.media.MediaSourceEngine.isTypeSupported(fullMimeType))
if (!shaka.media.MediaSourceEngine.isStreamSupported(stream))
return false;

@@ -279,3 +286,8 @@

videoId: variant.video ? variant.video.id : null,
audioId: variant.audio ? variant.audio.id : null
audioId: variant.audio ? variant.audio.id : null,
channelsCount: variant.audio ? variant.audio.channelsCount : null,
audioBandwidth: (variant.audio && variant.audio.bandwidth) ?
variant.audio.bandwidth : null,
videoBandwidth: (variant.video && variant.video.bandwidth) ?
variant.video.bandwidth : null
};

@@ -310,3 +322,6 @@ });

primary: stream.primary,
roles: stream.roles
roles: stream.roles,
channelsCount: null,
audioBandwidth: null,
videoBandwidth: null
};

@@ -628,18 +643,2 @@ });

/**
* Takes a MIME type and optional codecs string and produces the full MIME type.
*
* @param {string} mimeType
* @param {string=} opt_codecs
* @return {string}
*/
shaka.util.StreamUtils.getFullMimeType = function(mimeType, opt_codecs) {
var fullMimeType = mimeType;
if (opt_codecs) {
fullMimeType += '; codecs="' + opt_codecs + '"';
}
return fullMimeType;
};
/**
* Gets the index of the Period that contains the given time.

@@ -694,2 +693,22 @@ * @param {shakaExtern.Manifest} manifest

/**
* @param {shakaExtern.Manifest} manifest
* @param {shakaExtern.Variant} variant
* @return {number} The index of the Period which contains |stream|, or -1 if
* no Period contains |stream|.
*/
shaka.util.StreamUtils.findPeriodContainingVariant =
function(manifest, variant) {
for (var periodIdx = 0; periodIdx < manifest.periods.length; ++periodIdx) {
var period = manifest.periods[periodIdx];
for (var j = 0; j < period.variants.length; ++j) {
if (period.variants[j] == variant) {
return periodIdx;
}
}
}
return -1;
};
/**
* Gets the rebuffering goal from the manifest and configuration.

@@ -696,0 +715,0 @@ *

{
"name": "shaka-player",
"description": "DASH/EME video player library",
"version": "2.1.8",
"version": "2.2.0",
"homepage": "https://github.com/google/shaka-player",

@@ -36,3 +36,4 @@ "author": "Google",

"sprintf-js": "~1.0.3",
"useragent": "~2.1.13"
"useragent": "~2.1.13",
"which": "~1.3.0"
},

@@ -39,0 +40,0 @@ "main": "dist/shaka-player.compiled.js",

@@ -32,10 +32,5 @@ /**

goog.require('shaka.media.ManifestParser');
goog.require('shaka.media.Mp4TtmlParser');
goog.require('shaka.media.Mp4VttParser');
goog.require('shaka.media.PresentationTimeline');
goog.require('shaka.media.SegmentIndex');
goog.require('shaka.media.SegmentReference');
goog.require('shaka.media.TextEngine');
goog.require('shaka.media.TtmlTextParser');
goog.require('shaka.media.VttTextParser');
goog.require('shaka.net.DataUriPlugin');

@@ -57,2 +52,9 @@ goog.require('shaka.net.HttpPlugin');

goog.require('shaka.polyfill.installAll');
goog.require('shaka.text.Cue');
goog.require('shaka.text.Mp4TtmlParser');
goog.require('shaka.text.Mp4VttParser');
goog.require('shaka.text.SimpleTextDisplayer');
goog.require('shaka.text.TextEngine');
goog.require('shaka.text.TtmlTextParser');
goog.require('shaka.text.VttTextParser');
goog.require('shaka.util.Error');

@@ -19,9 +19,28 @@ /**

describe('SimpleAbrManager', function() {
/** @const */
var sufficientBWMultiplier = 1.06;
/** @const */
var defaultBandwidthEstimate = 500e3; // 500kbps
/** @const */
var defaultRestrictions = {
minWidth: 0,
maxWidth: Infinity,
minHeight: 0,
maxHeight: Infinity,
minPixels: 0,
maxPixels: Infinity,
minBandwidth: 0,
maxBandwidth: Infinity
};
/** @type {shakaExtern.AbrConfiguration} */
var config;
/** @type {!jasmine.Spy} */
var switchCallback;
/** @type {!shaka.abr.SimpleAbrManager} */
var abrManager;
/** @type {shakaExtern.Manifest} */
var manifest;
/** @type {!Array.<shakaExtern.Variant>} */
var variants;
var textStreams;
var sufficientBWMultiplier = 1.06;
var ContentType = shaka.util.ManifestParserUtils.ContentType;

@@ -64,9 +83,19 @@

config = {
enabled: true,
defaultBandwidthEstimate: defaultBandwidthEstimate,
switchInterval: 8,
bandwidthUpgradeTarget: 0.85,
bandwidthDowngradeTarget: 0.95,
restrictions: defaultRestrictions
};
variants = manifest.periods[0].variants;
textStreams = manifest.periods[0].textStreams;
abrManager = new shaka.abr.SimpleAbrManager();
abrManager.init(switchCallback);
abrManager.init(shaka.test.Util.spyFunc(switchCallback));
config.defaultBandwidthEstimate = defaultBandwidthEstimate;
config.restrictions = defaultRestrictions;
abrManager.configure(config);
abrManager.setVariants(variants);
abrManager.setTextStreams(textStreams);
});

@@ -84,22 +113,17 @@

it('can choose audio and video Streams right away', function() {
var chosen = abrManager.chooseStreams([ContentType.AUDIO,
ContentType.VIDEO]);
expect(chosen[ContentType.AUDIO]).toBeTruthy();
expect(chosen[ContentType.VIDEO]).toBeTruthy();
var chosen = abrManager.chooseVariant();
expect(chosen).not.toBe(null);
});
it('uses custom default estimate', function() {
abrManager.setDefaultEstimate(3e6);
var chosen = abrManager.chooseStreams([ContentType.AUDIO,
ContentType.VIDEO]);
expect(chosen[ContentType.VIDEO].id).toBe(6);
config.defaultBandwidthEstimate = 3e6;
abrManager.configure(config);
var chosen = abrManager.chooseVariant();
expect(chosen.id).toBe(4);
});
it('can handle empty variants', function() {
var ContentType = shaka.util.ManifestParserUtils.ContentType;
abrManager.setVariants([]);
abrManager.setTextStreams([]);
var chosen = abrManager.chooseStreams([ContentType.AUDIO,
ContentType.VIDEO]);
expect(Object.keys(chosen).length).toBe(0);
var chosen = abrManager.chooseVariant();
expect(chosen).toEqual(null);
});

@@ -117,6 +141,6 @@

abrManager.setVariants(manifest.periods[0].variants);
var chosen = abrManager.chooseStreams([ContentType.AUDIO]);
expect(chosen[ContentType.AUDIO]).toBeTruthy();
expect(chosen[ContentType.VIDEO]).toBeFalsy();
var chosen = abrManager.chooseVariant();
expect(chosen).not.toBe(null);
expect(chosen.audio).not.toBe(null);
expect(chosen.video).toBe(null);
});

@@ -134,6 +158,6 @@

abrManager.setVariants(manifest.periods[0].variants);
var chosen = abrManager.chooseStreams([ContentType.VIDEO]);
expect(chosen[ContentType.VIDEO]).toBeTruthy();
expect(chosen[ContentType.AUDIO]).toBeFalsy();
var chosen = abrManager.chooseVariant();
expect(chosen).not.toBe(null);
expect(chosen.audio).toBe(null);
expect(chosen.video).not.toBe(null);
});

@@ -153,3 +177,3 @@

abrManager.setVariants(variants);
abrManager.chooseStreams([ContentType.AUDIO, ContentType.VIDEO]);
abrManager.chooseVariant();

@@ -167,17 +191,5 @@ abrManager.segmentDownloaded(1000, bytesPerSecond);

// and variant 5 - for bandwidth = 6e5
var audioStream = variants[2].audio;
var videoStream = variants[2].video;
var expectedVariant = (bandwidth == 6e5) ? variants[5] : variants[2];
if (bandwidth == 6e5) {
audioStream = variants[5].audio;
videoStream = variants[5].video;
}
expect(switchCallback).toHaveBeenCalled();
// Create empty object first and initialize the fields through
// [] to allow field names to be expressions.
var expectedObject = {};
expectedObject[ContentType.AUDIO] = audioStream;
expectedObject[ContentType.VIDEO] = videoStream;
expect(switchCallback.calls.argsFor(0)[0]).toEqual(expectedObject);
expect(switchCallback).toHaveBeenCalledWith(expectedVariant);
});

@@ -195,3 +207,3 @@ });

abrManager.setVariants(variants);
abrManager.chooseStreams([ContentType.AUDIO, ContentType.VIDEO]);
abrManager.chooseVariant();

@@ -214,3 +226,3 @@ // 0 duration segment shouldn't cause us to get stuck on the lowest variant

abrManager.setVariants(variants);
abrManager.chooseStreams([ContentType.AUDIO, ContentType.VIDEO]);
abrManager.chooseVariant();

@@ -232,12 +244,5 @@ // Simulate some segments being downloaded just above the desired

// Expect variants 4 to be chosen
var videoStream = variants[4].video;
var audioStream = variants[4].audio;
var expectedVariant = variants[4];
expect(switchCallback).toHaveBeenCalled();
// Create empty object first and initialize the fields through
// [] to allow field names to be expressions.
var expectedObject = {};
expectedObject[ContentType.AUDIO] = audioStream;
expectedObject[ContentType.VIDEO] = videoStream;
expect(switchCallback.calls.argsFor(0)[0]).toEqual(expectedObject);
expect(switchCallback).toHaveBeenCalledWith(expectedVariant);
});

@@ -251,3 +256,3 @@

abrManager.setVariants(variants);
abrManager.chooseStreams([ContentType.AUDIO, ContentType.VIDEO]);
abrManager.chooseVariant();

@@ -267,3 +272,3 @@ // Don't enable AbrManager.

abrManager.setVariants(variants);
abrManager.chooseStreams([ContentType.AUDIO, ContentType.VIDEO]);
abrManager.chooseVariant();

@@ -293,4 +298,3 @@ abrManager.segmentDownloaded(1000, bytesPerSecond);

// Stay inside switch interval.
shaka.test.Util.fakeEventLoop(
(shaka.abr.SimpleAbrManager.SWITCH_INTERVAL_MS / 1000.0) - 2);
shaka.test.Util.fakeEventLoop(config.switchInterval - 2);
abrManager.segmentDownloaded(1000, bytesPerSecond);

@@ -315,3 +319,3 @@

abrManager.setVariants(variants);
abrManager.chooseStreams([ContentType.AUDIO, ContentType.VIDEO]);
abrManager.chooseVariant();

@@ -340,6 +344,7 @@ abrManager.segmentDownloaded(1000, bytesPerSecond);

// Set the default high so that the initial choice will be high-quality.
abrManager.setDefaultEstimate(4e6);
config.defaultBandwidthEstimate = 4e6;
abrManager.configure(config);
abrManager.setVariants(variants);
abrManager.chooseStreams([ContentType.AUDIO, ContentType.VIDEO]);
abrManager.chooseVariant();

@@ -370,9 +375,10 @@ abrManager.segmentDownloaded(1000, bytesPerSecond);

abrManager.setVariants(manifest.periods[0].variants);
var chosen = abrManager.chooseStreams([ContentType.VIDEO]);
expect(chosen[ContentType.VIDEO].id).toBe(2);
var chosen = abrManager.chooseVariant();
expect(chosen.id).toBe(1);
abrManager.setRestrictions({maxWidth: 100});
chosen = abrManager.chooseStreams([ContentType.VIDEO]);
expect(chosen[ContentType.VIDEO].id).toBe(0);
config.restrictions.maxWidth = 100;
abrManager.configure(config);
chosen = abrManager.chooseVariant();
expect(chosen.id).toBe(0);
});
});

@@ -19,11 +19,19 @@ /**

describe('CastProxy', function() {
var CastProxy;
var FakeEvent;
/** @const */
var CastProxy = shaka.cast.CastProxy;
/** @const */
var FakeEvent = shaka.util.FakeEvent;
/** @const */
var Util = shaka.test.Util;
var originalCastSender;
/** @const */
var originalCastSender = shaka.cast.CastSender;
/** @const */
var fakeAppId = 'fake app ID';
var fakeAppId = 'fake app ID';
var mockVideo;
var mockPlayer;
var mockSender;
/** @type {!shaka.test.FakeVideo} */
var mockVideo;
/** @type {!jasmine.Spy} */
var mockCastSenderConstructor;

@@ -35,10 +43,6 @@

beforeAll(function() {
CastProxy = shaka.cast.CastProxy;
FakeEvent = shaka.util.FakeEvent;
mockCastSenderConstructor = jasmine.createSpy('CastSender constructor');
mockCastSenderConstructor.and.callFake(createMockCastSender);
originalCastSender = shaka.cast.CastSender;
shaka.cast.CastSender = mockCastSenderConstructor;
shaka.cast.CastSender = Util.spyFunc(mockCastSenderConstructor);
});

@@ -305,3 +309,4 @@

var proxyListener = jasmine.createSpy('listener');
proxy.getVideo().addEventListener('timeupdate', proxyListener);
proxy.getVideo().addEventListener(
'timeupdate', Util.spyFunc(proxyListener));

@@ -319,3 +324,4 @@ expect(proxyListener).not.toHaveBeenCalled();

var proxyListener = jasmine.createSpy('listener');
proxy.getVideo().addEventListener('timeupdate', proxyListener);
proxy.getVideo().addEventListener(
'timeupdate', Util.spyFunc(proxyListener));

@@ -336,3 +342,4 @@ // Set up the sender in casting mode:

var proxyListener = jasmine.createSpy('listener');
proxy.getVideo().addEventListener('timeupdate', proxyListener);
proxy.getVideo().addEventListener(
'timeupdate', Util.spyFunc(proxyListener));

@@ -451,3 +458,4 @@ // Set up the sender in casting mode:

var proxyListener = jasmine.createSpy('listener');
proxy.getPlayer().addEventListener('buffering', proxyListener);
proxy.getPlayer().addEventListener(
'buffering', Util.spyFunc(proxyListener));

@@ -465,3 +473,4 @@ expect(proxyListener).not.toHaveBeenCalled();

var proxyListener = jasmine.createSpy('listener');
proxy.getPlayer().addEventListener('buffering', proxyListener);
proxy.getPlayer().addEventListener(
'buffering', Util.spyFunc(proxyListener));

@@ -482,3 +491,4 @@ // Set up the sender in casting mode:

var proxyListener = jasmine.createSpy('listener');
proxy.getPlayer().addEventListener('buffering', proxyListener);
proxy.getPlayer().addEventListener(
'buffering', Util.spyFunc(proxyListener));

@@ -503,3 +513,3 @@ // Set up the sender in casting mode:

var listener = jasmine.createSpy('listener');
proxy.addEventListener('caststatuschanged', listener);
proxy.addEventListener('caststatuschanged', Util.spyFunc(listener));
expect(listener).not.toHaveBeenCalled();

@@ -506,0 +516,0 @@ mockSender.onCastStatusChanged();

@@ -19,12 +19,20 @@ /**

describe('CastReceiver', function() {
var CastReceiver;
var CastUtils;
/** @const */
var CastReceiver = shaka.cast.CastReceiver;
/** @const */
var CastUtils = shaka.cast.CastUtils;
/** @const */
var Util = shaka.test.Util;
var originalCast;
var originalUserAgent;
/** @const */
var originalCast = window['cast'];
/** @const */
var originalUserAgent = navigator.userAgent;
var mockReceiverManager;
/** @type {!shaka.test.FakeVideo} */
var mockVideo;
/** @type {!jasmine.Spy} */
var mockAppDataCallback;
var mockPlayer;
var mockAppDataCallback;
var mockReceiverManager;

@@ -34,2 +42,3 @@ var mockReceiverApi;

var mockGenericMessageBus;
/** @type {!jasmine.Spy} */
var mockCanDisplayType;

@@ -40,3 +49,5 @@

/** @type {boolean} */
var isChrome;
/** @type {boolean} */
var isChromecast;

@@ -60,11 +71,5 @@

CastReceiver = shaka.cast.CastReceiver;
CastUtils = shaka.cast.CastUtils;
// Don't do any more work here if the tests will not end up running.
if (!isChromecast && !isChrome) return;
originalCast = window['cast'];
originalUserAgent = navigator.userAgent;
// In uncompiled mode, there is a UA check for Chromecast in order to make

@@ -121,3 +126,4 @@ // manual testing easier. For these automated tests, we want to act as if

checkChromeOrChromecast();
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
expect(mockReceiverManager.start).toHaveBeenCalled();

@@ -128,3 +134,4 @@ });

checkChromeOrChromecast();
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
expect(Object.keys(mockVideo.on).length).toBeGreaterThan(0);

@@ -143,3 +150,4 @@ expect(Object.keys(mockPlayer.listeners).length).toBeGreaterThan(0);

});
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
expect(mockCanDisplayType).toHaveBeenCalled();

@@ -159,3 +167,4 @@ expect(mockPlayer.setMaxHardwareResolution).

});
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
expect(mockCanDisplayType).toHaveBeenCalled();

@@ -168,3 +177,4 @@ expect(mockPlayer.setMaxHardwareResolution).

checkChromeOrChromecast();
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
expect(mockPlayer.getConfiguration).not.toHaveBeenCalled();

@@ -177,3 +187,4 @@ expect(mockShakaMessageBus.messages.length).toBe(0);

beforeEach(function() {
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});

@@ -197,3 +208,4 @@

beforeEach(function() {
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});

@@ -204,3 +216,3 @@

var listener = jasmine.createSpy('listener');
receiver.addEventListener('caststatuschanged', listener);
receiver.addEventListener('caststatuschanged', Util.spyFunc(listener));

@@ -224,3 +236,3 @@ shaka.test.Util.delay(0.2).then(function() {

var listener = jasmine.createSpy('listener');
receiver.addEventListener('caststatuschanged', listener);
receiver.addEventListener('caststatuschanged', Util.spyFunc(listener));

@@ -269,3 +281,4 @@ var fakeLoadingEvent = {type: 'loading'};

beforeEach(function() {
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});

@@ -298,8 +311,11 @@

describe('"init" message', function() {
var fakeInitState;
/** @const */
var fakeConfig = {key: 'value'};
/** @const */
var fakeAppData = {myFakeAppData: 1234};
var fakeInitState;
beforeEach(function() {
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));

@@ -466,3 +482,4 @@ fakeInitState = {

beforeEach(function() {
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});

@@ -486,3 +503,4 @@

beforeEach(function() {
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});

@@ -542,3 +560,4 @@

beforeEach(function() {
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});

@@ -569,8 +588,12 @@

describe('"asyncCall" message', function() {
var p;
/** @const */
var fakeSenderId = 'senderId';
/** @const */
var fakeCallId = '5';
/** @type {!shaka.util.PublicPromise} */
var p;
beforeEach(function() {
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));

@@ -652,3 +675,4 @@ fakeConnectedSenders(1);

beforeEach(function() {
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
fakeConnectedSenders(1);

@@ -778,3 +802,4 @@ });

beforeEach(function() {
receiver = new CastReceiver(mockVideo, mockPlayer, mockAppDataCallback);
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});

@@ -781,0 +806,0 @@

@@ -21,2 +21,3 @@ /**

var CastUtils;
var Util = shaka.test.Util;

@@ -65,4 +66,5 @@ var originalChrome;

sender = new CastSender(fakeAppId, onStatusChanged, onRemoteEvent,
onResumeLocal, onInitStateRequired);
sender = new CastSender(
fakeAppId, Util.spyFunc(onStatusChanged), Util.spyFunc(onRemoteEvent),
Util.spyFunc(onResumeLocal), Util.spyFunc(onInitStateRequired));
});

@@ -69,0 +71,0 @@

@@ -19,10 +19,7 @@ /**

describe('CastUtils', function() {
var CastUtils;
var FakeEvent;
/** @const */
var CastUtils = shaka.cast.CastUtils;
/** @const */
var FakeEvent = shaka.util.FakeEvent;
beforeAll(function() {
CastUtils = shaka.cast.CastUtils;
FakeEvent = shaka.util.FakeEvent;
});
it('includes every Player member', function() {

@@ -94,3 +91,4 @@ var ignoredMembers = [

// new Event() is not usable on IE11:
var event = document.createEvent('CustomEvent');
var event =
/** @type {!CustomEvent} */ (document.createEvent('CustomEvent'));
event.initCustomEvent('myEventType', false, false, null);

@@ -183,9 +181,12 @@

describe('TimeRanges', function() {
/** @type {!HTMLVideoElement} */
var video;
/** @type {!shaka.util.EventManager} */
var eventManager;
/** @type {!shaka.media.MediaSourceEngine} */
var mediaSourceEngine;
beforeAll(function() {
video = /** @type {HTMLMediaElement} */(
document.createElement('video'));
video =
/** @type {!HTMLVideoElement} */ (document.createElement('video'));
document.body.appendChild(video);

@@ -198,3 +199,6 @@ });

var mediaSource = new MediaSource();
var mimeType = 'video/mp4; codecs="avc1.42c01e"';
var fakeVideoStream = {
mimeType: 'video/mp4',
codecs: 'avc1.42c01e'
};
var initSegmentUrl = '/base/test/test/assets/sintel-video-init.mp4';

@@ -222,3 +226,3 @@ var videoSegmentUrl = '/base/test/test/assets/sintel-video-segment.mp4';

var initObject = {};
initObject[ContentType.VIDEO] = mimeType;
initObject[ContentType.VIDEO] = fakeVideoStream;
mediaSourceEngine.init(initObject);

@@ -225,0 +229,0 @@ shaka.test.Util.fetch(initSegmentUrl).then(function(data) {

@@ -20,3 +20,4 @@ /**

describe('DashParser ContentProtection', function() {
var Dash;
/** @const */
var Dash = shaka.test.Dash;

@@ -46,3 +47,4 @@ /**

customScheme: callback,
ignoreDrmInfo: ignoreDrmInfo
ignoreDrmInfo: ignoreDrmInfo,
xlinkFailGracefully: false
},

@@ -53,3 +55,4 @@ hls: { defaultTimeOffset: 0 }

networkingEngine: netEngine,
filterPeriod: function() {},
filterNewPeriod: function() {},
filterAllPeriods: function() {},
onTimelineRegionAdded: fail, // Should not have any EventStream elements.

@@ -162,6 +165,2 @@ onEvent: fail,

beforeAll(function() {
Dash = shaka.test.Dash;
});
it('handles clear content', function(done) {

@@ -168,0 +167,0 @@ var source = buildManifestText([], [], []);

@@ -19,16 +19,25 @@ /**

describe('DashParser Live', function() {
var Dash;
/** @const */
var Util = shaka.test.Util;
/** @const */
var ManifestParser = shaka.test.ManifestParser;
/** @const */
var realTimeout = window.setTimeout;
/** @const */
var oldNow = Date.now;
/** @const */
var updateTime = 5;
/** @const */
var originalUri = 'http://example.com/';
/** @type {!shaka.test.FakeNetworkingEngine} */
var fakeNetEngine;
var oldNow;
/** @type {!shaka.dash.DashParser} */
var parser;
var realTimeout;
var updateTime = 5;
/** @type {shakaExtern.ManifestParser.PlayerInterface} */
var playerInterface;
var Util;
beforeAll(function() {
Dash = shaka.test.Dash;
Util = shaka.test.Util;
realTimeout = window.setTimeout;
oldNow = Date.now;
jasmine.clock().install();

@@ -48,3 +57,4 @@ // This polyfill is required for fakeEventLoop.

customScheme: function(node) { return null; },
ignoreDrmInfo: false
ignoreDrmInfo: false,
xlinkFailGracefully: false
},

@@ -55,3 +65,4 @@ hls: { defaultTimeOffset: 0 }

networkingEngine: fakeNetEngine,
filterPeriod: function() {},
filterNewPeriod: function() {},
filterAllPeriods: function() {},
onTimelineRegionAdded: fail, // Should not have any EventStream elements.

@@ -150,7 +161,13 @@ onEvent: fail,

.then(function(manifest) {
Dash.verifySegmentIndex(manifest, firstReferences, 0);
var stream = manifest.periods[0].variants[0].video;
ManifestParser.verifySegmentIndex(stream, firstReferences);
expect(manifest.periods.length).toBe(1);
fakeNetEngine.setResponseMapAsText({'dummy://foo': secondManifest});
delayForUpdatePeriod();
Dash.verifySegmentIndex(manifest, secondReferences, 0);
ManifestParser.verifySegmentIndex(stream, secondReferences);
// In https://github.com/google/shaka-player/issues/963, we
// duplicated periods during the first update. This check covers
// this case.
expect(manifest.periods.length).toBe(1);
}).catch(fail).then(done);

@@ -198,3 +215,3 @@ shaka.polyfill.Promise.flush();

expect(stream.findSegmentPosition(0)).toBe(1);
Dash.verifySegmentIndex(manifest, basicRefs, 0);
ManifestParser.verifySegmentIndex(stream, basicRefs);

@@ -207,3 +224,3 @@ // 15 seconds for @timeShiftBufferDepth and the first segment

expect(stream.findSegmentPosition(0)).toBe(2);
Dash.verifySegmentIndex(manifest, basicRefs.slice(1), 0);
ManifestParser.verifySegmentIndex(stream, basicRefs.slice(1));
}).catch(fail).then(done);

@@ -252,4 +269,6 @@ shaka.polyfill.Promise.flush();

.then(function(manifest) {
Dash.verifySegmentIndex(manifest, basicRefs, 0);
Dash.verifySegmentIndex(manifest, basicRefs, 1);
var stream1 = manifest.periods[0].variants[0].video;
var stream2 = manifest.periods[1].variants[0].video;
ManifestParser.verifySegmentIndex(stream1, basicRefs);
ManifestParser.verifySegmentIndex(stream2, basicRefs);

@@ -261,4 +280,4 @@ // 15 seconds for @timeShiftBufferDepth and the first segment

// The first reference should have been evicted.
Dash.verifySegmentIndex(manifest, basicRefs.slice(1), 0);
Dash.verifySegmentIndex(manifest, basicRefs, 1);
ManifestParser.verifySegmentIndex(stream1, basicRefs.slice(1));
ManifestParser.verifySegmentIndex(stream2, basicRefs);

@@ -268,4 +287,4 @@ // Same as above, but 1 period length later

delayForUpdatePeriod();
Dash.verifySegmentIndex(manifest, [], 0);
Dash.verifySegmentIndex(manifest, basicRefs.slice(1), 1);
ManifestParser.verifySegmentIndex(stream1, []);
ManifestParser.verifySegmentIndex(stream2, basicRefs.slice(1));
}).catch(fail).then(done);

@@ -367,5 +386,8 @@ shaka.polyfill.Promise.flush();

var filterPeriod = jasmine.createSpy('filterPeriod');
playerInterface.filterPeriod = filterPeriod;
var filterNewPeriod = jasmine.createSpy('filterNewPeriod');
playerInterface.filterNewPeriod = Util.spyFunc(filterNewPeriod);
var filterAllPeriods = jasmine.createSpy('filterAllPeriods');
playerInterface.filterAllPeriods = Util.spyFunc(filterAllPeriods);
fakeNetEngine.setResponseMapAsText({'dummy://foo': firstManifest});

@@ -375,3 +397,5 @@ parser.start('dummy://foo', playerInterface)

expect(manifest.periods.length).toBe(1);
expect(filterPeriod.calls.count()).toBe(1);
// Should call filterAllPeriods for parsing the first manifest
expect(filterNewPeriod.calls.count()).toBe(0);
expect(filterAllPeriods.calls.count()).toBe(1);

@@ -383,3 +407,5 @@ fakeNetEngine.setResponseMapAsText({'dummy://foo': secondManifest});

expect(manifest.periods.length).toBe(2);
expect(filterPeriod.calls.count()).toBe(2);
// Should call filterNewPeriod for parsing the new manifest
expect(filterAllPeriods.calls.count()).toBe(1);
expect(filterNewPeriod.calls.count()).toBe(1);
}).catch(fail).then(done);

@@ -411,3 +437,2 @@ shaka.polyfill.Promise.flush();

var manifestData = shaka.util.StringUtils.toUTF8(manifestText);
var originalUri = 'http://example.com/';
var redirectedUri = 'http://redirected.com/';

@@ -440,3 +465,4 @@

var manifest = makeSimpleLiveManifestText(lines, updateTime);
playerInterface.onError = jasmine.createSpy('onError');
var onError = jasmine.createSpy('onError');
playerInterface.onError = Util.spyFunc(onError);

@@ -456,3 +482,3 @@ fakeNetEngine.setResponseMapAsText({'dummy://foo': manifest});

delayForUpdatePeriod();
expect(playerInterface.onError.calls.count()).toBe(1);
expect(onError.calls.count()).toBe(1);
}).catch(fail).then(done);

@@ -729,14 +755,11 @@ shaka.polyfill.Promise.flush();

describe('stop', function() {
var manifestUri;
var dateUri;
var manifestRequestType;
var dateRequestType;
/** @const */
var manifestRequestType = shaka.net.NetworkingEngine.RequestType.MANIFEST;
/** @const */
var dateRequestType = shaka.net.NetworkingEngine.RequestType.MANIFEST;
/** @const */
var manifestUri = 'dummy://foo';
/** @const */
var dateUri = 'http://foo.bar/date';
beforeAll(function() {
manifestUri = 'dummy://foo';
dateUri = 'http://foo.bar/date';
manifestRequestType = shaka.net.NetworkingEngine.RequestType.MANIFEST;
dateRequestType = shaka.net.NetworkingEngine.RequestType.MANIFEST;
});
beforeEach(function() {

@@ -824,3 +847,5 @@ var manifest = [

it('interrupts UTCTiming requests', function(done) {
/** @type {!shaka.util.PublicPromise} */
var delay = fakeNetEngine.delayNextRequest();
Util.delay(0.2, realTimeout).then(function() {

@@ -868,5 +893,5 @@ // This is the initial manifest request.

var basicRefs = [
shaka.test.Dash.makeReference('s1.mp4', 1, 0, 10),
shaka.test.Dash.makeReference('s2.mp4', 2, 10, 15),
shaka.test.Dash.makeReference('s3.mp4', 3, 15, 30)
shaka.test.ManifestParser.makeReference('s1.mp4', 1, 0, 10, originalUri),
shaka.test.ManifestParser.makeReference('s2.mp4', 2, 10, 15, originalUri),
shaka.test.ManifestParser.makeReference('s3.mp4', 3, 15, 30, originalUri)
];

@@ -884,6 +909,6 @@ var updateLines = [

var updateRefs = [
shaka.test.Dash.makeReference('s1.mp4', 1, 0, 10),
shaka.test.Dash.makeReference('s2.mp4', 2, 10, 15),
shaka.test.Dash.makeReference('s3.mp4', 3, 15, 30),
shaka.test.Dash.makeReference('s4.mp4', 4, 30, 40)
shaka.test.ManifestParser.makeReference('s1.mp4', 1, 0, 10, originalUri),
shaka.test.ManifestParser.makeReference('s2.mp4', 2, 10, 15, originalUri),
shaka.test.ManifestParser.makeReference('s3.mp4', 3, 15, 30, originalUri),
shaka.test.ManifestParser.makeReference('s4.mp4', 4, 30, 40, originalUri)
];

@@ -917,5 +942,5 @@ var partialUpdateLines = [

var basicRefs = [
shaka.test.Dash.makeReference('s1.mp4', 1, 0, 10),
shaka.test.Dash.makeReference('s2.mp4', 2, 10, 15),
shaka.test.Dash.makeReference('s3.mp4', 3, 15, 30)
shaka.test.ManifestParser.makeReference('s1.mp4', 1, 0, 10, originalUri),
shaka.test.ManifestParser.makeReference('s2.mp4', 2, 10, 15, originalUri),
shaka.test.ManifestParser.makeReference('s3.mp4', 3, 15, 30, originalUri)
];

@@ -937,6 +962,6 @@ var updateLines = [

var updateRefs = [
shaka.test.Dash.makeReference('s1.mp4', 1, 0, 10),
shaka.test.Dash.makeReference('s2.mp4', 2, 10, 15),
shaka.test.Dash.makeReference('s3.mp4', 3, 15, 30),
shaka.test.Dash.makeReference('s4.mp4', 4, 30, 40)
shaka.test.ManifestParser.makeReference('s1.mp4', 1, 0, 10, originalUri),
shaka.test.ManifestParser.makeReference('s2.mp4', 2, 10, 15, originalUri),
shaka.test.ManifestParser.makeReference('s3.mp4', 3, 15, 30, originalUri),
shaka.test.ManifestParser.makeReference('s4.mp4', 4, 30, 40, originalUri)
];

@@ -967,5 +992,5 @@ var partialUpdateLines = [

var basicRefs = [
shaka.test.Dash.makeReference('s1.mp4', 1, 0, 10),
shaka.test.Dash.makeReference('s2.mp4', 2, 10, 20),
shaka.test.Dash.makeReference('s3.mp4', 3, 20, 30)
shaka.test.ManifestParser.makeReference('s1.mp4', 1, 0, 10, originalUri),
shaka.test.ManifestParser.makeReference('s2.mp4', 2, 10, 20, originalUri),
shaka.test.ManifestParser.makeReference('s3.mp4', 3, 20, 30, originalUri)
];

@@ -981,6 +1006,6 @@ var updateLines = [

var updateRefs = [
shaka.test.Dash.makeReference('s1.mp4', 1, 0, 10),
shaka.test.Dash.makeReference('s2.mp4', 2, 10, 20),
shaka.test.Dash.makeReference('s3.mp4', 3, 20, 30),
shaka.test.Dash.makeReference('s4.mp4', 4, 30, 40)
shaka.test.ManifestParser.makeReference('s1.mp4', 1, 0, 10, originalUri),
shaka.test.ManifestParser.makeReference('s2.mp4', 2, 10, 20, originalUri),
shaka.test.ManifestParser.makeReference('s3.mp4', 3, 20, 30, originalUri),
shaka.test.ManifestParser.makeReference('s4.mp4', 4, 30, 40, originalUri)
];

@@ -999,28 +1024,28 @@ var partialUpdateLines = [

describe('EventStream', function() {
/** @const */
var originalManifest = [
'<MPD type="dynamic" minimumUpdatePeriod="PT' + updateTime + 'S"',
' availabilityStartTime="1970-01-01T00:00:00Z">',
' <Period id="1" duration="PT60S" start="PT10S">',
' <EventStream schemeIdUri="http://example.com" value="foobar"',
' timescale="100">',
' <Event duration="5000" />',
' <Event id="abc" presentationTime="300" duration="1000" />',
' </EventStream>',
' <AdaptationSet mimeType="video/mp4">',
' <Representation bandwidth="1">',
' <SegmentBase indexRange="100-200" />',
' </Representation>',
' </AdaptationSet>',
' </Period>',
'</MPD>'
].join('\n');
/** @type {!jasmine.Spy} */
var onTimelineRegionAddedSpy;
var originalManifest;
beforeAll(function() {
originalManifest = [
'<MPD type="dynamic" minimumUpdatePeriod="PT' + updateTime + 'S"',
' availabilityStartTime="1970-01-01T00:00:00Z">',
' <Period id="1" duration="PT60S" start="PT10S">',
' <EventStream schemeIdUri="http://example.com" value="foobar"',
' timescale="100">',
' <Event duration="5000" />',
' <Event id="abc" presentationTime="300" duration="1000" />',
' </EventStream>',
' <AdaptationSet mimeType="video/mp4">',
' <Representation bandwidth="1">',
' <SegmentBase indexRange="100-200" />',
' </Representation>',
' </AdaptationSet>',
' </Period>',
'</MPD>'
].join('\n');
});
beforeEach(function() {
onTimelineRegionAddedSpy = jasmine.createSpy('onTimelineRegionAdded');
playerInterface.onTimelineRegionAdded = onTimelineRegionAddedSpy;
playerInterface.onTimelineRegionAdded =
shaka.test.Util.spyFunc(onTimelineRegionAddedSpy);
});

@@ -1027,0 +1052,0 @@

@@ -20,6 +20,12 @@ /**

describe('DashParser Manifest', function() {
var Dash;
/** @const */
var Dash = shaka.test.Dash;
/** @type {!shaka.test.FakeNetworkingEngine} */
var fakeNetEngine;
/** @type {!shaka.dash.DashParser} */
var parser;
/** @type {!jasmine.Spy} */
var onEventSpy;
/** @type {shakaExtern.ManifestParser.PlayerInterface} */
var playerInterface;

@@ -33,5 +39,6 @@

networkingEngine: fakeNetEngine,
filterPeriod: function() {},
filterNewPeriod: function() {},
filterAllPeriods: function() {},
onTimelineRegionAdded: fail, // Should not have any EventStream elements.
onEvent: onEventSpy,
onEvent: shaka.test.Util.spyFunc(onEventSpy),
onError: fail

@@ -41,6 +48,2 @@ };

beforeAll(function() {
Dash = shaka.test.Dash;
});
/**

@@ -379,6 +382,6 @@ * Makes a series of tests for the given manifest type.

describe('supports UTCTiming', function() {
var originalNow;
/** @const */
var originalNow = Date.now;
beforeAll(function() {
originalNow = Date.now;
Date.now = function() { return 10 * 1000; };

@@ -637,3 +640,4 @@ });

shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.DASH_INVALID_XML);
shaka.util.Error.Code.DASH_INVALID_XML,
'dummy://foo');
Dash.testFails(done, source, error);

@@ -657,6 +661,28 @@ });

shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.DASH_INVALID_XML);
shaka.util.Error.Code.DASH_INVALID_XML,
'dummy://foo');
Dash.testFails(done, source, error);
});
it('xlink problems when xlinkFailGracefully is false', function(done) {
var source = [
'<MPD minBufferTime="PT75S" xmlns="urn:mpeg:dash:schema:mpd:2011" ' +
'xmlns:xlink="http://www.w3.org/1999/xlink">',
' <Period id="1" duration="PT30S">',
' <AdaptationSet mimeType="video/mp4">',
' <Representation bandwidth="1" xlink:href="https://xlink1" ' +
'xlink:actuate="onInvalid">', // Incorrect actuate
' <SegmentBase indexRange="100-200" />',
' </Representation>',
' </AdaptationSet>',
' </Period>',
'</MPD>'
].join('\n');
var error = new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.DASH_UNSUPPORTED_XLINK_ACTUATE);
Dash.testFails(done, source, error);
});
it('failed network requests', function(done) {

@@ -680,3 +706,4 @@ var expectedError = new shaka.util.Error(

shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.DASH_INVALID_XML);
shaka.util.Error.Code.DASH_INVALID_XML,
'dummy://foo');
Dash.testFails(done, source, error);

@@ -982,2 +1009,112 @@ });

describe('AudioChannelConfiguration', function() {
/**
* @param {?number} expectedNumChannels The expected number of channels
* @param {!Object.<string, string>} schemeMap A map where the map key is
* the AudioChannelConfiguration's schemeIdUri attribute, and the map
* value is the value attribute.
* @return {!Promise}
*/
function testAudioChannelConfiguration(expectedNumChannels, schemeMap) {
var header = [
'<MPD minBufferTime="PT75S">',
' <Period id="1" duration="PT30S">',
' <AdaptationSet mimeType="audio/mp4">',
' <Representation id="1" bandwidth="1">'
].join('\n');
var configs = [];
for (var scheme in schemeMap) {
var value = schemeMap[scheme];
configs.push('<AudioChannelConfiguration schemeIdUri="' + scheme +
'" value="' + value + '" />');
}
var footer = [
' <SegmentTemplate media="1-$Number$.mp4" duration="1" />',
' </Representation>',
' </AdaptationSet>',
' </Period>',
'</MPD>'
].join('\n');
var source = header + configs.join('\n') + footer;
// Create a fresh parser, to avoid issues when we chain multiple tests
// together.
parser = shaka.test.Dash.makeDashParser();
fakeNetEngine.setResponseMapAsText({'dummy://foo': source});
return parser.start('dummy://foo', playerInterface)
.then(function(manifest) {
expect(manifest.periods.length).toBe(1);
expect(manifest.periods[0].variants.length).toBe(1);
var variant = manifest.periods[0].variants[0];
expect(variant.audio.channelsCount).toEqual(expectedNumChannels);
}).catch(fail);
}
it('parses outputChannelPositionList scheme', function(done) {
Promise.resolve().then(function() {
// Parses the space-separated list and finds 8 channels.
return testAudioChannelConfiguration(8,
{ 'urn:mpeg:dash:outputChannelPositionList:2012':
'2 0 1 4 5 3 17 1' });
}).then(function() {
// Does not get confused about extra spaces.
return testAudioChannelConfiguration(7,
{ 'urn:mpeg:dash:outputChannelPositionList:2012':
' 5 2 1 12 8 9 1 '});
}).then(done);
});
it('parses 23003:3 scheme', function(done) {
return Promise.resolve().then(function() {
// Parses a simple channel count.
return testAudioChannelConfiguration(2,
{ 'urn:mpeg:dash:23003:3:audio_channel_configuration:2011': '2' });
}).then(function() {
// This scheme seems to use the same format.
return testAudioChannelConfiguration(6,
{ 'urn:dts:dash:audio_channel_configuration:2012': '6' });
}).then(function() {
// Results in null if the value is not an integer.
return testAudioChannelConfiguration(null,
{ 'urn:mpeg:dash:23003:3:audio_channel_configuration:2011':
'foo' });
}).then(done);
});
it('parses dolby scheme', function(done) {
return Promise.resolve().then(function() {
// Parses a hex value in which each 1-bit is a channel.
return testAudioChannelConfiguration(6,
{ 'tag:dolby.com,2014:dash:audio_channel_configuration:2011':
'F801' });
}).then(function() {
// This scheme seems to use the same format.
return testAudioChannelConfiguration(8,
{ 'urn:dolby:dash:audio_channel_configuration:2011': '7037' });
}).then(function() {
// Results in null if the value is not a valid hex number.
return testAudioChannelConfiguration(null,
{ 'urn:dolby:dash:audio_channel_configuration:2011': 'x' });
}).then(done);
});
it('ignores unrecognized schemes', function(done) {
return Promise.resolve().then(function() {
return testAudioChannelConfiguration(null,
{ 'foo': 'bar' });
}).then(function() {
return testAudioChannelConfiguration(2,
{
'foo': 'bar',
'urn:mpeg:dash:23003:3:audio_channel_configuration:2011': '2'
});
}).then(done);
});
});
/**

@@ -993,4 +1130,4 @@ * @param {string} manifestText

expect(fakeNetEngine.registerResponseFilter).toHaveBeenCalled();
var filter =
fakeNetEngine.registerResponseFilter.calls.mostRecent().args[0];
var filter = /** @type {!Function} */ (
fakeNetEngine.registerResponseFilter.calls.mostRecent().args[0]);
var type = shaka.net.NetworkingEngine.RequestType.SEGMENT;

@@ -997,0 +1134,0 @@ var response = {data: emsgUpdate.buffer};

@@ -19,11 +19,12 @@ /**

describe('DashParser SegmentBase', function() {
var Dash;
/** @const */
var Dash = shaka.test.Dash;
/** @type {!shaka.test.FakeNetworkingEngine} */
var fakeNetEngine;
/** @type {!shaka.dash.DashParser} */
var parser;
/** @type {shakaExtern.ManifestParser.PlayerInterface} */
var playerInterface;
beforeAll(function() {
Dash = shaka.test.Dash;
});
beforeEach(function() {

@@ -35,3 +36,4 @@ fakeNetEngine = new shaka.test.FakeNetworkingEngine();

networkingEngine: fakeNetEngine,
filterPeriod: function() {},
filterNewPeriod: function() {},
filterAllPeriods: function() {},
onTimelineRegionAdded: fail, // Should not have any EventStream elements.

@@ -38,0 +40,0 @@ onEvent: fail,

@@ -19,8 +19,9 @@ /**

describe('DashParser SegmentList', function() {
var Dash;
/** @const */
var Dash = shaka.test.Dash;
/** @const */
var ManifestParser = shaka.test.ManifestParser;
/** @const */
var baseUri = 'http://example.com/';
beforeAll(function() {
Dash = shaka.test.Dash;
});
shaka.test.Dash.makeTimelineTests('SegmentList', '', [

@@ -49,4 +50,4 @@ '<SegmentURL media="s1.mp4" />',

var references = [
Dash.makeReference('s1.mp4', 1, 50, 60),
Dash.makeReference('s2.mp4', 2, 60, 65)
ManifestParser.makeReference('s1.mp4', 1, 50, 60, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 60, 65, baseUri)
];

@@ -62,3 +63,4 @@ Dash.testSegmentIndex(done, source, references);

], 30 /* duration */);
var references = [Dash.makeReference('s1.mp4', 1, 0, 30)];
var references = [ManifestParser.makeReference('s1.mp4', 1,
0, 30, baseUri)];
Dash.testSegmentIndex(done, source, references);

@@ -78,6 +80,6 @@ });

var references = [
Dash.makeReference('s1.mp4', 1, 0, 10),
Dash.makeReference('s2.mp4', 2, 10, 20),
Dash.makeReference('s3.mp4', 3, 20, 30),
Dash.makeReference('s4.mp4', 4, 30, 40)
ManifestParser.makeReference('s1.mp4', 1, 0, 10, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 10, 20, baseUri),
ManifestParser.makeReference('s3.mp4', 3, 20, 30, baseUri),
ManifestParser.makeReference('s4.mp4', 4, 30, 40, baseUri)
];

@@ -97,6 +99,6 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s1.mp4', 5, 40, 50),
Dash.makeReference('s2.mp4', 6, 50, 60),
Dash.makeReference('s3.mp4', 7, 60, 70),
Dash.makeReference('s4.mp4', 8, 70, 80)
ManifestParser.makeReference('s1.mp4', 5, 40, 50, baseUri),
ManifestParser.makeReference('s2.mp4', 6, 50, 60, baseUri),
ManifestParser.makeReference('s3.mp4', 7, 60, 70, baseUri),
ManifestParser.makeReference('s4.mp4', 8, 70, 80, baseUri)
];

@@ -114,4 +116,4 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s1.mp4', 1, 0, 10),
Dash.makeReference('s2.mp4', 2, 10, 20)
ManifestParser.makeReference('s1.mp4', 1, 0, 10, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 10, 20, baseUri)
];

@@ -131,6 +133,6 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s1.mp4', 1, 0, 2),
Dash.makeReference('s2.mp4', 2, 2, 4),
Dash.makeReference('s3.mp4', 3, 4, 6),
Dash.makeReference('s4.mp4', 4, 6, 8)
ManifestParser.makeReference('s1.mp4', 1, 0, 2, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 2, 4, baseUri),
ManifestParser.makeReference('s3.mp4', 3, 4, 6, baseUri),
ManifestParser.makeReference('s4.mp4', 4, 6, 8, baseUri)
];

@@ -208,6 +210,6 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s1.mp4', 1, 0, 50),
Dash.makeReference('s2.mp4', 2, 50, 100),
Dash.makeReference('s3.mp4', 3, 100, 150),
Dash.makeReference('s4.mp4', 4, 150, 200)
ManifestParser.makeReference('s1.mp4', 1, 0, 50, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 50, 100, baseUri),
ManifestParser.makeReference('s3.mp4', 3, 100, 150, baseUri),
ManifestParser.makeReference('s4.mp4', 4, 150, 200, baseUri)
];

@@ -242,5 +244,5 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s1.mp4', 1, 50, 60),
Dash.makeReference('s2.mp4', 2, 60, 65),
Dash.makeReference('s3.mp4', 3, 65, 73)
ManifestParser.makeReference('s1.mp4', 1, 50, 60, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 60, 65, baseUri),
ManifestParser.makeReference('s3.mp4', 3, 65, 73, baseUri)
];

@@ -275,5 +277,5 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s1.mp4', 1, 50, 60),
Dash.makeReference('s2.mp4', 2, 60, 65),
Dash.makeReference('s3.mp4', 3, 65, 73)
ManifestParser.makeReference('s1.mp4', 1, 50, 60, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 60, 65, baseUri),
ManifestParser.makeReference('s3.mp4', 3, 65, 73, baseUri)
];

@@ -312,6 +314,6 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s1.mp4', 1, 40, 50),
Dash.makeReference('s2.mp4', 2, 50, 55),
Dash.makeReference('s3.mp4', 3, 55, 63),
Dash.makeReference('s4.mp4', 4, 63, 70)
ManifestParser.makeReference('s1.mp4', 1, 40, 50, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 50, 55, baseUri),
ManifestParser.makeReference('s3.mp4', 3, 55, 63, baseUri),
ManifestParser.makeReference('s4.mp4', 4, 63, 70, baseUri)
];

@@ -318,0 +320,0 @@

@@ -19,11 +19,16 @@ /**

describe('DashParser SegmentTemplate', function() {
var Dash;
/** @const */
var Dash = shaka.test.Dash;
/** @const */
var ManifestParser = shaka.test.ManifestParser;
/** @const */
var baseUri = 'http://example.com/';
/** @type {!shaka.test.FakeNetworkingEngine} */
var fakeNetEngine;
/** @type {!shaka.dash.DashParser} */
var parser;
/** @type {shakaExtern.ManifestParser.PlayerInterface} */
var playerInterface;
beforeAll(function() {
Dash = shaka.test.Dash;
});
beforeEach(function() {

@@ -35,3 +40,4 @@ fakeNetEngine = new shaka.test.FakeNetworkingEngine();

networkingEngine: fakeNetEngine,
filterPeriod: function() {},
filterNewPeriod: function() {},
filterAllPeriods: function() {},
onTimelineRegionAdded: fail, // Should not have any EventStream elements.

@@ -53,8 +59,8 @@ onEvent: fail,

var references = [
Dash.makeReference('s1.mp4', 0, 0, 10),
Dash.makeReference('s2.mp4', 1, 10, 20),
Dash.makeReference('s3.mp4', 2, 20, 30),
Dash.makeReference('s4.mp4', 3, 30, 40),
Dash.makeReference('s5.mp4', 4, 40, 50),
Dash.makeReference('s6.mp4', 5, 50, 60)
ManifestParser.makeReference('s1.mp4', 0, 0, 10, baseUri),
ManifestParser.makeReference('s2.mp4', 1, 10, 20, baseUri),
ManifestParser.makeReference('s3.mp4', 2, 20, 30, baseUri),
ManifestParser.makeReference('s4.mp4', 3, 30, 40, baseUri),
ManifestParser.makeReference('s5.mp4', 4, 40, 50, baseUri),
ManifestParser.makeReference('s6.mp4', 5, 50, 60, baseUri)
];

@@ -70,5 +76,5 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s10.mp4', 0, 0, 10),
Dash.makeReference('s11.mp4', 1, 10, 20),
Dash.makeReference('s12.mp4', 2, 20, 30)
ManifestParser.makeReference('s10.mp4', 0, 0, 10, baseUri),
ManifestParser.makeReference('s11.mp4', 1, 10, 20, baseUri),
ManifestParser.makeReference('s12.mp4', 2, 20, 30, baseUri)
];

@@ -250,5 +256,5 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('0-0-500.mp4', 0, 0, 15),
Dash.makeReference('1-15-500.mp4', 1, 15, 30),
Dash.makeReference('2-30-500.mp4', 2, 30, 45)
ManifestParser.makeReference('0-0-500.mp4', 0, 0, 15, baseUri),
ManifestParser.makeReference('1-15-500.mp4', 1, 15, 30, baseUri),
ManifestParser.makeReference('2-30-500.mp4', 2, 30, 45, baseUri)
];

@@ -264,5 +270,5 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('0-0-500.mp4', 0, 0, 10),
Dash.makeReference('1-10-500.mp4', 1, 10, 20),
Dash.makeReference('2-20-500.mp4', 2, 20, 30)
ManifestParser.makeReference('0-0-500.mp4', 0, 0, 10, baseUri),
ManifestParser.makeReference('1-10-500.mp4', 1, 10, 20, baseUri),
ManifestParser.makeReference('2-20-500.mp4', 2, 20, 30, baseUri)
];

@@ -278,5 +284,5 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('1-0-500.mp4', 0, 0, 10),
Dash.makeReference('2-10-500.mp4', 1, 10, 20),
Dash.makeReference('3-20-500.mp4', 2, 20, 30)
ManifestParser.makeReference('1-0-500.mp4', 0, 0, 10, baseUri),
ManifestParser.makeReference('2-10-500.mp4', 1, 10, 20, baseUri),
ManifestParser.makeReference('3-20-500.mp4', 2, 20, 30, baseUri)
];

@@ -292,5 +298,5 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('10-0-500.mp4', 0, 0, 10),
Dash.makeReference('11-10-500.mp4', 1, 10, 20),
Dash.makeReference('12-20-500.mp4', 2, 20, 30)
ManifestParser.makeReference('10-0-500.mp4', 0, 0, 10, baseUri),
ManifestParser.makeReference('11-10-500.mp4', 1, 10, 20, baseUri),
ManifestParser.makeReference('12-20-500.mp4', 2, 20, 30, baseUri)
];

@@ -306,5 +312,5 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('1-0-500.mp4', 0, 0, 1),
Dash.makeReference('2-9000-500.mp4', 1, 1, 2),
Dash.makeReference('3-18000-500.mp4', 2, 2, 3)
ManifestParser.makeReference('1-0-500.mp4', 0, 0, 1, baseUri),
ManifestParser.makeReference('2-9000-500.mp4', 1, 1, 2, baseUri),
ManifestParser.makeReference('3-18000-500.mp4', 2, 2, 3, baseUri)
];

@@ -340,18 +346,21 @@ Dash.testSegmentIndex(done, source, references);

expect(variants[0].video.getSegmentReference(0)).toEqual(
Dash.makeReference('1-0-100.mp4', 0, 0, 10));
ManifestParser.makeReference('1-0-100.mp4', 0, 0, 10, baseUri));
expect(variants[0].video.findSegmentPosition(12)).toBe(1);
expect(variants[0].video.getSegmentReference(1)).toEqual(
Dash.makeReference('2-10-100.mp4', 1, 10, 20));
ManifestParser.makeReference('2-10-100.mp4', 1, 10,
20, baseUri));
expect(variants[1].video.findSegmentPosition(0)).toBe(0);
expect(variants[1].video.getSegmentReference(0)).toEqual(
Dash.makeReference('1-0-200.mp4', 0, 0, 10));
ManifestParser.makeReference('1-0-200.mp4', 0, 0, 10, baseUri));
expect(variants[1].video.findSegmentPosition(12)).toBe(1);
expect(variants[1].video.getSegmentReference(1)).toEqual(
Dash.makeReference('2-10-200.mp4', 1, 10, 20));
ManifestParser.makeReference('2-10-200.mp4', 1, 10,
20, baseUri));
expect(variants[2].video.findSegmentPosition(0)).toBe(0);
expect(variants[2].video.getSegmentReference(0)).toEqual(
Dash.makeReference('1-0-300.mp4', 0, 0, 10));
ManifestParser.makeReference('1-0-300.mp4', 0, 0, 10, baseUri));
expect(variants[2].video.findSegmentPosition(12)).toBe(1);
expect(variants[2].video.getSegmentReference(1)).toEqual(
Dash.makeReference('2-10-300.mp4', 1, 10, 20));
ManifestParser.makeReference('2-10-300.mp4', 1, 10,
20, baseUri));
}).catch(fail).then(done);

@@ -358,0 +367,0 @@ });

@@ -19,8 +19,5 @@ /**

describe('MpdUtils', function() {
var MpdUtils;
/** @const */
var MpdUtils = shaka.dash.MpdUtils;
beforeAll(function() {
MpdUtils = shaka.dash.MpdUtils;
});
describe('fillUriTemplate', function() {

@@ -31,3 +28,3 @@ it('handles a single RepresentationID identifier', function() {

'/example/$RepresentationID$.mp4',
100, null, null, null).toString()).toBe('/example/100.mp4');
'100', null, null, null).toString()).toBe('/example/100.mp4');

@@ -38,3 +35,3 @@ // RepresentationID cannot use a width specifier.

'/example/$RepresentationID%01d$.mp4',
100, null, null, null).toString()).toBe('/example/100.mp4');
'100', null, null, null).toString()).toBe('/example/100.mp4');

@@ -118,3 +115,3 @@ expect(

'/example/$RepresentationID$_$Number$_$Bandwidth$_$Time$.mp4',
1, 2, 3, 4).toString()).toBe('/example/1_2_3_4.mp4');
'1', 2, 3, 4).toString()).toBe('/example/1_2_3_4.mp4');

@@ -125,3 +122,3 @@ // No spaces.

'/example/$RepresentationID$$Number$$Bandwidth$$Time$.mp4',
1, 2, 3, 4).toString()).toBe('/example/1234.mp4');
'1', 2, 3, 4).toString()).toBe('/example/1234.mp4');

@@ -132,3 +129,3 @@ // Different order.

'/example/$Bandwidth$_$Time$_$RepresentationID$_$Number$.mp4',
1, 2, 3, 4).toString()).toBe('/example/3_4_1_2.mp4');
'1', 2, 3, 4).toString()).toBe('/example/3_4_1_2.mp4');

@@ -139,3 +136,3 @@ // Single width.

'$RepresentationID$_$Number%01d$_$Bandwidth%01d$_$Time%01d$',
1, 2, 3, 400).toString()).toBe('1_2_3_400');
'1', 2, 3, 400).toString()).toBe('1_2_3_400');

@@ -146,3 +143,3 @@ // Different widths.

'$RepresentationID$_$Number%02d$_$Bandwidth%02d$_$Time%02d$',
1, 2, 3, 4).toString()).toBe('1_02_03_04');
'1', 2, 3, 4).toString()).toBe('1_02_03_04');

@@ -153,3 +150,3 @@ // Double $$.

'$$/$RepresentationID$$$$Number$$$$Bandwidth$$$$Time$$$.$$',
1, 2, 3, 4).toString()).toBe('$/1$2$3$4$.$');
'1', 2, 3, 4).toString()).toBe('$/1$2$3$4$.$');
});

@@ -161,3 +158,3 @@

'/example/$Garbage$.mp4',
1, 2, 3, 4).toString()).toBe('/example/$Garbage$.mp4');
'1', 2, 3, 4).toString()).toBe('/example/$Garbage$.mp4');

@@ -167,3 +164,3 @@ expect(

'/example/$Time.mp4',
1, 2, 3, 4).toString()).toBe('/example/$Time.mp4');
'1', 2, 3, 4).toString()).toBe('/example/$Time.mp4');
});

@@ -395,2 +392,3 @@ });

});
/**

@@ -447,2 +445,278 @@ * Creates a new TimePoint.

});
describe('processXlinks', function() {
/** @const */
var Error = shaka.util.Error;
/** @type {!shaka.test.FakeNetworkingEngine} */
var fakeNetEngine;
/** @type {shakaExtern.RetryParameters} */
var retry;
/** @type {!DOMParser} */
var parser;
/** @type {boolean} */
var failGracefully;
beforeEach(function() {
failGracefully = false;
retry = shaka.net.NetworkingEngine.defaultRetryParameters();
fakeNetEngine = new shaka.test.FakeNetworkingEngine();
parser = new DOMParser();
});
it('will replace elements and children', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="https://xlink1" xlink:actuate="onLoad" />');
var xlinkXMLString = '<ToReplace variable="1"><Contents /></ToReplace>';
var desiredXMLString = inBaseContainer(
'<ToReplace variable="1"><Contents /></ToReplace>');
fakeNetEngine.setResponseMapAsText({'https://xlink1': xlinkXMLString});
testSucceeds(baseXMLString, desiredXMLString, 1, done);
});
it('preserves non-xlink attributes', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace otherVariable="q" xlink:href="https://xlink1" ' +
'xlink:actuate="onLoad" />');
var xlinkXMLString = '<ToReplace variable="1"><Contents /></ToReplace>';
var desiredXMLString = inBaseContainer(
'<ToReplace otherVariable="q" variable="1"><Contents /></ToReplace>');
fakeNetEngine.setResponseMapAsText({'https://xlink1': xlinkXMLString});
testSucceeds(baseXMLString, desiredXMLString, 1, done);
});
it('preserves text', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="https://xlink1" xlink:actuate="onLoad" />');
var xlinkXMLString =
'<ToReplace variable="1">TEXT CONTAINED WITHIN</ToReplace>';
var desiredXMLString = inBaseContainer(
'<ToReplace variable="1">TEXT CONTAINED WITHIN</ToReplace>');
fakeNetEngine.setResponseMapAsText({'https://xlink1': xlinkXMLString});
testSucceeds(baseXMLString, desiredXMLString, 1, done);
});
it('supports multiple replacements', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="https://xlink1" xlink:actuate="onLoad" />',
'<ToReplace xlink:href="https://xlink2" xlink:actuate="onLoad" />');
var xlinkXMLString1 = makeRecursiveXMLString(1, 'https://xlink3');
var xlinkXMLString2 = '<ToReplace variable="2"><Contents /></ToReplace>';
var xlinkXMLString3 = '<ToReplace otherVariable="blue" />';
var desiredXMLString = inBaseContainer(
'<ToReplace xmlns="urn:mpeg:dash:schema:mpd:2011" ' +
'xmlns:xlink="http://www.w3.org/1999/xlink" variable="1">' +
'<ToReplace otherVariable="blue" /></ToReplace>',
'<ToReplace variable="2"><Contents /></ToReplace>');
fakeNetEngine.setResponseMapAsText({
'https://xlink1': xlinkXMLString1,
'https://xlink2': xlinkXMLString2,
'https://xlink3': xlinkXMLString3});
testSucceeds(baseXMLString, desiredXMLString, 3, done);
});
it('fails if loaded file is invalid xml', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="https://xlink1" xlink:actuate="onLoad" />');
// Note this does not have a close angle bracket.
var xlinkXMLString = '<ToReplace></ToReplace';
var expectedError = new shaka.util.Error(
Error.Severity.CRITICAL, Error.Category.MANIFEST,
Error.Code.DASH_INVALID_XML, 'https://xlink1');
fakeNetEngine.setResponseMapAsText({'https://xlink1': xlinkXMLString});
testFails(baseXMLString, expectedError, 1, done);
});
it('fails if it recurses too many times', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="https://xlink1" xlink:actuate="onLoad" />');
// Create a large but finite number of links, so this won't
// infinitely recurse if there isn't a depth limit.
var responseMap = {};
for (var i = 1; i < 20; i++) {
responseMap['https://xlink' + i] =
makeRecursiveXMLString(0, 'https://xlink' + (i + 1) + '');
}
var expectedError = new shaka.util.Error(
Error.Severity.CRITICAL, Error.Category.MANIFEST,
Error.Code.DASH_XLINK_DEPTH_LIMIT);
fakeNetEngine.setResponseMapAsText(responseMap);
testFails(baseXMLString, expectedError, 5, done);
});
it('preserves url parameters', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="https://xlink1?parameter" ' +
'xlink:actuate="onLoad" />');
var xlinkXMLString = '<ToReplace variable="1"><Contents /></ToReplace>';
var desiredXMLString = inBaseContainer(
'<ToReplace variable="1"><Contents /></ToReplace>');
fakeNetEngine.setResponseMapAsText(
{'https://xlink1?parameter': xlinkXMLString});
testSucceeds(baseXMLString, desiredXMLString, 1, done);
});
it('replaces existing contents', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="https://xlink1" xlink:actuate="onLoad">' +
'<Unwanted /></ToReplace>');
var xlinkXMLString = '<ToReplace variable="1"><Contents /></ToReplace>';
var desiredXMLString = inBaseContainer(
'<ToReplace variable="1"><Contents /></ToReplace>');
fakeNetEngine.setResponseMapAsText({'https://xlink1': xlinkXMLString});
testSucceeds(baseXMLString, desiredXMLString, 1, done);
});
it('handles relative links', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="xlink1" xlink:actuate="onLoad" />',
'<ToReplace xlink:href="xlink2" xlink:actuate="onLoad" />');
var xlinkXMLString1 = // This is loaded relative to base.
makeRecursiveXMLString(1, 'xlink3');
var xlinkXMLString2 = // This is loaded relative to base.
'<ToReplace variable="2"><Contents /></ToReplace>';
var xlinkXMLString3 = // This is loaded relative to string1.
'<ToReplace variable="3" />';
var responseMap = {};
responseMap['https://base/xlink1'] = xlinkXMLString1;
responseMap['https://base/xlink2'] = xlinkXMLString2;
responseMap['https://base/xlink3'] = xlinkXMLString3;
var desiredXMLString = inBaseContainer(
'<ToReplace xmlns="urn:mpeg:dash:schema:mpd:2011" ' +
'xmlns:xlink="http://www.w3.org/1999/xlink" variable="1">' +
'<ToReplace variable="3" /></ToReplace>',
'<ToReplace variable="2"><Contents /></ToReplace>');
fakeNetEngine.setResponseMapAsText(responseMap);
testSucceeds(baseXMLString, desiredXMLString, 3, done);
});
it('fails for actuate=onRequest', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="https://xlink1" ' +
'xlink:actuate="onRequest" />');
var xlinkXMLString = '<ToReplace variable="1"><Contents /></ToReplace>';
var expectedError = new shaka.util.Error(
Error.Severity.CRITICAL, Error.Category.MANIFEST,
Error.Code.DASH_UNSUPPORTED_XLINK_ACTUATE);
fakeNetEngine.setResponseMapAsText({'https://xlink1': xlinkXMLString});
testFails(baseXMLString, expectedError, 0, done);
});
it('fails for no actuate', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="https://xlink1" />');
var xlinkXMLString = '<ToReplace variable="1"><Contents /></ToReplace>';
var expectedError = new shaka.util.Error(
Error.Severity.CRITICAL, Error.Category.MANIFEST,
Error.Code.DASH_UNSUPPORTED_XLINK_ACTUATE);
fakeNetEngine.setResponseMapAsText({'https://xlink1': xlinkXMLString});
testFails(baseXMLString, expectedError, 0, done);
});
it('removes elements with resolve-to-zero', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="urn:mpeg:dash:resolve-to-zero:2013" />');
var desiredXMLString = inBaseContainer();
testSucceeds(baseXMLString, desiredXMLString, 0, done);
});
it('needs the top-level to match the link\'s tagName', function(done) {
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="https://xlink1" xlink:actuate="onLoad" />');
var xlinkXMLString = '<BadTagName</BadTagName>';
fakeNetEngine.setResponseMapAsText({'https://xlink1': xlinkXMLString});
testFails(baseXMLString, null, 1, done);
});
it('doesn\'t error when set to fail gracefully', function(done) {
failGracefully = true;
var baseXMLString = inBaseContainer(
'<ToReplace xlink:href="https://xlink1" xlink:actuate="onLoad">' +
'<DefaultContents />' +
'</ToReplace>');
var xlinkXMLString = '<BadTagName</BadTagName>';
var desiredXMLString = inBaseContainer(
'<ToReplace><DefaultContents /></ToReplace>');
fakeNetEngine.setResponseMapAsText({'https://xlink1': xlinkXMLString});
testSucceeds(baseXMLString, desiredXMLString, 1, done);
});
function testSucceeds(
baseXMLString, desiredXMLString, desiredNetCalls, done) {
var desiredXML = parser.parseFromString(desiredXMLString, 'text/xml')
.documentElement;
testRequest(baseXMLString).then(function(finalXML) {
expect(fakeNetEngine.request).toHaveBeenCalledTimes(desiredNetCalls);
expect(finalXML).toEqualElement(desiredXML);
return Promise.resolve();
}).catch(fail).then(done);
}
function testFails(baseXMLString, desiredError, desiredNetCalls, done) {
testRequest(baseXMLString).then(fail).catch(function(error) {
expect(fakeNetEngine.request).toHaveBeenCalledTimes(desiredNetCalls);
if (desiredError)
shaka.test.Util.expectToEqualError(error, desiredError);
return Promise.resolve();
}).then(done);
}
/**
* Creates an XML string with an xlink link to another URL,
* for use in testing recursive chains of xlink links.
* @param {number} variable
* @param {string} link
* @return {string}
* @private
*/
function makeRecursiveXMLString(variable, link) {
var format =
'<ToReplace xmlns="urn:mpeg:dash:schema:mpd:2011" ' +
'xmlns:xlink="http://www.w3.org/1999/xlink" variable="%(var)s">' +
'<ToReplace xlink:href="%(link)s" xlink:actuate="onLoad" />' +
'</ToReplace>';
return sprintf(format, {var : variable, link : link});
}
/**
* @param {string=} opt_toReplaceOne
* @param {string=} opt_toReplaceTwo
* @return {string}
* @private
*/
function inBaseContainer(opt_toReplaceOne, opt_toReplaceTwo) {
var format =
'<Container xmlns="urn:mpeg:dash:schema:mpd:2011" ' +
'xmlns:xlink="http://www.w3.org/1999/xlink">' +
'<Thing>' +
'%(toReplaceOne)s' +
'</Thing>' +
'%(toReplaceTwo)s' +
'</Container>';
return sprintf(format, {
toReplaceOne: opt_toReplaceOne || '',
toReplaceTwo: opt_toReplaceTwo || ''});
}
function testRequest(baseXMLString) {
var xml = parser.parseFromString(baseXMLString, 'text/xml')
.documentElement;
return MpdUtils.processXlinks(xml, retry, failGracefully, 'https://base',
fakeNetEngine);
}
});
});

@@ -20,5 +20,12 @@ /**

describe('HlsParser', function() {
/** @const */
var Util = shaka.test.Util;
/** @type {!shaka.test.FakeNetworkingEngine} */
var fakeNetEngine;
/** @type {!shaka.hls.HlsParser} */
var parser;
/** @type {shakaExtern.ManifestParser.PlayerInterface} */
var playerInterface;
/** @type {shakaExtern.ManifestConfiguration} */
var config;

@@ -28,4 +35,3 @@ beforeEach(function() {

var retry = shaka.net.NetworkingEngine.defaultRetryParameters();
parser = new shaka.hls.HlsParser();
parser.configure({
config = {
retryParameters: retry,

@@ -35,3 +41,4 @@ dash: {

clockSyncUri: '',
ignoreDrmInfo: false
ignoreDrmInfo: false,
xlinkFailGracefully: false
},

@@ -41,8 +48,13 @@ hls: {

}
});
};
playerInterface = {
filterNewPeriod: function() {},
filterAllPeriods: function() {},
networkingEngine: fakeNetEngine,
filterPeriod: function() {},
onError: fail
onError: fail,
onEvent: fail,
onTimelineRegionAdded: fail
};
parser = new shaka.hls.HlsParser();
parser.configure(config);
});

@@ -144,3 +156,3 @@

'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'URI="test://audio"\n'
'CHANNELS="2",URI="test://audio"\n'
].join('');

@@ -176,2 +188,3 @@

.mime('audio/mp4', 'mp4a')
.channelsCount(2)
.build();

@@ -455,2 +468,35 @@

it('should call filterAllPeriods for parsing', function(done) {
var master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
'RESOLUTION=960x540,FRAME-RATE=60\n',
'test://video'
].join('');
var media = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MAP:URI="test://main.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'#EXT-X-BYTERANGE:121090@616\n',
'test://main.mp4'
].join('');
fakeNetEngine.setResponseMapAsText({
'test://master': master,
'test://audio': media,
'test://video': media,
'test://text': media
});
var filterAllPeriods = jasmine.createSpy('filterAllPeriods');
playerInterface.filterAllPeriods = Util.spyFunc(filterAllPeriods);
parser.start('test://master', playerInterface)
.then(function(manifest) {
expect(filterAllPeriods.calls.count()).toBe(1);
}).catch(fail).then(done);
});
it('gets mime type from header request', function(done) {

@@ -590,5 +636,3 @@ var master = [

parser.configure({
hls: {defaultTimeOffset: 10}
});
config.hls.defaultTimeOffset = 10;

@@ -688,4 +732,6 @@ testHlsParser(master, media, manifest, done);

var audioPosition = audio.findSegmentPosition(0);
expect(videoPosition).not.toBe(null);
expect(audioPosition).not.toBe(null);
goog.asserts.assert(videoPosition != null,
'Cannot find first video segment');
goog.asserts.assert(audioPosition != null,
'Cannot find first audio segment');

@@ -897,29 +943,2 @@ var videoReference = video.getSegmentReference(videoPosition);

it('if encountered live content (PLAYLIST-TYPE:EVENT)', function(done) {
var master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="aaa,bbb",',
'RESOLUTION=960x540,FRAME-RATE=60,VIDEO="vid"\n',
'test://audio\n',
'#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="vid",',
'URI="test://video"'
].join('');
var media = [
'#EXTM3U\n',
'#EXT-X-MAP:URI="test://main.mp4",BYTERANGE="616@0"\n',
'#EXT-X-PLAYLIST-TYPE:EVENT\n',
'#EXTINF:5,\n',
'#EXT-X-BYTERANGE:121090@616\n',
'test://main.mp4'
].join('');
var error = new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
Code.HLS_LIVE_CONTENT_NOT_SUPPORTED);
verifyError(master, media, error, done);
});
it('if encountered live content (no PLAYLIST-TYPE tag)', function(done) {

@@ -926,0 +945,0 @@ var master = [

@@ -20,2 +20,3 @@ /**

describe('ManifestTextParser', function() {
/** @type {!shaka.hls.ManifestTextParser} */
var parser;

@@ -288,3 +289,48 @@

describe('parseSegments', function() {
var manifestText = '#EXTM3U\n' +
'#EXT-X-TARGETDURATION:6\n' +
'#EXTINF:5\n' +
'uri\n' +
'#EXTINF:4\n' +
'uri2\n';
it('parses segments', function() {
verifyPlaylist(
{
type: shaka.hls.PlaylistType.MEDIA,
tags: [
new shaka.hls.Tag(/* id */ 0, 'EXT-X-TARGETDURATION', [], '6')
],
segments: [
new shaka.hls.Segment('uri',
[new shaka.hls.Tag(2, 'EXTINF', [], '5')]),
new shaka.hls.Segment('uri2',
[new shaka.hls.Tag(3, 'EXTINF', [], '4')])
]
},
manifestText);
});
it('identifies playlist tags', function() {
verifyPlaylist(
{
type: shaka.hls.PlaylistType.MEDIA,
tags: [
new shaka.hls.Tag(/* id */ 0, 'EXT-X-TARGETDURATION', [], '6'),
new shaka.hls.Tag(/* id */ 4, 'EXT-X-ENDLIST', [])
],
segments: [
new shaka.hls.Segment('uri',
[new shaka.hls.Tag(2, 'EXTINF', [], '5')]),
new shaka.hls.Segment('uri2',
[new shaka.hls.Tag(3, 'EXTINF', [], '4')])
]
},
// Append a playlist tag to the manifest text so it appears after
// segment-related tags.
manifestText + '#EXT-X-ENDLIST');
});
});
/**

@@ -291,0 +337,0 @@ * @param {Object} expectedPlaylist

@@ -19,30 +19,51 @@ /**

describe('DrmEngine', function() {
/** @const */
var ContentType = shaka.util.ManifestParserUtils.ContentType;
// These come from Axinom and use the Axinom license server.
// TODO: Do not rely on third-party services long-term.
/** @const */
var videoInitSegmentUri = '/base/test/test/assets/multidrm-video-init.mp4';
/** @const */
var videoSegmentUri = '/base/test/test/assets/multidrm-video-segment.mp4';
/** @const */
var audioInitSegmentUri = '/base/test/test/assets/multidrm-audio-init.mp4';
/** @const */
var audioSegmentUri = '/base/test/test/assets/multidrm-audio-segment.mp4';
/** @type {!Object.<string, ?shakaExtern.DrmSupportType>} */
var support = {};
/** @type {!HTMLVideoElement} */
var video;
/** @type {!MediaSource} */
var mediaSource;
/** @type {shakaExtern.Manifest} */
var manifest;
/** @type {!jasmine.Spy} */
var onErrorSpy;
/** @type {!jasmine.Spy} */
var onKeyStatusSpy;
/** @type {!jasmine.Spy} */
var onExpirationSpy;
/** @type {!shaka.media.DrmEngine} */
var drmEngine;
/** @type {!shaka.media.MediaSourceEngine} */
var mediaSourceEngine;
/** @type {!shaka.net.NetworkingEngine} */
var networkingEngine;
/** @type {!shaka.util.EventManager} */
var eventManager;
/** @type {!ArrayBuffer} */
var videoInitSegment;
/** @type {!ArrayBuffer} */
var audioInitSegment;
/** @type {!ArrayBuffer} */
var videoSegment;
/** @type {!ArrayBuffer} */
var audioSegment;
// These come from Axinom and use the Axinom license server.
// TODO: Do not rely on third-party services long-term.
var videoInitSegmentUri = '/base/test/test/assets/multidrm-video-init.mp4';
var videoSegmentUri = '/base/test/test/assets/multidrm-video-segment.mp4';
var audioInitSegmentUri = '/base/test/test/assets/multidrm-audio-init.mp4';
var audioSegmentUri = '/base/test/test/assets/multidrm-audio-segment.mp4';
var ContentType = shaka.util.ManifestParserUtils.ContentType;
beforeAll(function(done) {

@@ -53,3 +74,3 @@ var supportTest = shaka.media.DrmEngine.probeSupport()

video = /** @type {HTMLVideoElement} */ (document.createElement('video'));
video = /** @type {!HTMLVideoElement} */ (document.createElement('video'));
video.width = 600;

@@ -96,3 +117,5 @@ video.height = 400;

drmEngine = new shaka.media.DrmEngine(
networkingEngine, onErrorSpy, onKeyStatusSpy, onExpirationSpy);
networkingEngine, shaka.test.Util.spyFunc(onErrorSpy),
shaka.test.Util.spyFunc(onKeyStatusSpy),
shaka.test.Util.spyFunc(onExpirationSpy));
var config = {

@@ -121,2 +144,5 @@ retryParameters: shaka.net.NetworkingEngine.defaultRetryParameters(),

var videoStream = manifest.periods[0].variants[0].video;
var audioStream = manifest.periods[0].variants[0].audio;
eventManager = new shaka.util.EventManager();

@@ -132,4 +158,4 @@

var expectedObject = {};
expectedObject[ContentType.AUDIO] = 'audio/mp4; codecs="mp4a.40.2"';
expectedObject[ContentType.VIDEO] = 'video/mp4; codecs="avc1.640015"';
expectedObject[ContentType.AUDIO] = audioStream;
expectedObject[ContentType.VIDEO] = videoStream;
mediaSourceEngine.init(expectedObject);

@@ -172,3 +198,3 @@ done();

});
networkingEngine.request = requestSpy;
networkingEngine.request = shaka.test.Util.spyFunc(requestSpy);

@@ -231,3 +257,3 @@ var encryptedEventSeen = new shaka.util.PublicPromise();

if (call) {
var map = call.args[0];
var map = /** @type {!Object} */ (call.args[0]);
expect(Object.keys(map).length).not.toBe(0);

@@ -234,0 +260,0 @@ for (var k in map) {

@@ -19,13 +19,20 @@ /**

describe('MediaSourceEngine', function() {
/** @const */
var ContentType = shaka.util.ManifestParserUtils.ContentType;
/** @const */
var presentationDuration = 840;
/** @type {!HTMLVideoElement} */
var video;
/** @type {!MediaSource} */
var mediaSource;
/** @type {!shaka.media.MediaSourceEngine} */
var mediaSourceEngine;
var generators;
var metadata;
var presentationDuration = 840;
var ContentType = shaka.util.ManifestParserUtils.ContentType;
// TODO: add text streams to MSE integration tests
beforeAll(function() {
video = /** @type {HTMLVideoElement} */ (document.createElement('video'));
video = /** @type {!HTMLVideoElement} */ (document.createElement('video'));
video.width = 600;

@@ -89,7 +96,7 @@ video.height = 400;

function getFullMimeType(streamMetadata) {
var fullMimeType = streamMetadata.mimeType;
if (streamMetadata.codecs)
fullMimeType += '; codecs="' + streamMetadata.codecs + '"';
return fullMimeType;
function getFakeStream(streamMetadata) {
return {
mimeType: streamMetadata.mimeType,
codecs: streamMetadata.codecs
};
}

@@ -101,3 +108,3 @@

var initObject = {};
initObject[ContentType.VIDEO] = getFullMimeType(metadata.video);
initObject[ContentType.VIDEO] = getFakeStream(metadata.video);
mediaSourceEngine.init(initObject);

@@ -124,3 +131,3 @@ mediaSourceEngine.setDuration(presentationDuration).then(function() {

var initObject = {};
initObject[ContentType.VIDEO] = getFullMimeType(metadata.video);
initObject[ContentType.VIDEO] = getFakeStream(metadata.video);
mediaSourceEngine.init(initObject);

@@ -155,3 +162,3 @@ mediaSourceEngine.setDuration(presentationDuration).then(function() {

var initObject = {};
initObject[ContentType.VIDEO] = getFullMimeType(metadata.video);
initObject[ContentType.VIDEO] = getFakeStream(metadata.video);
mediaSourceEngine.init(initObject);

@@ -187,3 +194,3 @@ mediaSourceEngine.setDuration(0).then(function() {

var initObject = {};
initObject[ContentType.VIDEO] = getFullMimeType(metadata.video);
initObject[ContentType.VIDEO] = getFakeStream(metadata.video);
mediaSourceEngine.init(initObject);

@@ -218,3 +225,3 @@ mediaSourceEngine.setDuration(presentationDuration).then(function() {

var initObject = {};
initObject[ContentType.VIDEO] = getFullMimeType(metadata.video);
initObject[ContentType.VIDEO] = getFakeStream(metadata.video);
mediaSourceEngine.init(initObject);

@@ -237,3 +244,3 @@ checkOrder(mediaSourceEngine.setDuration(presentationDuration));

var initObject = {};
initObject[ContentType.AUDIO] = getFullMimeType(metadata.audio);
initObject[ContentType.AUDIO] = getFakeStream(metadata.audio);
mediaSourceEngine.init(initObject);

@@ -262,4 +269,4 @@ mediaSourceEngine.setDuration(presentationDuration).then(function() {

var initObject = {};
initObject[ContentType.AUDIO] = getFullMimeType(metadata.audio);
initObject[ContentType.VIDEO] = getFullMimeType(metadata.video);
initObject[ContentType.AUDIO] = getFakeStream(metadata.audio);
initObject[ContentType.VIDEO] = getFakeStream(metadata.video);
mediaSourceEngine.init(initObject);

@@ -322,3 +329,3 @@

var initObject = {};
initObject[ContentType.VIDEO] = getFullMimeType(metadata.video);
initObject[ContentType.VIDEO] = getFakeStream(metadata.video);
mediaSourceEngine.init(initObject);

@@ -325,0 +332,0 @@ mediaSourceEngine.setDuration(presentationDuration).then(function() {

@@ -19,4 +19,18 @@ /**

describe('MediaSourceEngine', function() {
var originalIsTypeSupported;
var originalTextEngine;
var Util = shaka.test.Util;
var ContentType = shaka.util.ManifestParserUtils.ContentType;
var originalIsTypeSupported = window.MediaSource.isTypeSupported;
var originalTextEngine = shaka.text.TextEngine;
// Jasmine Spies don't handle toHaveBeenCalledWith well with objects, so use
// some numbers instead.
var buffer = /** @type {!ArrayBuffer} */ (/** @type {?} */ (1));
var buffer2 = /** @type {!ArrayBuffer} */ (/** @type {?} */ (2));
var buffer3 = /** @type {!ArrayBuffer} */ (/** @type {?} */ (3));
var fakeVideoStream = { mimeType: 'video/foo' };
var fakeAudioStream = { mimeType: 'audio/foo' };
var fakeTextStream = { mimeType: 'text/foo' };
var audioSourceBuffer;

@@ -27,10 +41,7 @@ var videoSourceBuffer;

var mockTextEngine;
/** @type {!shaka.media.MediaSourceEngine} */
var mediaSourceEngine;
var Util;
var ContentType;
beforeAll(function() {
Util = shaka.test.Util;
ContentType = shaka.util.ManifestParserUtils.ContentType;
originalIsTypeSupported = window.MediaSource.isTypeSupported;
// Since this is not an integration test, we don't want MediaSourceEngine to

@@ -44,4 +55,3 @@ // fail assertions based on browser support for types. Pretend that all

originalTextEngine = shaka.media.TextEngine;
shaka.media.TextEngine = createMockTextEngineCtor();
shaka.text.TextEngine = createMockTextEngineCtor();
});

@@ -51,3 +61,3 @@

window.MediaSource.isTypeSupported = originalIsTypeSupported;
shaka.media.TextEngine = originalTextEngine;
shaka.text.TextEngine = originalTextEngine;
});

@@ -89,8 +99,8 @@

var initObject = {};
initObject[ContentType.AUDIO] = 'audio/foo';
initObject[ContentType.VIDEO] = 'video/foo';
initObject[ContentType.AUDIO] = fakeAudioStream;
initObject[ContentType.VIDEO] = fakeVideoStream;
mediaSourceEngine.init(initObject);
expect(mockMediaSource.addSourceBuffer).toHaveBeenCalledWith('audio/foo');
expect(mockMediaSource.addSourceBuffer).toHaveBeenCalledWith('video/foo');
expect(shaka.media.TextEngine).not.toHaveBeenCalled();
expect(shaka.text.TextEngine).not.toHaveBeenCalled();
});

@@ -102,6 +112,6 @@

var initObject = {};
initObject[ContentType.TEXT] = 'text/foo';
initObject[ContentType.TEXT] = fakeTextStream;
mediaSourceEngine.init(initObject);
expect(mockMediaSource.addSourceBuffer).not.toHaveBeenCalled();
expect(shaka.media.TextEngine).toHaveBeenCalled();
expect(shaka.text.TextEngine).toHaveBeenCalled();
});

@@ -115,4 +125,4 @@ });

var initObject = {};
initObject[ContentType.AUDIO] = 'audio/foo';
initObject[ContentType.TEXT] = 'text/foo';
initObject[ContentType.AUDIO] = fakeAudioStream;
initObject[ContentType.TEXT] = fakeTextStream;
mediaSourceEngine.init(initObject);

@@ -124,6 +134,4 @@ });

expect(mediaSourceEngine.bufferStart(ContentType.AUDIO, 0))
.toBeCloseTo(0);
expect(mediaSourceEngine.bufferEnd(ContentType.AUDIO, 0))
.toBeCloseTo(10);
expect(mediaSourceEngine.bufferStart(ContentType.AUDIO)).toBeCloseTo(0);
expect(mediaSourceEngine.bufferEnd(ContentType.AUDIO)).toBeCloseTo(10);
});

@@ -135,6 +143,4 @@

expect(mediaSourceEngine.bufferStart(ContentType.AUDIO, 0))
.toBeCloseTo(5);
expect(mediaSourceEngine.bufferEnd(ContentType.AUDIO, 0))
.toBeCloseTo(30);
expect(mediaSourceEngine.bufferStart(ContentType.AUDIO)).toBeCloseTo(5);
expect(mediaSourceEngine.bufferEnd(ContentType.AUDIO)).toBeCloseTo(30);
});

@@ -145,4 +151,4 @@

expect(mediaSourceEngine.bufferStart(ContentType.AUDIO, 0)).toBeNull();
expect(mediaSourceEngine.bufferEnd(ContentType.AUDIO, 0)).toBeNull();
expect(mediaSourceEngine.bufferStart(ContentType.AUDIO)).toBeNull();
expect(mediaSourceEngine.bufferEnd(ContentType.AUDIO)).toBeNull();
});

@@ -169,4 +175,4 @@

var initObject = {};
initObject[ContentType.AUDIO] = 'audio/foo';
initObject[ContentType.TEXT] = 'text/foo';
initObject[ContentType.AUDIO] = fakeAudioStream;
initObject[ContentType.TEXT] = fakeTextStream;
mediaSourceEngine.init(initObject);

@@ -179,7 +185,7 @@ });

expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 0))
.toBeCloseTo(10);
.toBeCloseTo(10);
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 5))
.toBeCloseTo(5);
.toBeCloseTo(5);
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 9.9))
.toBeCloseTo(0.1);
.toBeCloseTo(0.1);
});

@@ -191,5 +197,5 @@

expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 10))
.toBeCloseTo(0);
.toBeCloseTo(0);
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 100))
.toBeCloseTo(0);
.toBeCloseTo(0);
});

@@ -203,15 +209,15 @@

expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 1))
.toBeCloseTo(6);
.toBeCloseTo(6);
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 2.5))
.toBeCloseTo(4.5);
.toBeCloseTo(4.5);
// between range 0 and 1
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 5))
.toBeCloseTo(4);
.toBeCloseTo(4);
// in range 1
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 6))
.toBeCloseTo(4);
.toBeCloseTo(4);
expect(mediaSourceEngine.bufferedAheadOf(ContentType.AUDIO, 9.9))
.toBeCloseTo(0.1);
.toBeCloseTo(0.1);
});

@@ -235,5 +241,5 @@

var initObject = {};
initObject[ContentType.AUDIO] = 'audio/foo';
initObject[ContentType.VIDEO] = 'video/foo';
initObject[ContentType.TEXT] = 'text/foo';
initObject[ContentType.AUDIO] = fakeAudioStream;
initObject[ContentType.VIDEO] = fakeVideoStream;
initObject[ContentType.TEXT] = fakeTextStream;
mediaSourceEngine.init(initObject);

@@ -243,6 +249,5 @@ });

it('appends the given data', function(done) {
mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null)
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null)
.then(function() {
expect(audioSourceBuffer.appendBuffer)
.toHaveBeenCalledWith(1);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
done();

@@ -256,3 +261,3 @@ });

mockVideo.error = { code: 5 };
mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null)
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null)
.then(function() {

@@ -267,3 +272,3 @@ fail('not reached');

expect(audioSourceBuffer.appendBuffer)
.toHaveBeenCalledWith(1);
.toHaveBeenCalledWith(buffer);
done();

@@ -279,3 +284,3 @@ });

mockVideo.error = { code: 5 };
mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null)
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null)
.then(function() {

@@ -287,3 +292,3 @@ fail('not reached');

expect(error.data).toEqual([ContentType.AUDIO]);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
done();

@@ -295,3 +300,3 @@ });

mockVideo.error = { code: 5 };
mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null)
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null)
.then(function() {

@@ -304,3 +309,3 @@ fail('not reached');

expect(error.data).toEqual([5]);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
done();

@@ -313,9 +318,9 @@ });

it('queues operations on a single SourceBuffer', function(done) {
var p1 = mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
var p2 = mediaSourceEngine.appendBuffer(ContentType.AUDIO, 2, null, null);
Util.capturePromiseStatus(p1);
Util.capturePromiseStatus(p2);
var p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer, null, null));
var p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer2, null, null));
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(2);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(buffer2);
expect(p1.status).toBe('pending');

@@ -327,3 +332,3 @@ expect(p2.status).toBe('pending');

expect(p2.status).toBe('pending');
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(2);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer2);

@@ -338,12 +343,12 @@ audioSourceBuffer.updateend();

it('queues operations independently for different types', function(done) {
var p1 = mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
var p2 = mediaSourceEngine.appendBuffer(ContentType.AUDIO, 2, null, null);
var p3 = mediaSourceEngine.appendBuffer(ContentType.VIDEO, 3, null, null);
Util.capturePromiseStatus(p1);
Util.capturePromiseStatus(p2);
Util.capturePromiseStatus(p3);
var p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer, null, null));
var p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer2, null, null));
var p3 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.VIDEO, buffer3, null, null));
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(2);
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(3);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(buffer2);
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer3);
expect(p1.status).toBe('pending');

@@ -358,3 +363,3 @@ expect(p2.status).toBe('pending');

expect(p2.status).toBe('pending');
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(2);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer2);

@@ -383,13 +388,13 @@ return p3;

var p1 = mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
var p2 = mediaSourceEngine.appendBuffer(ContentType.AUDIO, 2, null, null);
var p3 = mediaSourceEngine.appendBuffer(ContentType.AUDIO, 3, null, null);
Util.capturePromiseStatus(p1);
Util.capturePromiseStatus(p2);
Util.capturePromiseStatus(p3);
var p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer, null, null));
var p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer2, null, null));
var p3 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer3, null, null));
Util.delay(0.1).then(function() {
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(2);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(3);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer2);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer3);
expect(p1.status).toBe('resolved');

@@ -420,5 +425,5 @@ expect(p2.status).toBe('rejected');

var initObject = {};
initObject[ContentType.AUDIO] = 'audio/foo';
initObject[ContentType.VIDEO] = 'video/foo';
initObject[ContentType.TEXT] = 'text/foo';
initObject[ContentType.AUDIO] = fakeAudioStream;
initObject[ContentType.VIDEO] = fakeVideoStream;
initObject[ContentType.TEXT] = fakeTextStream;
mediaSourceEngine.init(initObject);

@@ -468,6 +473,6 @@ });

it('queues operations on a single SourceBuffer', function(done) {
var p1 = mediaSourceEngine.remove(ContentType.AUDIO, 1, 5);
var p2 = mediaSourceEngine.remove(ContentType.AUDIO, 6, 10);
Util.capturePromiseStatus(p1);
Util.capturePromiseStatus(p2);
var p1 = new shaka.test.StatusPromise(
mediaSourceEngine.remove(ContentType.AUDIO, 1, 5));
var p2 = new shaka.test.StatusPromise(
mediaSourceEngine.remove(ContentType.AUDIO, 6, 10));

@@ -492,8 +497,8 @@ expect(audioSourceBuffer.remove).toHaveBeenCalledWith(1, 5);

it('queues operations independently for different types', function(done) {
var p1 = mediaSourceEngine.remove(ContentType.AUDIO, 1, 5);
var p2 = mediaSourceEngine.remove(ContentType.AUDIO, 6, 10);
var p3 = mediaSourceEngine.remove(ContentType.VIDEO, 3, 8);
Util.capturePromiseStatus(p1);
Util.capturePromiseStatus(p2);
Util.capturePromiseStatus(p3);
var p1 = new shaka.test.StatusPromise(
mediaSourceEngine.remove(ContentType.AUDIO, 1, 5));
var p2 = new shaka.test.StatusPromise(
mediaSourceEngine.remove(ContentType.AUDIO, 6, 10));
var p3 = new shaka.test.StatusPromise(
mediaSourceEngine.remove(ContentType.VIDEO, 3, 8));

@@ -534,8 +539,8 @@ expect(audioSourceBuffer.remove).toHaveBeenCalledWith(1, 5);

var p1 = mediaSourceEngine.remove(ContentType.AUDIO, 1, 2);
var p2 = mediaSourceEngine.remove(ContentType.AUDIO, 2, 3);
var p3 = mediaSourceEngine.remove(ContentType.AUDIO, 3, 4);
Util.capturePromiseStatus(p1);
Util.capturePromiseStatus(p2);
Util.capturePromiseStatus(p3);
var p1 = new shaka.test.StatusPromise(
mediaSourceEngine.remove(ContentType.AUDIO, 1, 2));
var p2 = new shaka.test.StatusPromise(
mediaSourceEngine.remove(ContentType.AUDIO, 2, 3));
var p3 = new shaka.test.StatusPromise(
mediaSourceEngine.remove(ContentType.AUDIO, 3, 4));

@@ -568,5 +573,5 @@ Util.delay(0.1).then(function() {

var initObject = {};
initObject[ContentType.AUDIO] = 'audio/foo';
initObject[ContentType.VIDEO] = 'video/foo';
initObject[ContentType.TEXT] = 'text/foo';
initObject[ContentType.AUDIO] = fakeAudioStream;
initObject[ContentType.VIDEO] = fakeVideoStream;
initObject[ContentType.TEXT] = fakeTextStream;
mediaSourceEngine.init(initObject);

@@ -643,4 +648,4 @@ });

var initObject = {};
initObject[ContentType.AUDIO] = 'audio/foo';
initObject[ContentType.VIDEO] = 'video/foo';
initObject[ContentType.AUDIO] = fakeAudioStream;
initObject[ContentType.VIDEO] = fakeVideoStream;
mediaSourceEngine.init(initObject);

@@ -657,8 +662,7 @@ });

it('waits for all previous operations to complete', function(done) {
var p1 = mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
var p2 = mediaSourceEngine.appendBuffer(ContentType.VIDEO, 1, null, null);
var p3 = mediaSourceEngine.endOfStream();
Util.capturePromiseStatus(p1);
Util.capturePromiseStatus(p2);
Util.capturePromiseStatus(p3);
var p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer, null, null));
var p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.VIDEO, buffer, null, null));
var p3 = new shaka.test.StatusPromise(mediaSourceEngine.endOfStream());

@@ -678,3 +682,3 @@ expect(mockMediaSource.endOfStream).not.toHaveBeenCalled();

return p3;
}).then(function() {}).then(function() {
}).then(function() {
expect(mockMediaSource.endOfStream).toHaveBeenCalled();

@@ -687,5 +691,5 @@ done();

var p1 = mediaSourceEngine.endOfStream();
mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
mediaSourceEngine.appendBuffer(ContentType.VIDEO, 1, null, null);
mediaSourceEngine.appendBuffer(ContentType.VIDEO, 2, null, null);
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null);
mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null, null);
mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer2, null, null);

@@ -702,10 +706,11 @@ // endOfStream hasn't been called yet because blocking multiple queues

// The next operations have already been kicked off.
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
// This one is still in queue.
expect(videoSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(2);
expect(videoSourceBuffer.appendBuffer)
.not.toHaveBeenCalledWith(buffer2);
audioSourceBuffer.updateend();
videoSourceBuffer.updateend();
}).then(function() {
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(2);
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer2);
videoSourceBuffer.updateend();

@@ -720,3 +725,3 @@ }).then(function() {

var p1 = mediaSourceEngine.endOfStream();
mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null);

@@ -748,4 +753,4 @@ expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalled();

var initObject = {};
initObject[ContentType.AUDIO] = 'audio/foo';
initObject[ContentType.VIDEO] = 'video/foo';
initObject[ContentType.AUDIO] = fakeAudioStream;
initObject[ContentType.VIDEO] = fakeVideoStream;
mediaSourceEngine.init(initObject);

@@ -762,8 +767,7 @@ });

it('waits for all previous operations to complete', function(done) {
var p1 = mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
var p2 = mediaSourceEngine.appendBuffer(ContentType.VIDEO, 1, null, null);
var p3 = mediaSourceEngine.setDuration(100);
Util.capturePromiseStatus(p1);
Util.capturePromiseStatus(p2);
Util.capturePromiseStatus(p3);
var p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer, null, null));
var p2 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.VIDEO, buffer, null, null));
var p3 = new shaka.test.StatusPromise(mediaSourceEngine.setDuration(100));

@@ -783,3 +787,3 @@ expect(mockMediaSource.durationSetter_).not.toHaveBeenCalled();

return p3;
}).then(function() {}).then(function() {
}).then(function() {
expect(mockMediaSource.durationSetter_).toHaveBeenCalledWith(100);

@@ -792,5 +796,5 @@ done();

var p1 = mediaSourceEngine.setDuration(100);
mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
mediaSourceEngine.appendBuffer(ContentType.VIDEO, 1, null, null);
mediaSourceEngine.appendBuffer(ContentType.VIDEO, 2, null, null);
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null);
mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null, null);
mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer2, null, null);

@@ -807,10 +811,11 @@ // The setter hasn't been called yet because blocking multiple queues

// The next operations have already been kicked off.
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
// This one is still in queue.
expect(videoSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(2);
expect(videoSourceBuffer.appendBuffer)
.not.toHaveBeenCalledWith(buffer2);
audioSourceBuffer.updateend();
videoSourceBuffer.updateend();
}).then(function() {
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(2);
expect(videoSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer2);
videoSourceBuffer.updateend();

@@ -825,3 +830,3 @@ }).then(function() {

var p1 = mediaSourceEngine.setDuration(100);
mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null);

@@ -837,3 +842,3 @@ expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalled();

}).then(function() {
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
audioSourceBuffer.updateend();

@@ -853,4 +858,4 @@ }).then(function() {

var initObject = {};
initObject[ContentType.AUDIO] = 'audio/foo';
initObject[ContentType.VIDEO] = 'video/foo';
initObject[ContentType.AUDIO] = fakeAudioStream;
initObject[ContentType.VIDEO] = fakeVideoStream;
mediaSourceEngine.init(initObject);

@@ -860,7 +865,6 @@ });

it('waits for all operations to complete', function(done) {
mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
mediaSourceEngine.appendBuffer(ContentType.VIDEO, 1, null, null);
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null);
mediaSourceEngine.appendBuffer(ContentType.VIDEO, buffer, null, null);
var p = mediaSourceEngine.destroy();
Util.capturePromiseStatus(p);
var p = new shaka.test.StatusPromise(mediaSourceEngine.destroy());

@@ -883,6 +887,5 @@ expect(p.status).toBe('pending');

it('resolves even when a pending operation fails', function(done) {
var p1 = mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
var p2 = mediaSourceEngine.destroy();
Util.capturePromiseStatus(p1);
Util.capturePromiseStatus(p2);
var p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer, null, null));
var p2 = new shaka.test.StatusPromise(mediaSourceEngine.destroy());

@@ -899,6 +902,4 @@ audioSourceBuffer.error();

it('waits for blocking operations to complete', function(done) {
var p1 = mediaSourceEngine.endOfStream();
var p2 = mediaSourceEngine.destroy();
Util.capturePromiseStatus(p1);
Util.capturePromiseStatus(p2);
var p1 = new shaka.test.StatusPromise(mediaSourceEngine.endOfStream());
var p2 = new shaka.test.StatusPromise(mediaSourceEngine.destroy());

@@ -917,12 +918,11 @@ expect(p1.status).toBe('pending');

it('cancels operations that have not yet started', function(done) {
mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
var rejected = mediaSourceEngine.appendBuffer(
ContentType.AUDIO, 2, null, null);
Util.capturePromiseStatus(rejected);
mediaSourceEngine.appendBuffer(ContentType.AUDIO, buffer, null, null);
var rejected =
new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer2, null, null));
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(2);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(buffer2);
var p = mediaSourceEngine.destroy();
Util.capturePromiseStatus(p);
var p = new shaka.test.StatusPromise(mediaSourceEngine.destroy());

@@ -933,4 +933,5 @@ expect(p.status).toBe('pending');

expect(rejected.status).toBe('rejected');
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(1);
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(2);
expect(audioSourceBuffer.appendBuffer).toHaveBeenCalledWith(buffer);
expect(audioSourceBuffer.appendBuffer)
.not.toHaveBeenCalledWith(buffer2);
audioSourceBuffer.updateend();

@@ -940,3 +941,4 @@ return Util.delay(0.1);

expect(p.status).toBe('resolved');
expect(audioSourceBuffer.appendBuffer).not.toHaveBeenCalledWith(2);
expect(audioSourceBuffer.appendBuffer)
.not.toHaveBeenCalledWith(buffer2);
done();

@@ -947,8 +949,6 @@ });

it('cancels blocking operations that have not yet started', function(done) {
var p1 = mediaSourceEngine.appendBuffer(ContentType.AUDIO, 1, null, null);
var p2 = mediaSourceEngine.endOfStream();
var p3 = mediaSourceEngine.destroy();
Util.capturePromiseStatus(p1);
Util.capturePromiseStatus(p2);
Util.capturePromiseStatus(p3);
var p1 = new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer, null, null));
var p2 = new shaka.test.StatusPromise(mediaSourceEngine.endOfStream());
var p3 = new shaka.test.StatusPromise(mediaSourceEngine.destroy());

@@ -971,5 +971,5 @@ expect(p1.status).toBe('pending');

var p = mediaSourceEngine.destroy();
var rejected = mediaSourceEngine.appendBuffer(
ContentType.AUDIO, 1, null, null);
Util.capturePromiseStatus(rejected);
var rejected =
new shaka.test.StatusPromise(mediaSourceEngine.appendBuffer(
ContentType.AUDIO, buffer, null, null));

@@ -1006,4 +1006,5 @@ // The promise has already been rejected, but our capture requires 1 tick.

};
Object.defineProperty(mediaSource, 'duration',
{ get: mediaSource.durationGetter_, set: mediaSource.durationSetter_ });
Object.defineProperty(
mediaSource, 'duration',
{get: mediaSource.durationGetter_, set: mediaSource.durationSetter_});
return mediaSource;

@@ -1039,3 +1040,4 @@ }

'initParser', 'destroy', 'appendBuffer', 'remove', 'setTimestampOffset',
'setAppendWindowEnd', 'bufferStart', 'bufferEnd', 'bufferedAheadOf'
'setAppendWindowEnd', 'bufferStart', 'bufferEnd', 'bufferedAheadOf',
'setDisplayer'
]);

@@ -1042,0 +1044,0 @@

@@ -19,12 +19,25 @@ /**

describe('PlayheadObserver', function() {
/** @const */
var Util = shaka.test.Util;
/** @type {!shaka.media.PlayheadObserver} */
var observer;
/** @type {!shaka.test.FakeVideo} */
var video;
/** @type {{readyState: string}} */
var mockMediaSource;
/** @type {?} */
var mediaSource;
/** @type {!shaka.test.FakePresentationTimeline} */
var timeline;
/** @type {shakaExtern.Manifest} */
var manifest;
/** @type {shakaExtern.StreamingConfiguration} */
var config;
/** @type {!jasmine.Spy} */
var onBuffering;
/** @type {!jasmine.Spy} */
var onChangePeriod;
/** @type {!jasmine.Spy} */
var onEvent;

@@ -51,3 +64,2 @@

// shakaExtern.Manifest
manifest = {

@@ -60,3 +72,2 @@ periods: [],

// shakaExtern.StreamingConfiguration
config = {

@@ -66,3 +77,3 @@ rebufferingGoal: 10,

retryParameters: shaka.net.NetworkingEngine.defaultRetryParameters(),
infiniteRetriesForLiveStreams: true,
failureCallback: function() {},
bufferBehind: 15,

@@ -95,4 +106,4 @@ ignoreTextStreamFailures: false,

observer = new shaka.media.PlayheadObserver(
video, mediaSource, manifest, config, onBuffering, onEvent,
onChangePeriod);
video, mediaSource, manifest, config, Util.spyFunc(onBuffering),
Util.spyFunc(onEvent), Util.spyFunc(onChangePeriod));

@@ -108,4 +119,4 @@ observer.seeked();

observer = new shaka.media.PlayheadObserver(
video, mediaSource, manifest, config, onBuffering, onEvent,
onChangePeriod);
video, mediaSource, manifest, config, Util.spyFunc(onBuffering),
Util.spyFunc(onEvent), Util.spyFunc(onChangePeriod));

@@ -122,4 +133,4 @@ video.currentTime = 20;

observer = new shaka.media.PlayheadObserver(
video, mediaSource, manifest, config, onBuffering, onEvent,
onChangePeriod);
video, mediaSource, manifest, config, Util.spyFunc(onBuffering),
Util.spyFunc(onEvent), Util.spyFunc(onChangePeriod));

@@ -137,4 +148,4 @@ video.currentTime = 40;

observer = new shaka.media.PlayheadObserver(
video, mediaSource, manifest, config, onBuffering, onEvent,
onChangePeriod);
video, mediaSource, manifest, config, Util.spyFunc(onBuffering),
Util.spyFunc(onEvent), Util.spyFunc(onChangePeriod));

@@ -157,4 +168,4 @@ video.currentTime = 22;

observer = new shaka.media.PlayheadObserver(
video, mediaSource, manifest, config, onBuffering, onEvent,
onChangePeriod);
video, mediaSource, manifest, config, Util.spyFunc(onBuffering),
Util.spyFunc(onEvent), Util.spyFunc(onChangePeriod));

@@ -176,4 +187,4 @@ jasmine.clock().tick(1000);

observer = new shaka.media.PlayheadObserver(
video, mediaSource, manifest, config, onBuffering, onEvent,
onChangePeriod);
video, mediaSource, manifest, config, Util.spyFunc(onBuffering),
Util.spyFunc(onEvent), Util.spyFunc(onChangePeriod));

@@ -199,4 +210,4 @@ jasmine.clock().tick(1000);

observer = new shaka.media.PlayheadObserver(
video, mediaSource, manifest, config, onBuffering, onEvent,
onChangePeriod);
video, mediaSource, manifest, config, Util.spyFunc(onBuffering),
Util.spyFunc(onEvent), Util.spyFunc(onChangePeriod));

@@ -220,4 +231,4 @@ jasmine.clock().tick(1000);

observer = new shaka.media.PlayheadObserver(
video, mediaSource, manifest, config, onBuffering, onEvent,
onChangePeriod);
video, mediaSource, manifest, config, Util.spyFunc(onBuffering),
Util.spyFunc(onEvent), Util.spyFunc(onChangePeriod));

@@ -266,4 +277,4 @@ jasmine.clock().tick(1000);

observer = new shaka.media.PlayheadObserver(
video, mediaSource, manifest, config, onBuffering, onEvent,
onChangePeriod);
video, mediaSource, manifest, config, Util.spyFunc(onBuffering),
Util.spyFunc(onEvent), Util.spyFunc(onChangePeriod));
});

@@ -483,4 +494,4 @@

observer = new shaka.media.PlayheadObserver(
video, mediaSource, manifest, config, onBuffering, onEvent,
onChangePeriod);
video, mediaSource, manifest, config, Util.spyFunc(onBuffering),
Util.spyFunc(onEvent), Util.spyFunc(onChangePeriod));

@@ -487,0 +498,0 @@ // Ignore the call for the initial Period.

@@ -100,12 +100,22 @@ /**

describe('Playhead', function() {
/** @const */
var Util = shaka.test.Util;
/** @type {!shaka.test.FakeVideo} */
var video;
/** @type {!shaka.test.FakePresentationTimeline} */
var timeline;
/** @type {shakaExtern.Manifest} */
var manifest;
/** @type {!shaka.media.Playhead} */
var playhead;
/** @type {shakaExtern.StreamingConfiguration} */
var config;
// Callback to us from Playhead when a valid 'seeking' event occurs.
/** @type {!jasmine.Spy} */
var onSeek;
// Callback to us from Playhead when an event should be sent to the app.
/** @type {!jasmine.Spy} */
var onEvent;

@@ -129,3 +139,2 @@

// shakaExtern.Manifest
manifest = {

@@ -138,3 +147,2 @@ periods: [],

// shakaExtern.StreamingConfiguration
config = {

@@ -144,3 +152,3 @@ rebufferingGoal: 10,

retryParameters: shaka.net.NetworkingEngine.defaultRetryParameters(),
infiniteRetriesForLiveStreams: true,
failureCallback: function() {},
bufferBehind: 15,

@@ -157,3 +165,2 @@ ignoreTextStreamFailures: false,

playhead.destroy().then(done);
playhead = null;
});

@@ -168,4 +175,4 @@

5 /* startTime */,
onSeek,
onEvent);
Util.spyFunc(onSeek),
Util.spyFunc(onEvent));

@@ -209,4 +216,4 @@ expect(video.addEventListener).toHaveBeenCalledWith(

5 /* startTime */,
onSeek,
onEvent);
Util.spyFunc(onSeek),
Util.spyFunc(onEvent));

@@ -236,4 +243,4 @@ expect(playhead.getTime()).toBe(5);

5 /* startTime */,
onSeek,
onEvent);
Util.spyFunc(onSeek),
Util.spyFunc(onEvent));

@@ -376,4 +383,4 @@ // Calling on['seeking']() is like dispatching a 'seeking' event. So, each

5 /* startTime */,
onSeek,
onEvent);
Util.spyFunc(onSeek),
Util.spyFunc(onEvent));

@@ -419,4 +426,4 @@ video.on['seeking']();

5 /* startTime */,
onSeek,
onEvent);
Util.spyFunc(onSeek),
Util.spyFunc(onEvent));
expect(video.currentTime).toBe(1000);

@@ -453,4 +460,4 @@ video.on['seeking']();

5 /* startTime */,
onSeek,
onEvent);
Util.spyFunc(onSeek),
Util.spyFunc(onEvent));

@@ -487,4 +494,4 @@ video.on['seeking']();

5 /* startTime */,
onSeek,
onEvent);
Util.spyFunc(onSeek),
Util.spyFunc(onEvent));

@@ -642,4 +649,4 @@ video.on['seeking']();

data.start /* startTime */,
onSeek,
onEvent);
Util.spyFunc(onSeek),
Util.spyFunc(onEvent));

@@ -883,4 +890,4 @@ jasmine.clock().tick(1000);

data.start /* startTime */,
onSeek,
onEvent);
Util.spyFunc(onSeek),
Util.spyFunc(onEvent));

@@ -947,4 +954,4 @@ jasmine.clock().tick(1000);

0 /* startTime */,
onSeek,
onEvent);
Util.spyFunc(onSeek),
Util.spyFunc(onEvent));
});

@@ -951,0 +958,0 @@

@@ -19,3 +19,6 @@ /**

describe('PresentationTimeline', function() {
var originalDateNow;
/** @const */
var originalDateNow = Date.now;
/** @type {!Date} */
var baseTime;

@@ -25,3 +28,2 @@

baseTime = new Date(2015, 11, 30);
originalDateNow = Date.now;
Date.now = function() { return baseTime.getTime(); };

@@ -28,0 +30,0 @@ });

@@ -19,13 +19,14 @@ /**

describe('SegmentIndex', /** @suppress {accessControls} */ function() {
/** @const */
var actual1 = makeReference(0, 0, 10, uri(0));
/** @const */
var actual2 = makeReference(1, 10, 20, uri(20));
/** @const */
var actual3 = makeReference(2, 20, 30, uri(20));
describe('find', function() {
it('finds the correct references', function() {
var actual1 = makeReference(0, 0, 10, uri(0));
var actual2 = makeReference(1, 10, 20, uri(10));
var actual3 = makeReference(2, 20, 30, uri(20));
var index, pos1, pos2, pos3;
// One reference.
index = new shaka.media.SegmentIndex([actual1]);
pos1 = index.find(5);
var index = new shaka.media.SegmentIndex([actual1]);
var pos1 = index.find(5);
expect(pos1).toBe(actual1.position);

@@ -36,3 +37,3 @@

pos1 = index.find(5);
pos2 = index.find(15);
var pos2 = index.find(15);
expect(pos1).toBe(actual1.position);

@@ -45,3 +46,3 @@ expect(pos2).toBe(actual2.position);

pos2 = index.find(15);
pos3 = index.find(25);
var pos3 = index.find(25);
expect(pos1).toBe(actual1.position);

@@ -104,16 +105,6 @@ expect(pos2).toBe(actual2.position);

describe('get', function() {
var actual1, actual2, actual3;
beforeEach(function() {
actual1 = makeReference(0, 0, 10, uri(0));
actual2 = makeReference(1, 10, 20, uri(10));
actual3 = makeReference(2, 20, 30, uri(20));
});
it('returns the correct references', function() {
var index, r1, r2, r3;
// One reference.
index = new shaka.media.SegmentIndex([actual1]);
r1 = index.get(0);
var index = new shaka.media.SegmentIndex([actual1]);
var r1 = index.get(0);
expect(r1).toEqual(actual1);

@@ -124,3 +115,3 @@

r1 = index.get(0);
r2 = index.get(1);
var r2 = index.get(1);
expect(r1).toEqual(actual1);

@@ -133,3 +124,3 @@ expect(r2).toEqual(actual2);

r2 = index.get(1);
r3 = index.get(2);
var r3 = index.get(2);
expect(r1).toEqual(actual1);

@@ -175,7 +166,3 @@ expect(r2).toEqual(actual2);

var references2 = [
makeReference(0, 0, 10, uri(0)),
makeReference(1, 10, 20, uri(10)),
makeReference(2, 20, 30, uri(20))
];
var references2 = [actual1, actual2, actual3];

@@ -188,7 +175,3 @@ index1.merge(references2);

it('zero references into three references', function() {
var references1 = [
makeReference(0, 0, 10, uri(0)),
makeReference(1, 10, 20, uri(10)),
makeReference(2, 20, 30, uri(20))
];
var references1 = [actual1, actual2, actual3];
var index1 = new shaka.media.SegmentIndex(references1);

@@ -295,8 +278,6 @@

describe('evict', function() {
var actual1, actual2, actual3, index1;
/** @type {!shaka.media.SegmentIndex} */
var index1;
beforeEach(function() {
actual1 = makeReference(0, 0, 10, uri(0));
actual2 = makeReference(1, 10, 20, uri(10));
actual3 = makeReference(2, 20, 30, uri(20));
index1 = new shaka.media.SegmentIndex([actual1, actual2, actual3]);

@@ -303,0 +284,0 @@ });

@@ -19,36 +19,55 @@ /**

describe('StreamingEngine', function() {
/** @const */
var ContentType = shaka.util.ManifestParserUtils.ContentType;
/** @const */
var Util = shaka.test.Util;
var metadata;
var generators;
/** @type {!shaka.util.EventManager} */
var eventManager;
/** @type {!HTMLVideoElement} */
var video;
var timeline;
/** @type {!shaka.media.Playhead} */
var playhead;
/** @type {shakaExtern.StreamingConfiguration} */
var config;
var onBuffering;
var netEngine;
/** @type {!MediaSource} */
var mediaSource;
/** @type {!shaka.media.MediaSourceEngine} */
var mediaSourceEngine;
/** @type {!shaka.media.StreamingEngine} */
var streamingEngine;
var netEngine;
var audioStream1;
var videoStream1;
var audioStream2;
var videoStream2;
/** @type {shakaExtern.Variant} */
var variant1;
/** @type {shakaExtern.Variant} */
var variant2;
/** @type {shakaExtern.Manifest} */
var manifest;
/** @type {!jasmine.Spy} */
var onBuffering;
/** @type {!jasmine.Spy} */
var onChooseStreams;
/** @type {!jasmine.Spy} */
var onCanSwitch;
/** @type {!jasmine.Spy} */
var onError;
/** @type {!jasmine.Spy} */
var onEvent;
/** @type {!jasmine.Spy} */
var onInitialStreamsSetup;
/** @type {!jasmine.Spy} */
var onStartupComplete;
var streamingEngine;
var ContentType = shaka.util.ManifestParserUtils.ContentType;
beforeAll(function() {
video = /** @type {HTMLVideoElement} */ (document.createElement('video'));
video = /** @type {!HTMLVideoElement} */ (document.createElement('video'));
video.width = 600;

@@ -69,3 +88,3 @@ video.height = 400;

retryParameters: shaka.net.NetworkingEngine.defaultRetryParameters(),
infiniteRetriesForLiveStreams: true,
failureCallback: function() {},
bufferBehind: 15,

@@ -87,3 +106,2 @@ ignoreTextStreamFailures: false,

eventManager = new shaka.util.EventManager();

@@ -272,3 +290,3 @@ setupMediaSource().catch(fail).then(done);

onSeek,
onEvent);
shaka.test.Util.spyFunc(onEvent));
}

@@ -283,3 +301,4 @@

manifest.presentationTimeline = timeline;
manifest.presentationTimeline =
/** @type {!shaka.media.PresentationTimeline} */ (timeline);
manifest.minBufferTime = 2;

@@ -298,6 +317,4 @@

audioStream1 = manifest.periods[0].variants[0].audio;
videoStream1 = manifest.periods[0].variants[0].video;
audioStream2 = manifest.periods[1].variants[0].audio;
videoStream2 = manifest.periods[1].variants[0].video;
variant1 = manifest.periods[0].variants[0];
variant2 = manifest.periods[1].variants[0];
}

@@ -310,10 +327,10 @@

netEngine: /** @type {!shaka.net.NetworkingEngine} */(netEngine),
onChooseStreams: onChooseStreams,
onCanSwitch: onCanSwitch,
onError: onError,
onEvent: onEvent,
onChooseStreams: Util.spyFunc(onChooseStreams),
onCanSwitch: Util.spyFunc(onCanSwitch),
onError: Util.spyFunc(onError),
onEvent: Util.spyFunc(onEvent),
onManifestUpdate: function() {},
onSegmentAppended: playhead.onSegmentAppended.bind(playhead),
onInitialStreamsSetup: onInitialStreamsSetup,
onStartupComplete: onStartupComplete
onInitialStreamsSetup: Util.spyFunc(onInitialStreamsSetup),
onStartupComplete: Util.spyFunc(onStartupComplete)
};

@@ -344,3 +361,6 @@ streamingEngine = new shaka.media.StreamingEngine(

onChooseStreams.and.callFake(defaultOnChooseStreams);
streamingEngine.init();
streamingEngine.init().catch(function(error) {
fail(error);
done();
});
});

@@ -372,3 +392,6 @@

onChooseStreams.and.callFake(defaultOnChooseStreams);
streamingEngine.init();
streamingEngine.init().catch(function(error) {
fail(error);
done();
});
});

@@ -399,3 +422,6 @@

onChooseStreams.and.callFake(defaultOnChooseStreams);
streamingEngine.init();
streamingEngine.init().catch(function(error) {
fail(error);
done();
});
});

@@ -426,3 +452,6 @@

onChooseStreams.and.callFake(defaultOnChooseStreams);
streamingEngine.init();
streamingEngine.init().catch(function(error) {
fail(error);
done();
});
});

@@ -469,3 +498,6 @@ });

onChooseStreams.and.callFake(defaultOnChooseStreams);
streamingEngine.init();
streamingEngine.init().catch(function(error) {
fail(error);
done();
});
});

@@ -502,3 +534,6 @@

onChooseStreams.and.callFake(defaultOnChooseStreams);
streamingEngine.init();
streamingEngine.init().catch(function(error) {
fail(error);
done();
});
});

@@ -550,3 +585,6 @@

onChooseStreams.and.callFake(defaultOnChooseStreams);
streamingEngine.init();
streamingEngine.init().catch(function(error) {
fail(error);
done();
});
});

@@ -571,8 +609,6 @@ });

onChooseStreams.and.callFake(defaultOnChooseStreams);
streamingEngine.init();
return streamingEngine.init();
}).then(function() {
return waitForTime(5);
})
.catch(fail)
.then(done);
}).catch(fail).then(done);
});

@@ -594,8 +630,6 @@

onChooseStreams.and.callFake(defaultOnChooseStreams);
streamingEngine.init();
return streamingEngine.init();
}).then(function() {
return waitForTime(8);
})
.catch(fail)
.then(done);
}).catch(fail).then(done);
});

@@ -614,13 +648,10 @@

onChooseStreams.and.callFake(defaultOnChooseStreams);
streamingEngine.init();
return streamingEngine.init();
}).then(function() {
return waitForTime(23);
})
.then(function() {
}).then(function() {
// Should be close enough to still have the gap buffered.
expect(video.buffered.length).toBe(2);
expect(onEvent).not.toHaveBeenCalled();
})
.catch(fail)
.then(done);
}).catch(fail).then(done);
});

@@ -639,13 +670,10 @@

onChooseStreams.and.callFake(defaultOnChooseStreams);
streamingEngine.init();
return streamingEngine.init();
}).then(function() {
return waitForTime(23);
})
.then(function() {
}).then(function() {
// Should be close enough to still have the gap buffered.
expect(video.buffered.length).toBe(2);
expect(onEvent).toHaveBeenCalled();
})
.catch(fail)
.then(done);
}).catch(fail).then(done);
});

@@ -675,5 +703,4 @@

onChooseStreams.and.callFake(defaultOnChooseStreams);
streamingEngine.init();
})
.catch(done.fail);
return streamingEngine.init();
}).catch(done.fail);
});

@@ -710,4 +737,3 @@

manifest = setupGappyManifest(gapAtStart, dropSegment);
audioStream1 = manifest.periods[0].variants[0].audio;
videoStream1 = manifest.periods[0].variants[0].video;
variant1 = manifest.periods[0].variants[0];

@@ -834,13 +860,6 @@ setupPlayhead();

function defaultOnChooseStreams(period) {
// Create empty object first and initialize the fields through
// [] to allow field names to be expressions.
var ret = {};
if (period == manifest.periods[0]) {
ret[ContentType.AUDIO] = audioStream1;
ret[ContentType.VIDEO] = videoStream1;
return ret;
return { variant: variant1, text: null };
} else if (period == manifest.periods[1]) {
ret[ContentType.AUDIO] = audioStream2;
ret[ContentType.VIDEO] = videoStream2;
return ret;
return { variant: variant2, text: null };
} else {

@@ -847,0 +866,0 @@ throw new Error();

@@ -19,2 +19,3 @@ /**

describe('HttpPlugin', function() {
/** @type {shakaExtern.RetryParameters} */
var retryParameters;

@@ -83,2 +84,3 @@

.then(function() {
/** @type {!jasmine.Ajax.RequestStub} */
var actual = jasmine.Ajax.requests.mostRecent();

@@ -85,0 +87,0 @@ expect(actual).toBeTruthy();

@@ -19,17 +19,24 @@ /**

describe('NetworkingEngine', /** @suppress {accessControls} */ function() {
/** @const */
var StatusPromise = shaka.test.StatusPromise;
/** @const */
var Util = shaka.test.Util;
/** @const */
var requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
/** @const */
var originalGetLocationProtocol =
shaka.net.NetworkingEngine.getLocationProtocol_;
/** @type {string} */
var fakeProtocol;
/** @type {!shaka.net.NetworkingEngine} */
var networkingEngine;
/** @type {!jasmine.Spy} */
var resolveScheme;
/** @type {!jasmine.Spy} */
var rejectScheme;
var requestType;
var Util;
var originalGetLocationProtocol;
var fakeProtocol;
/** @type {!shaka.util.Error} */
var error;
beforeAll(function() {
Util = shaka.test.Util;
requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
originalGetLocationProtocol =
shaka.net.NetworkingEngine.getLocationProtocol_;
shaka.net.NetworkingEngine.getLocationProtocol_ = function() {

@@ -41,2 +48,3 @@ return fakeProtocol;

beforeEach(function() {
fakeProtocol = 'http:';
error = new shaka.util.Error(

@@ -56,4 +64,6 @@ shaka.util.Error.Severity.RECOVERABLE,

.and.callFake(function() { return Promise.reject(error); });
shaka.net.NetworkingEngine.registerScheme('resolve', resolveScheme);
shaka.net.NetworkingEngine.registerScheme('reject', rejectScheme);
shaka.net.NetworkingEngine.registerScheme(
'resolve', Util.spyFunc(resolveScheme));
shaka.net.NetworkingEngine.registerScheme(
'reject', Util.spyFunc(rejectScheme));
});

@@ -135,13 +145,16 @@

describe('backoff', function() {
/** @const */
var baseDelay = 200;
var origSetTimeout;
/** @const */
var origSetTimeout = shaka.net.Backoff.setTimeout_;
/** @const */
var realRandom = Math.random;
/** @type {!jasmine.Spy} */
var setTimeoutSpy;
var realRandom;
beforeAll(function() {
origSetTimeout = shaka.net.NetworkingEngine.setTimeout_;
setTimeoutSpy = jasmine.createSpy('setTimeout');
setTimeoutSpy.and.callFake(origSetTimeout);
shaka.net.NetworkingEngine.setTimeout_ = setTimeoutSpy;
realRandom = Math.random;
shaka.net.Backoff.setTimeout_ = Util.spyFunc(setTimeoutSpy);
Math.random = function() { return 0.75; };

@@ -152,3 +165,3 @@ });

Math.random = realRandom;
shaka.net.NetworkingEngine.setTimeout_ = origSetTimeout;
shaka.net.Backoff.setTimeout_ = origSetTimeout;
});

@@ -320,5 +333,5 @@

it('fills in defaults for partial request objects', function(done) {
var originalRequest = {
var originalRequest = /** @type {shakaExtern.Request} */ ({
uris: ['resolve://foo']
};
});

@@ -339,2 +352,3 @@ resolveScheme.and.callFake(function(uri, request, requestTypePassed) {

describe('request filter', function() {
/** @type {!jasmine.Spy} */
var filter;

@@ -344,7 +358,7 @@

filter = jasmine.createSpy('request filter');
networkingEngine.registerRequestFilter(filter);
networkingEngine.registerRequestFilter(Util.spyFunc(filter));
});
afterEach(function() {
networkingEngine.unregisterRequestFilter(filter);
networkingEngine.unregisterRequestFilter(Util.spyFunc(filter));
});

@@ -382,3 +396,3 @@

var responseFilter = jasmine.createSpy('response filter');
networkingEngine.registerResponseFilter(responseFilter);
networkingEngine.registerResponseFilter(Util.spyFunc(responseFilter));

@@ -390,4 +404,3 @@ var p = new shaka.util.PublicPromise();

var request = createRequest('resolve://foo');
var r = networkingEngine.request(requestType, request);
Util.capturePromiseStatus(r);
var r = new StatusPromise(networkingEngine.request(requestType, request));

@@ -444,3 +457,3 @@ Util.delay(0.1).then(function() {

var secondFilter = jasmine.createSpy('second request filter');
networkingEngine.registerRequestFilter(secondFilter);
networkingEngine.registerRequestFilter(Util.spyFunc(secondFilter));

@@ -540,3 +553,3 @@ var order = 0;

var unusedFilter = jasmine.createSpy('unused filter');
networkingEngine.unregisterRequestFilter(unusedFilter);
networkingEngine.unregisterRequestFilter(Util.spyFunc(unusedFilter));
});

@@ -546,2 +559,3 @@ });

describe('response filter', function() {
/** @type {!jasmine.Spy} */
var filter;

@@ -551,3 +565,3 @@

filter = jasmine.createSpy('response filter');
networkingEngine.registerResponseFilter(filter);
networkingEngine.registerResponseFilter(Util.spyFunc(filter));
resolveScheme.and.callFake(function(request) {

@@ -562,3 +576,3 @@ var response = {

afterEach(function() {
networkingEngine.unregisterResponseFilter(filter);
networkingEngine.unregisterResponseFilter(Util.spyFunc(filter));
});

@@ -626,3 +640,3 @@

var secondFilter = jasmine.createSpy('second response filter');
networkingEngine.registerResponseFilter(secondFilter);
networkingEngine.registerResponseFilter(Util.spyFunc(secondFilter));

@@ -669,3 +683,3 @@ var order = 0;

var request = createRequest('resolve://foo');
var r = networkingEngine.request(requestType, request)
var r = new StatusPromise(networkingEngine.request(requestType, request)
.catch(fail)

@@ -677,4 +691,3 @@ .then(function(response) {

done();
});
Util.capturePromiseStatus(r);
}));

@@ -721,3 +734,3 @@ Util.delay(0.1).then(function() {

var unusedFilter = jasmine.createSpy('unused filter');
networkingEngine.unregisterResponseFilter(unusedFilter);
networkingEngine.unregisterResponseFilter(Util.spyFunc(unusedFilter));
});

@@ -732,6 +745,6 @@ });

var r1 = networkingEngine.request(requestType, request);
var r2 = networkingEngine.request(requestType, request);
Util.capturePromiseStatus(r1);
Util.capturePromiseStatus(r2);
var r1 =
new StatusPromise(networkingEngine.request(requestType, request));
var r2 =
new StatusPromise(networkingEngine.request(requestType, request));

@@ -741,6 +754,6 @@ expect(r1.status).toBe('pending');

/** @type {!shaka.test.StatusPromise} */
var d;
Util.delay(0.1).then(function() {
d = networkingEngine.destroy();
Util.capturePromiseStatus(d);
d = new StatusPromise(networkingEngine.destroy());
expect(d.status).toBe('pending');

@@ -765,3 +778,3 @@ expect(r1.status).toBe('pending');

var filter = jasmine.createSpy('request filter');
networkingEngine.registerRequestFilter(filter);
networkingEngine.registerRequestFilter(Util.spyFunc(filter));
var p = new shaka.util.PublicPromise();

@@ -777,5 +790,5 @@ filter.and.returnValue(p);

});
var r = networkingEngine.request(requestType, request);
Util.capturePromiseStatus(r);
var r = new StatusPromise(networkingEngine.request(requestType, request));
/** @type {!shaka.test.StatusPromise} */
var d;

@@ -786,4 +799,3 @@ Util.delay(0.1).then(function() {

d = networkingEngine.destroy();
Util.capturePromiseStatus(d);
d = new StatusPromise(networkingEngine.destroy());
p.resolve();

@@ -804,6 +816,6 @@

var r1 = networkingEngine.request(requestType, request);
var r2 = networkingEngine.request(requestType, request);
Util.capturePromiseStatus(r1);
Util.capturePromiseStatus(r2);
var r1 =
new StatusPromise(networkingEngine.request(requestType, request));
var r2 =
new StatusPromise(networkingEngine.request(requestType, request));

@@ -813,6 +825,6 @@ expect(r1.status).toBe('pending');

/** @type {!shaka.test.StatusPromise} */
var d;
Util.delay(0.1).then(function() {
d = networkingEngine.destroy();
Util.capturePromiseStatus(d);
d = new StatusPromise(networkingEngine.destroy());
expect(d.status).toBe('pending');

@@ -839,6 +851,8 @@

var r1 = networkingEngine.request(requestType, request);
var r1 =
new StatusPromise(networkingEngine.request(requestType, request));
/** @type {!shaka.test.StatusPromise} */
var r2;
/** @type {!shaka.test.StatusPromise} */
var d;
Util.capturePromiseStatus(r1);
expect(r1.status).toBe('pending');

@@ -849,8 +863,6 @@ Util.delay(0.1).then(function() {

d = networkingEngine.destroy();
Util.capturePromiseStatus(d);
d = new StatusPromise(networkingEngine.destroy());
expect(d.status).toBe('pending');
r2 = networkingEngine.request(requestType, request);
Util.capturePromiseStatus(r2);
r2 = new StatusPromise(networkingEngine.request(requestType, request));
expect(r2.status).toBe('pending');

@@ -892,5 +904,6 @@ // A new request has not been made.

var r1 = networkingEngine.request(requestType, request);
var r1 =
new StatusPromise(networkingEngine.request(requestType, request));
/** @type {shaka.test.StatusPromise} */
var d;
Util.capturePromiseStatus(r1);
expect(r1.status).toBe('pending');

@@ -900,4 +913,3 @@ Util.delay(0.1).then(function() {

d = networkingEngine.destroy();
Util.capturePromiseStatus(d);
d = new StatusPromise(networkingEngine.destroy());
expect(d.status).toBe('pending');

@@ -929,3 +941,4 @@

var onSegmentDownloaded = jasmine.createSpy('onSegmentDownloaded');
networkingEngine = new shaka.net.NetworkingEngine(onSegmentDownloaded);
networkingEngine =
new shaka.net.NetworkingEngine(Util.spyFunc(onSegmentDownloaded));

@@ -932,0 +945,0 @@ networkingEngine.request(requestType, createRequest('resolve://foo'))

@@ -18,10 +18,13 @@ /**

describe('DBEngine', function() {
describe('DBEngine', /** @suppress {accessControls} */ function() {
/** @const */
var oldName = shaka.offline.DBEngine.DB_NAME_;
/** @type {!shaka.offline.DBEngine} */
var db;
/** @type {!Object.<string, string>} */
var schema;
var oldName;
beforeAll(/** @suppress {accessControls} */ function() {
beforeAll(function() {
if (shaka.offline.DBEngine.isSupported()) {
oldName = shaka.offline.DBEngine.DB_NAME_;
shaka.offline.DBEngine.DB_NAME_ += '_test';

@@ -43,3 +46,3 @@ }

afterAll(/** @suppress {accessControls} */ function() {
afterAll(function() {
if (shaka.offline.DBEngine.isSupported()) {

@@ -142,3 +145,3 @@ shaka.offline.DBEngine.DB_NAME_ = oldName;

.then(function() {
return db.forEach('test', spy);
return db.forEach('test', shaka.test.Util.spyFunc(spy));
})

@@ -145,0 +148,0 @@ .then(function() {

@@ -18,11 +18,18 @@ /**

describe('Offline', function() {
var originalName;
describe('Offline', /** @suppress {accessControls} */ function() {
/** @const */
var originalName = shaka.offline.DBEngine.DB_NAME_;
/** @type {!shaka.offline.DBEngine} */
var dbEngine;
/** @type {!shaka.offline.Storage} */
var storage;
/** @type {!shaka.Player} */
var player;
/** @type {!HTMLVideoElement} */
var video;
/** @type {shakaExtern.SupportType} */
var support;
beforeAll(/** @suppress {accessControls} */ function(done) {
beforeAll(function(done) {
video = /** @type {!HTMLVideoElement} */ (document.createElement('video'));

@@ -39,3 +46,2 @@ video.width = 600;

originalName = shaka.offline.DBEngine.DB_NAME_;
shaka.offline.DBEngine.DB_NAME_ += '_test';

@@ -62,3 +68,3 @@ // Ensure we start with a clean slate.

afterAll(/** @suppress {accessControls} */ function() {
afterAll(function() {
document.body.removeChild(video);

@@ -115,2 +121,3 @@ shaka.offline.DBEngine.DB_NAME_ = originalName;

var sessionId;
/** @type {!shaka.media.DrmEngine} */
var drmEngine;

@@ -121,19 +128,17 @@ storage.store('test:sintel-enc')

expect(storedContent.offlineUri).toBe('offline:0');
return player.load(storedContent.offlineUri);
return dbEngine.get('manifest', 0);
})
.then(function() {
video.play();
return shaka.test.Util.delay(5);
})
.then(function() { return dbEngine.get('manifest', 0); })
.then(function(manifestDb) {
// Did we store a persistent license?
expect(manifestDb.sessionIds.length).toBeGreaterThan(0);
sessionId = manifestDb.sessionIds[0];
// Create a DrmEngine so we can try to load the session later.
// Create a DrmEngine now so we can use it to try to load the session
// later, after the content has been deleted.
var OfflineManifestParser = shaka.offline.OfflineManifestParser;
var manifest = OfflineManifestParser.reconstructManifest(manifestDb);
var netEngine = player.getNetworkingEngine();
goog.asserts.assert(netEngine, 'Must have a NetworkingEngine');
drmEngine = new shaka.media.DrmEngine(
player.getNetworkingEngine(), onError, function() {},
function() {});
netEngine, onError, function() {}, function() {});
drmEngine.configure(player.getConfiguration().drm);

@@ -143,2 +148,12 @@ return drmEngine.init(manifest, true /* isOffline */);

.then(function() {
// Load the stored content.
return player.load(storedContent.offlineUri);
})
.then(function() {
// Let it play some.
video.play();
return shaka.test.Util.delay(10);
})
.then(function() {
// Is it playing?
expect(video.currentTime).toBeGreaterThan(3);

@@ -148,3 +163,6 @@ expect(video.ended).toBe(false);

})
.then(function() { return storage.remove(storedContent); })
.then(function() {
// Remove the content.
return storage.remove(storedContent);
})
.then(

@@ -161,2 +179,4 @@ /**

.then(function(session) {
// We should not have been able to load the session.
// Removing the content should have deleted the session.
expect(session).toBeFalsy();

@@ -168,2 +188,53 @@ return drmEngine.destroy();

});
drm_it(
'stores, plays, and deletes protected content with a temporary license',
function(done) {
// Because this does not rely on persistent licenses, it should be
// testable with PlayReady as well as Widevine.
if (!support['offline'] ||
!support.drm['com.widevine.alpha'] ||
!support.drm['com.microsoft.playready']) {
pending('Offline or DRM not supported');
}
shaka.test.TestScheme.setupPlayer(player, 'sintel-enc');
var storedContent;
storage.configure({ usePersistentLicense: false });
storage.store('test:sintel-enc')
.then(function(content) {
storedContent = content;
expect(storedContent.offlineUri).toBe('offline:0');
return dbEngine.get('manifest', 0);
})
.then(function(manifestDb) {
// There should not be any licenses stored.
expect(manifestDb.sessionIds.length).toEqual(0);
// Load the stored content.
return player.load(storedContent.offlineUri);
})
.then(function() {
// Let it play some.
video.play();
return shaka.test.Util.delay(10);
})
.then(function() {
// Is it playing?
expect(video.currentTime).toBeGreaterThan(3);
expect(video.ended).toBe(false);
return player.unload();
})
.then(function() {
// Remove the content.
return storage.remove(storedContent);
})
.then(function() { return dbEngine.get('manifest', 0); })
.then(function(manifestDb) {
expect(manifestDb).toBeFalsy();
})
.catch(fail)
.then(done);
});
});

@@ -19,15 +19,27 @@ /**

describe('OfflineManifestParser', function() {
var originalIsStorageEngineSupported;
var originalCreateStorageEngine;
/** @const */
var originalIsStorageEngineSupported =
shaka.offline.OfflineUtils.isStorageEngineSupported;
/** @const */
var originalCreateStorageEngine =
shaka.offline.OfflineUtils.createStorageEngine;
/** @const */
var playerInterface =
/** @type {shakaExtern.ManifestParser.PlayerInterface} */ (null);
/**
* @type {{
* init: !jasmine.Spy,
* destroy: !jasmine.Spy,
* get: !jasmine.Spy,
* insert: !jasmine.Spy
* }}
*/
var fakeStorageEngine;
/** @type {!jasmine.Spy} */
var fakeCreateStorageEngine;
/** @type {shaka.offline.OfflineManifestParser} */
var parser;
beforeAll(function() {
originalIsStorageEngineSupported =
shaka.offline.OfflineUtils.isStorageEngineSupported;
originalCreateStorageEngine =
shaka.offline.OfflineUtils.createStorageEngine;
});
afterAll(function() {

@@ -57,3 +69,4 @@ shaka.offline.OfflineUtils.isStorageEngineSupported =

fakeCreateStorageEngine.and.returnValue(fakeStorageEngine);
shaka.offline.OfflineUtils.createStorageEngine = fakeCreateStorageEngine;
shaka.offline.OfflineUtils.createStorageEngine =
shaka.test.Util.spyFunc(fakeCreateStorageEngine);

@@ -80,3 +93,3 @@ parser = new shaka.offline.OfflineManifestParser();

parser.start(uri, /* playerInterface */ null)
parser.start(uri, playerInterface)
.then(function(manifest) {

@@ -99,3 +112,3 @@ expect(manifest).toBeTruthy();

parser.start(uri, /* playerInterface */ null)
parser.start(uri, playerInterface)
.then(fail)

@@ -123,3 +136,3 @@ .catch(function(err) {

parser.start(uri, /* playerInterface */ null)
parser.start(uri, playerInterface)
.then(fail)

@@ -136,3 +149,3 @@ .catch(function(err) {

var uri = 'offline:abc';
parser.start(uri, /* playerInterface */ null)
parser.start(uri, playerInterface)
.then(fail)

@@ -151,2 +164,3 @@ .catch(function(err) {

describe('update expiration', function() {
/** @type {string} */
var sessionId;

@@ -170,3 +184,3 @@

parser.start(uri, /* playerInterface */ null)
parser.start(uri, playerInterface)
.then(function(manifest) {

@@ -222,8 +236,6 @@ expect(manifest).toBeTruthy();

describe('reconstructing manifest', function() {
var originalReconstructPeriod;
/** @const */
var originalReconstructPeriod =
shaka.offline.OfflineUtils.reconstructPeriod;
beforeAll(function() {
originalReconstructPeriod = shaka.offline.OfflineUtils.reconstructPeriod;
});
afterAll(function() {

@@ -247,3 +259,3 @@ shaka.offline.OfflineUtils.reconstructPeriod = originalReconstructPeriod;

parser.start(uri, /* playerInterface */ null)
parser.start(uri, playerInterface)
.then(function(manifest) {

@@ -292,5 +304,6 @@ expect(manifest).toBeTruthy();

var spy = jasmine.createSpy('reconstructPeriod');
shaka.offline.OfflineUtils.reconstructPeriod = spy;
shaka.offline.OfflineUtils.reconstructPeriod =
shaka.test.Util.spyFunc(spy);
parser.start(uri, /* playerInterface */ null)
parser.start(uri, playerInterface)
.then(function(manifest) {

@@ -321,5 +334,6 @@ expect(manifest).toBeTruthy();

var spy = jasmine.createSpy('reconstructPeriod');
shaka.offline.OfflineUtils.reconstructPeriod = spy;
shaka.offline.OfflineUtils.reconstructPeriod =
shaka.test.Util.spyFunc(spy);
parser.start(uri, /* playerInterface */ null)
parser.start(uri, playerInterface)
.then(function(manifest) {

@@ -326,0 +340,0 @@ expect(manifest).toBeTruthy();

@@ -19,17 +19,15 @@ /**

describe('OfflineScheme', function() {
var OfflineScheme;
var originalIsStorageEngineSupported;
var originalCreateStorageEngine;
var OfflineScheme = shaka.offline.OfflineScheme;
var originalIsStorageEngineSupported =
shaka.offline.OfflineUtils.isStorageEngineSupported;
var originalCreateStorageEngine =
shaka.offline.OfflineUtils.createStorageEngine;
/** @type {{init: !jasmine.Spy, destroy: !jasmine.Spy, get: !jasmine.Spy}} */
var fakeStorageEngine;
/** @type {!jasmine.Spy} */
var fakeCreateStorageEngine;
/** @type {shakaExtern.Request} */
var request;
beforeAll(function() {
OfflineScheme = shaka.offline.OfflineScheme;
originalIsStorageEngineSupported =
shaka.offline.OfflineUtils.isStorageEngineSupported;
originalCreateStorageEngine =
shaka.offline.OfflineUtils.createStorageEngine;
});
afterAll(function() {

@@ -58,3 +56,4 @@ shaka.offline.OfflineUtils.isStorageEngineSupported =

fakeCreateStorageEngine.and.returnValue(fakeStorageEngine);
shaka.offline.OfflineUtils.createStorageEngine = fakeCreateStorageEngine;
shaka.offline.OfflineUtils.createStorageEngine =
shaka.test.Util.spyFunc(fakeCreateStorageEngine);

@@ -61,0 +60,0 @@ // The whole request is ignored by the OfflineScheme.

@@ -265,3 +265,4 @@ /**

containsEmsgBoxes: false,
roles: []
roles: [],
channelsCount: null

@@ -268,0 +269,0 @@ };

@@ -19,18 +19,21 @@ /**

describe('Storage', function() {
var originalSupportsStorageEngine;
var originalCreateStorageEngine;
var SegmentReference;
/** @const */
var SegmentReference = shaka.media.SegmentReference;
/** @const */
var originalSupportsStorageEngine =
shaka.offline.OfflineUtils.supportsStorageEngine;
/** @const */
var originalCreateStorageEngine =
shaka.offline.OfflineUtils.createStorageEngine;
/** @type {shaka.test.MemoryDBEngine} */
var fakeStorageEngine;
/** @type {!shaka.offline.Storage} */
var storage;
/** @type {!shaka.Player} */
var player;
/** @type {!shaka.test.FakeNetworkingEngine} */
var netEngine;
beforeAll(function() {
SegmentReference = shaka.media.SegmentReference;
originalSupportsStorageEngine =
shaka.offline.OfflineUtils.supportsStorageEngine;
originalCreateStorageEngine =
shaka.offline.OfflineUtils.createStorageEngine;
});
afterAll(function() {

@@ -111,3 +114,4 @@ shaka.offline.OfflineUtils.supportsStorageEngine =

segments: [],
roles: []
roles: [],
channelsCount: null
}

@@ -152,3 +156,6 @@ ]

videoId: 0,
audioId: 1
audioId: 1,
channelsCount: null,
audioBandwidth: null,
videoBandwidth: null
}

@@ -183,13 +190,16 @@ ];

describe('store', function() {
var originalWarning;
/** @const */
var originalWarning = shaka.log.warning;
/** @type {shakaExtern.Manifest} */
var manifest;
/** @type {!Array.<shakaExtern.Track>} */
var tracks;
/** @type {!shaka.test.FakeDrmEngine} */
var drmEngine;
/** @type {!shaka.media.SegmentIndex} */
var stream1Index;
/** @type {!shaka.media.SegmentIndex} */
var stream2Index;
beforeAll(function() {
originalWarning = shaka.log.warning;
});
beforeEach(function() {

@@ -209,8 +219,14 @@ drmEngine = new shaka.test.FakeDrmEngine();

// will have 0 for bandwidth, so adjust the tracks list to match.
tracks.forEach(function(t) { t.bandwidth = 0; });
tracks.forEach(function(t) {
t.bandwidth = 0;
t.audioBandwidth = null;
t.videoBandwidth = null;
});
storage.loadInternal = function() {
return Promise.resolve({
manifest: manifest,
drmEngine: drmEngine
return Promise.resolve().then(function() {
return {
manifest: manifest,
drmEngine: drmEngine
};
});

@@ -271,3 +287,3 @@ };

var warning = jasmine.createSpy('shaka.log.warning');
shaka.log.warning = warning;
shaka.log.warning = shaka.test.Util.spyFunc(warning);
storage.store('')

@@ -333,4 +349,9 @@ .then(function(data) {

licenseServerUri: 'http://example.com',
persistentStateRequire: true,
audioRobustness: 'HARDY'
persistentStateRequired: true,
distinctiveIdentifierRequired: false,
initData: null,
keyIds: null,
serverCertificate: null,
audioRobustness: 'HARDY',
videoRobustness: 'OTHER'
};

@@ -399,4 +420,9 @@ drmEngine.setDrmInfo(drmInfo);

licenseServerUri: 'http://example.com',
persistentStateRequire: true,
audioRobustness: 'HARDY'
persistentStateRequired: true,
distinctiveIdentifierRequired: false,
keyIds: null,
initData: null,
serverCertificate: null,
audioRobustness: 'HARDY',
videoRobustness: 'OTHER'
};

@@ -466,3 +492,3 @@ drmEngine.setDrmInfo(drmInfo);

tracks: tracks,
appMetadata: undefined
appMetadata: {}
});

@@ -522,3 +548,3 @@

tracks: tracks,
appMetadata: undefined
appMetadata: {}
});

@@ -725,2 +751,3 @@

describe('default track selection callback', function() {
/** @type {!Array.<shakaExtern.Track>} */
var allTextTracks;

@@ -785,5 +812,7 @@

storage.loadInternal = function() {
return Promise.resolve({
manifest: manifest,
drmEngine: drmEngine
return Promise.resolve().then(function() {
return {
manifest: manifest,
drmEngine: /** @type {!shaka.media.DrmEngine} */ (drmEngine)
};
});

@@ -793,3 +822,3 @@ };

// Use the default track selection callback.
storage.configure({ trackSelectionCallback: undefined });
storage.configure({trackSelectionCallback: undefined});
});

@@ -825,3 +854,3 @@

var warning = jasmine.createSpy('shaka.log.warning');
shaka.log.warning = warning;
shaka.log.warning = shaka.test.Util.spyFunc(warning);

@@ -886,5 +915,43 @@ // An exact match is available for en-US, en-GB, and en.

}); // describe('default track selection callback')
describe('temporary license', function() {
/** @type {shakaExtern.DrmInfo} */
var drmInfo;
beforeEach(function() {
drmInfo = {
keySystem: 'com.example.abc',
licenseServerUri: 'http://example.com',
persistentStateRequired: false,
distinctiveIdentifierRequired: false,
keyIds: [],
initData: null,
serverCertificate: null,
audioRobustness: 'HARDY',
videoRobustness: 'OTHER'
};
drmEngine.setDrmInfo(drmInfo);
drmEngine.setSessionIds(['abcd']);
storage.configure({usePersistentLicense: false});
});
it('does not store offline sessions', function(done) {
storage.store('')
.then(function(data) {
expect(data.offlineUri).toBe('offline:0');
return fakeStorageEngine.get('manifest', 0);
})
.then(function(manifestDb) {
expect(manifestDb).toBeTruthy();
expect(manifestDb.drmInfo).toEqual(drmInfo);
expect(manifestDb.sessionIds.length).toEqual(0);
})
.catch(fail)
.then(done);
});
}); // describe('temporary license')
}); // describe('store')
describe('remove', function() {
/** @type {number} */
var segmentId;

@@ -981,2 +1048,20 @@

it('will delete content with a temporary license', function(done) {
storage.configure({usePersistentLicense: false});
var manifestId = 0;
createAndInsertSegments(manifestId, 5)
.then(function(refs) {
var manifest = createManifest(manifestId);
manifest.periods[0].streams.push({segments: refs});
return fakeStorageEngine.insert('manifest', manifest);
})
.then(function() {
expectDatabaseCount(1, 5);
return removeManifest(manifestId);
})
.then(function() { expectDatabaseCount(0, 0); })
.catch(fail)
.then(done);
});
it('will not delete other manifest\'s segments', function(done) {

@@ -1046,3 +1131,4 @@ var manifestId1 = 1;

it('throws an error if the URI is malformed', function(done) {
var bogusContent = {offlineUri: 'foo:bar'};
var bogusContent =
/** @type {shakaExtern.StoredContent} */ ({offlineUri: 'foo:bar'});
storage.remove(bogusContent).then(fail).catch(function(error) {

@@ -1089,3 +1175,4 @@ var expectedError = new shaka.util.Error(

function removeManifest(manifestId) {
return storage.remove({offlineUri: 'offline:' + manifestId});
return storage.remove(/** @type {shakaExtern.StoredContent} */ (
{offlineUri: 'offline:' + manifestId}));
}

@@ -1092,0 +1179,0 @@

@@ -19,5 +19,9 @@ /**

describe('Player', function() {
var Util;
/** @const */
var Util = shaka.test.Util;
/** @const */
var Feature = shakaAssets.Feature;
/** @type {!jasmine.Spy} */
var onErrorSpy;
var Feature;

@@ -33,3 +37,3 @@ /** @type {shakaExtern.SupportType} */

var shaka;
var compiledShaka;

@@ -43,11 +47,6 @@ beforeAll(function(done) {

// Load test utils from outside the compiled library.
Util = window.shaka.test.Util;
// Load asset features from outside the compiled library.
Feature = window.shakaAssets.Feature;
var loaded = window.shaka.util.PublicPromise();
var loaded = new shaka.util.PublicPromise();
if (getClientArg('uncompiled')) {
// For debugging purposes, use the uncompiled library.
shaka = window.shaka;
compiledShaka = shaka;
loaded.resolve();

@@ -58,8 +57,8 @@ } else {

require(['/base/dist/shaka-player.compiled.js'], function(shakaModule) {
shaka = shakaModule;
shaka.net.NetworkingEngine.registerScheme(
'test', window.shaka.test.TestScheme);
shaka.media.ManifestParser.registerParserByMime(
compiledShaka = shakaModule;
compiledShaka.net.NetworkingEngine.registerScheme(
'test', shaka.test.TestScheme);
compiledShaka.media.ManifestParser.registerParserByMime(
'application/x-test-manifest',
window.shaka.test.TestScheme.ManifestParser);
shaka.test.TestScheme.ManifestParser);

@@ -71,5 +70,5 @@ loaded.resolve();

loaded.then(function() {
return window.shaka.test.TestScheme.createManifests(shaka, '_compiled');
return shaka.test.TestScheme.createManifests(compiledShaka, '_compiled');
}).then(function() {
return shaka.Player.probeSupport();
return compiledShaka.Player.probeSupport();
}).then(function(supportResults) {

@@ -82,10 +81,10 @@ support = supportResults;

beforeEach(function() {
player = new shaka.Player(video);
player = new compiledShaka.Player(video);
// Grab event manager from the uncompiled library:
eventManager = new window.shaka.util.EventManager();
eventManager = new shaka.util.EventManager();
onErrorSpy = jasmine.createSpy('onError');
onErrorSpy.and.callFake(function(event) { fail(event.detail); });
eventManager.listen(player, 'error', onErrorSpy);
eventManager.listen(player, 'error', Util.spyFunc(onErrorSpy));
});

@@ -131,6 +130,5 @@

id: jasmine.any(Number),
// Include 'window' to use uncompiled version version of the
// library.
type: window.shaka.util.ManifestParserUtils.ContentType.VIDEO,
fromAdaptation: true
type: 'variant',
fromAdaptation: true,
bandwidth: 0
}]),

@@ -154,3 +152,2 @@

it('does not cause cues to be null', function(done) {
var textTrack = video.textTracks[0];
player.load('test:sintel_compiled').then(function() {

@@ -160,12 +157,19 @@ video.play();

}).then(function() {
// This should not be null initially.
expect(textTrack.cues).not.toBe(null);
// This TextTrack was created as part of load() when we set up the
// TextDisplayer.
var textTrack = video.textTracks[0];
expect(textTrack).not.toBe(null);
player.setTextTrackVisibility(true);
// This should definitely not be null when visible.
expect(textTrack.cues).not.toBe(null);
if (textTrack) {
// This should not be null initially.
expect(textTrack.cues).not.toBe(null);
player.setTextTrackVisibility(false);
// This should not transition to null when invisible.
expect(textTrack.cues).not.toBe(null);
player.setTextTrackVisibility(true);
// This should definitely not be null when visible.
expect(textTrack.cues).not.toBe(null);
player.setTextTrackVisibility(false);
// This should not transition to null when invisible.
expect(textTrack.cues).not.toBe(null);
}
}).catch(fail).then(done);

@@ -176,3 +180,3 @@ });

describe('plays', function() {
it('while external text tracks', function(done) {
it('with external text tracks', function(done) {
player.load('test:sintel_no_text_compiled').then(function() {

@@ -223,3 +227,3 @@ // For some reason, using path-absolute URLs (i.e. without the hostname)

window.shakaAssets.testAssets.forEach(function(asset) {
shakaAssets.testAssets.forEach(function(asset) {
if (asset.disabled) return;

@@ -359,3 +363,3 @@

function addLicenseRequestHeaders(headers, requestType, request) {
var RequestType = shaka.net.NetworkingEngine.RequestType;
var RequestType = compiledShaka.net.NetworkingEngine.RequestType;
if (requestType != RequestType.LICENSE) return;

@@ -362,0 +366,0 @@

@@ -20,9 +20,7 @@ /**

describe('Promise polyfill', function() {
var Promise;
/** @const */
var Promise = shaka.polyfill.Promise;
/** @const */
var dummy = { 'FOO': 'BAR' };
beforeAll(function() {
Promise = shaka.polyfill.Promise;
});
describe('resolve', function() {

@@ -133,3 +131,3 @@ it('Promise.resolve returns a resolved promise', function(done) {

p.then(function() {
p.promise.then(function() {
expect(resolved).toBe(true);

@@ -145,3 +143,4 @@ done();

var rejected = jasmine.createSpy('onRejected');
p.then(fulfilled, rejected);
p.promise.then(shaka.test.Util.spyFunc(fulfilled),
shaka.test.Util.spyFunc(rejected));

@@ -171,3 +170,3 @@ p.resolve(dummy);

p.then(fail, function() {
p.promise.then(fail, function() {
expect(rejected).toBe(true);

@@ -183,3 +182,4 @@ done();

var rejected = jasmine.createSpy('onRejected');
p.then(fulfilled, rejected);
p.promise.then(shaka.test.Util.spyFunc(fulfilled),
shaka.test.Util.spyFunc(rejected));

@@ -239,3 +239,3 @@ p.reject(dummy);

var p = deferred();
var p2 = p.then();
var p2 = p.promise.then();

@@ -260,11 +260,11 @@ expect(p2).toBeTruthy();

p.then(function() {
return p2;
return p2.promise;
}, fail).then(function(i) {
expect(i).toBe(2);
expect(p2.resolved).toBe(true);
return p3;
return p3.promise;
}).then(fail, function(i) {
expect(i).toBe(3);
expect(p3.resolved).toBe(true);
return p4;
return p4.promise;
}).then(function(i) {

@@ -336,3 +336,3 @@ expect(i).toBe(4);

var all = Promise.all([p1, p2, p3]);
var all = Promise.all([p1.promise, p2.promise, p3.promise]);
all.then(function() {

@@ -385,3 +385,3 @@ expect(p1.resolved).toBe(true);

var all = Promise.all([p1, p2]);
var all = Promise.all([p1.promise, p2.promise]);
all.then(fail, function(e) {

@@ -398,7 +398,7 @@ expect(e).toBe(1);

var p1 = Promise.resolve(0);
var p2 = Promise.resolve().then(function() { return p; });
var p2 = Promise.resolve().then(function() { return p.promise; });
var all = Promise.all([p1, p2]);
var spy = jasmine.createSpy('resolve').and.callFake(done);
all.then(spy, fail);
all.then(shaka.test.Util.spyFunc(spy), fail);

@@ -416,3 +416,3 @@ setTimeout(function() {

Promise.all([p1, p2, p3]).then(function(data) {
Promise.all([p1.promise, p2.promise, p3.promise]).then(function(data) {
expect(data.length).toBe(3);

@@ -461,3 +461,3 @@ expect(data[0]).toBe(1);

var anyDone = false;
var race = Promise.race([p1, p2, p3]);
var race = Promise.race([p1.promise, p2.promise, p3.promise]);
race.then(function() { expect(anyDone).toBe(true); })

@@ -477,3 +477,3 @@ .catch(fail).then(done);

var race = Promise.race([p1, p2, p3]);
var race = Promise.race([p1.promise, p2, p3.promise]);
race.then(function(arg) { expect(arg).toBe(2); })

@@ -488,3 +488,3 @@ .catch(fail).then(done);

var race = Promise.race([p1, p2, p3]);
var race = Promise.race([p1.promise, p2.promise, p3.promise]);
race.then(function(arg) { expect(arg).toBe(2); })

@@ -501,3 +501,3 @@ .catch(fail).then(done);

var race = Promise.race([p1, p2]);
var race = Promise.race([p1.promise, p2]);
race.then(fail, function(e) {

@@ -514,3 +514,3 @@ expect(e).toBe(2);

var race = Promise.race([p1, p2]);
var race = Promise.race([p1.promise, p2.promise]);
race.then(fail, function(e) {

@@ -528,3 +528,3 @@ expect(e).toBe(2);

var race = Promise.race([p1, p2]);
var race = Promise.race([p1.promise, p2.promise]);
race.then(function(e) { expect(e).toBe(2); }, fail).then(done);

@@ -539,7 +539,7 @@

var p = deferred();
var p1 = Promise.resolve().then(function() { return p; });
var p1 = Promise.resolve().then(function() { return p.promise; });
var race = Promise.race([p1]);
var spy = jasmine.createSpy('resolve').and.callFake(done);
race.then(spy, fail);
race.then(shaka.test.Util.spyFunc(spy), fail);

@@ -572,3 +572,3 @@ setTimeout(function() {

Promise.race([p1, p2]).then(function(arg) {
Promise.race([p1.promise, p2]).then(function(arg) {
expect(arg).toBe(null);

@@ -593,3 +593,3 @@ }).catch(fail).then(done);

var called = false;
p.then(function() {
p.promise.then(function() {
expect(called).toBe(false);

@@ -612,3 +612,3 @@ called = true;

var called = false;
p.then(function() {
p.promise.then(function() {
fail('Promise resolved when should reject');

@@ -664,6 +664,6 @@ done();

p.then(function() {
p.promise.then(function() {
expect(++calls).toBe(1);
});
p.then(function() {
p.promise.then(function() {
expect(++calls).toBe(2);

@@ -673,3 +673,3 @@ // Ensure that it handles exceptions correctly.

});
p.then(function() {
p.promise.then(function() {
expect(++calls).toBe(3);

@@ -691,6 +691,6 @@ });

p.then(undefined, function() {
p.promise.then(undefined, function() {
expect(++calls).toBe(1);
});
p.then(undefined, function() {
p.promise.then(undefined, function() {
expect(++calls).toBe(2);

@@ -700,3 +700,3 @@ // Ensure that it handles exceptions correctly.

});
p.then(undefined, function() {
p.promise.then(undefined, function() {
expect(++calls).toBe(3);

@@ -762,2 +762,9 @@ });

/**
* @return {{
* promise: shaka.polyfill.Promise,
* resolve: function(*=),
* reject: function(*=)
* }}
*/
function deferred() {

@@ -771,6 +778,8 @@ var resolveLocal;

p.resolve = resolveLocal;
p.reject = rejectLocal;
return p;
return {
promise: p,
resolve: resolveLocal,
reject: rejectLocal
};
}
});

@@ -32,3 +32,9 @@ /**

/**
* @param {!Object} matchers
*/
jasmine.addMatchers = function(matchers) {};
/**

@@ -150,3 +156,10 @@ * @constructor

/**
* A custom matcher for DOM Node objects.
* @param {!Element} expected
*/
jasmine.Matchers.prototype.toEqualElement = function(expected) {};
/**

@@ -185,6 +198,6 @@ * @constructor

/**
* @param {*} value
* @param {*=} opt_value
* @return {!jasmine.Spy}
*/
jasmine.SpyStrategy.prototype.throwError = function(value) {};
jasmine.SpyStrategy.prototype.throwError = function(opt_value) {};

@@ -269,3 +282,2 @@

* @extends {Function}
* @struct
*/

@@ -285,3 +297,3 @@ jasmine.Spy = function() {};

* @param {string} name
* @return {!jasmine.Spy|!Function}
* @return {!jasmine.Spy}
* @see https://github.com/google/closure-compiler/issues/1422

@@ -295,3 +307,3 @@ */

* @param {!Array.<string>} members
* @return {!Object}
* @return {?}
*/

@@ -304,3 +316,3 @@ jasmine.createSpyObj = function(name, members) {};

* @param {string} name
* @return {!jasmine.Spy|!Function}
* @return {!jasmine.Spy}
* @see https://github.com/google/closure-compiler/issues/1422

@@ -524,15 +536,15 @@ */

/** const {string|RegExp} */
/** @const {string|RegExp} */
jasmine.Ajax.RequestStub.prototype.query;
/** const {string|RegExp} */
/** @const {string|RegExp} */
jasmine.Ajax.RequestStub.prototype.data;
/** const {string} */
/** @const {string} */
jasmine.Ajax.RequestStub.prototype.method;
/** const {!Object.<string, string>} */
/** @const {!Object.<string, string>} */
jasmine.Ajax.RequestStub.prototype.requestHeaders;

@@ -571,6 +583,2 @@

/** @const {!jasmine.Ajax.RequestTracker} */
jasmine.Ajax.requests;
/** @return {!jasmine.Ajax.RequestStub} */

@@ -597,1 +605,4 @@ jasmine.Ajax.RequestTracker.prototype.first = function() {};

/** @const {!jasmine.Ajax.RequestTracker} */
jasmine.Ajax.requests;

@@ -33,3 +33,4 @@ /**

clockSyncUri: '',
ignoreDrmInfo: false
ignoreDrmInfo: false,
xlinkFailGracefully: false
},

@@ -43,46 +44,2 @@ hls: { defaultTimeOffset: 0 }

/**
* Verifies the segment references in a manifest.
*
* @param {shakaExtern.Manifest} manifest
* @param {!Array.<shaka.media.SegmentReference>} references
* @param {number} periodIndex
*/
shaka.test.Dash.verifySegmentIndex = function(
manifest, references, periodIndex) {
expect(manifest).toBeTruthy();
var stream = manifest.periods[periodIndex].variants[0].video;
expect(stream).toBeTruthy();
expect(stream.findSegmentPosition).toBeTruthy();
expect(stream.getSegmentReference).toBeTruthy();
if (references.length == 0) {
expect(stream.findSegmentPosition(0)).toBe(null);
return;
}
// Even if the first segment doesn't start at 0, this should return the first
// segment.
expect(stream.findSegmentPosition(0)).toBe(references[0].position);
for (var i = 0; i < references.length - 1; i++) {
var expectedRef = references[i];
var position = stream.findSegmentPosition(expectedRef.startTime);
expect(position).not.toBe(null);
var actualRef =
stream.getSegmentReference(/** @type {number} */ (position));
expect(actualRef).toEqual(expectedRef);
}
// Make sure that the references stop at the end.
var lastExpectedReference = references[references.length - 1];
var positionAfterEnd =
stream.findSegmentPosition(lastExpectedReference.endTime);
expect(positionAfterEnd).toBe(null);
var referencePastEnd =
stream.getSegmentReference(lastExpectedReference.position + 1);
expect(referencePastEnd).toBe(null);
};
/**
* Tests the segment index produced by the DASH manifest parser.

@@ -100,3 +57,4 @@ *

new shaka.test.FakeNetworkingEngine({'dummy://foo': buffer}),
filterPeriod: function() {},
filterNewPeriod: function() {},
filterAllPeriods: function() {},
onTimelineRegionAdded: fail, // Should not have any EventStream elements.

@@ -108,3 +66,4 @@ onEvent: fail,

.then(function(manifest) {
shaka.test.Dash.verifySegmentIndex(manifest, references, 0);
var stream = manifest.periods[0].variants[0].video;
shaka.test.ManifestParser.verifySegmentIndex(stream, references);
})

@@ -129,3 +88,4 @@ .catch(fail)

new shaka.test.FakeNetworkingEngine({'dummy://foo': manifestData}),
filterPeriod: function() {},
filterNewPeriod: function() {},
filterAllPeriods: function() {},
onTimelineRegionAdded: fail, // Should not have any EventStream elements.

@@ -239,24 +199,2 @@ onEvent: fail,

/**
* Creates a segment reference using a relative URI.
*
* @param {string} uri A relative URI to http://example.com
* @param {number} position
* @param {number} start
* @param {number} end
* @param {number=} opt_startByte
* @param {?number=} opt_endByte
* @return {!shaka.media.SegmentReference}
*/
shaka.test.Dash.makeReference =
function(uri, position, start, end, opt_startByte, opt_endByte) {
var base = 'http://example.com/';
var startByte = opt_startByte || 0;
var endByte = opt_endByte || null;
var getUris = function() { return [base + uri]; };
return new shaka.media.SegmentReference(
position, start, end, getUris, startByte, endByte);
};
/**
* Makes a set of tests for SegmentTimeline. This is used to test

@@ -273,2 +211,4 @@ * SegmentTimeline within both SegmentList and SegmentTemplate.

var Dash = shaka.test.Dash;
var ManifestParser = shaka.test.ManifestParser;
var baseUri = 'http://example.com/';

@@ -303,7 +243,7 @@ /**

var references = [
Dash.makeReference('s1.mp4', 1, 34, 46),
Dash.makeReference('s2.mp4', 2, 46, 67),
Dash.makeReference('s3.mp4', 3, 67, 111),
Dash.makeReference('s4.mp4', 4, 111, 121),
Dash.makeReference('s5.mp4', 5, 121, 131)
ManifestParser.makeReference('s1.mp4', 1, 34, 46, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 46, 67, baseUri),
ManifestParser.makeReference('s3.mp4', 3, 67, 111, baseUri),
ManifestParser.makeReference('s4.mp4', 4, 111, 121, baseUri),
ManifestParser.makeReference('s5.mp4', 5, 121, 131, baseUri)
];

@@ -323,7 +263,7 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s1.mp4', 1, 34, 46),
Dash.makeReference('s2.mp4', 2, 46, 56),
Dash.makeReference('s3.mp4', 3, 56, 66),
Dash.makeReference('s4.mp4', 4, 66, 76),
Dash.makeReference('s5.mp4', 5, 76, 120)
ManifestParser.makeReference('s1.mp4', 1, 34, 46, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 46, 56, baseUri),
ManifestParser.makeReference('s3.mp4', 3, 56, 66, baseUri),
ManifestParser.makeReference('s4.mp4', 4, 66, 76, baseUri),
ManifestParser.makeReference('s5.mp4', 5, 76, 120, baseUri)
];

@@ -344,7 +284,7 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s1.mp4', 1, 22, 30),
Dash.makeReference('s2.mp4', 2, 30, 40),
Dash.makeReference('s3.mp4', 3, 40, 50),
Dash.makeReference('s4.mp4', 4, 50, 62),
Dash.makeReference('s5.mp4', 5, 62, 72)
ManifestParser.makeReference('s1.mp4', 1, 22, 30, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 30, 40, baseUri),
ManifestParser.makeReference('s3.mp4', 3, 40, 50, baseUri),
ManifestParser.makeReference('s4.mp4', 4, 50, 62, baseUri),
ManifestParser.makeReference('s5.mp4', 5, 62, 72, baseUri)
];

@@ -363,7 +303,7 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s1.mp4', 1, 5, 10),
Dash.makeReference('s2.mp4', 2, 10, 20),
Dash.makeReference('s3.mp4', 3, 20, 30),
Dash.makeReference('s4.mp4', 4, 30, 40),
Dash.makeReference('s5.mp4', 5, 40, 50)
ManifestParser.makeReference('s1.mp4', 1, 5, 10, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 10, 20, baseUri),
ManifestParser.makeReference('s3.mp4', 3, 20, 30, baseUri),
ManifestParser.makeReference('s4.mp4', 4, 30, 40, baseUri),
ManifestParser.makeReference('s5.mp4', 5, 40, 50, baseUri)
];

@@ -382,7 +322,7 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s1.mp4', 1, 0, 10),
Dash.makeReference('s2.mp4', 2, 10, 20),
Dash.makeReference('s3.mp4', 3, 20, 30),
Dash.makeReference('s4.mp4', 4, 30, 40),
Dash.makeReference('s5.mp4', 5, 40, 50)
ManifestParser.makeReference('s1.mp4', 1, 0, 10, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 10, 20, baseUri),
ManifestParser.makeReference('s3.mp4', 3, 20, 30, baseUri),
ManifestParser.makeReference('s4.mp4', 4, 30, 40, baseUri),
ManifestParser.makeReference('s5.mp4', 5, 40, 50, baseUri)
];

@@ -404,7 +344,7 @@ Dash.testSegmentIndex(done, source, references);

var references = [
Dash.makeReference('s1.mp4', 1, 2, 2.5),
Dash.makeReference('s2.mp4', 2, 2.5, 3.5),
Dash.makeReference('s3.mp4', 3, 3.5, 7),
Dash.makeReference('s4.mp4', 4, 7, 8),
Dash.makeReference('s5.mp4', 5, 8, 9)
ManifestParser.makeReference('s1.mp4', 1, 2, 2.5, baseUri),
ManifestParser.makeReference('s2.mp4', 2, 2.5, 3.5, baseUri),
ManifestParser.makeReference('s3.mp4', 3, 3.5, 7, baseUri),
ManifestParser.makeReference('s4.mp4', 4, 7, 8, baseUri),
ManifestParser.makeReference('s5.mp4', 5, 8, 9, baseUri)
];

@@ -411,0 +351,0 @@ Dash.testSegmentIndex(done, source, references);

@@ -72,19 +72,63 @@ /**

spyOn(this, 'destroy').and.callThrough();
spyOn(this, 'init').and.callThrough();
spyOn(this, 'reinitText').and.callThrough();
spyOn(this, 'bufferStart').and.callThrough();
spyOn(this, 'bufferEnd').and.callThrough();
spyOn(this, 'bufferedAheadOf').and.callThrough();
spyOn(this, 'appendBuffer').and.callThrough();
spyOn(this, 'remove').and.callThrough();
spyOn(this, 'clear').and.callThrough();
spyOn(this, 'flush').and.callThrough();
spyOn(this, 'endOfStream').and.callThrough();
spyOn(this, 'setStreamProperties').and.callThrough();
spyOn(this, 'setDuration').and.callThrough();
spyOn(this, 'getDuration').and.callThrough();
/** @type {!jasmine.Spy} */
this.init = jasmine.createSpy('init').and.stub();
/** @type {!jasmine.Spy} */
this.reinitText = jasmine.createSpy('reinitText').and.stub();
/** @type {!jasmine.Spy} */
this.endOfStream =
jasmine.createSpy('endOfStream').and.returnValue(Promise.resolve());
/** @type {!jasmine.Spy} */
this.setDuration = jasmine.createSpy('setDuration')
.and.callFake(this.setDurationImpl_.bind(this));
/** @type {!jasmine.Spy} */
this.getDuration = jasmine.createSpy('getDuration')
.and.callFake(this.getDurationImpl_.bind(this));
/** @type {!jasmine.Spy} */
this.appendBuffer = jasmine.createSpy('appendBuffer')
.and.callFake(this.appendBufferImpl.bind(this));
/** @type {!jasmine.Spy} */
this.clear = jasmine.createSpy('clear')
.and.callFake(this.clearImpl_.bind(this));
/** @type {!jasmine.Spy} */
this.bufferStart = jasmine.createSpy('bufferStart')
.and.callFake(this.bufferStartImpl_.bind(this));
/** @type {!jasmine.Spy} */
this.bufferEnd = jasmine.createSpy('bufferEnd')
.and.callFake(this.bufferEndImpl_.bind(this));
/** @type {!jasmine.Spy} */
this.isBuffered = jasmine.createSpy('isBuffered')
.and.callFake(this.isBufferedImpl_.bind(this));
/** @type {!jasmine.Spy} */
this.bufferedAheadOf = jasmine.createSpy('bufferedAheadOf')
.and.callFake(this.bufferedAheadOfImpl_.bind(this));
/** @type {!jasmine.Spy} */
this.setStreamProperties = jasmine.createSpy('setStreamProperties')
.and.callFake(this.setStreamPropertiesImpl_.bind(this));
/** @type {!jasmine.Spy} */
this.remove = jasmine.createSpy('remove')
.and.callFake(this.removeImpl.bind(this));
/** @type {!jasmine.Spy} */
this.flush = jasmine.createSpy('flush').and.returnValue(Promise.resolve());
};
/** @override */
shaka.test.FakeMediaSourceEngine.prototype.destroy = function() {
return Promise.resolve();
};
/**

@@ -116,18 +160,8 @@ * @typedef {{

/** @override */
shaka.test.FakeMediaSourceEngine.prototype.destroy = function() {
return Promise.resolve();
};
/** @override */
shaka.test.FakeMediaSourceEngine.prototype.init = function() {};
/** @override */
shaka.test.FakeMediaSourceEngine.prototype.reinitText = function() {};
/** @override */
shaka.test.FakeMediaSourceEngine.prototype.bufferStart = function(type) {
/**
* @param {string} type
* @return {?number}
* @private
*/
shaka.test.FakeMediaSourceEngine.prototype.bufferStartImpl_ = function(type) {
if (this.segments[type] === undefined) throw new Error('unexpected type');

@@ -143,4 +177,8 @@

/** @override */
shaka.test.FakeMediaSourceEngine.prototype.bufferEnd = function(type) {
/**
* @param {string} type
* @return {?number}
* @private
*/
shaka.test.FakeMediaSourceEngine.prototype.bufferEndImpl_ = function(type) {
if (this.segments[type] === undefined) throw new Error('unexpected type');

@@ -156,4 +194,10 @@

/** @override */
shaka.test.FakeMediaSourceEngine.prototype.isBuffered = function(type, time) {
/**
* @param {string} type
* @param {number} time
* @return {boolean}
* @private
*/
shaka.test.FakeMediaSourceEngine.prototype.isBufferedImpl_ =
function(type, time) {
if (this.segments[type] === undefined) throw new Error('unexpected type');

@@ -170,4 +214,9 @@

/** @override */
shaka.test.FakeMediaSourceEngine.prototype.bufferedAheadOf = function(
/**
* @param {string} type
* @param {number} start
* @return {number}
* @private
*/
shaka.test.FakeMediaSourceEngine.prototype.bufferedAheadOfImpl_ = function(
type, start) {

@@ -198,4 +247,10 @@ if (this.segments[type] === undefined) throw new Error('unexpected type');

/** @override */
shaka.test.FakeMediaSourceEngine.prototype.appendBuffer = function(
/**
* @param {string} type
* @param {!ArrayBuffer} data
* @param {?number} startTime
* @param {?number} endTime
* @return {!Promise}
*/
shaka.test.FakeMediaSourceEngine.prototype.appendBufferImpl = function(
type, data, startTime, endTime) {

@@ -263,4 +318,10 @@ if (this.segments[type] === undefined) throw new Error('unexpected type');

/** @override */
shaka.test.FakeMediaSourceEngine.prototype.remove = function(type, start, end) {
/**
* @param {string} type
* @param {number} start
* @param {number} end
* @return {!Promise}
*/
shaka.test.FakeMediaSourceEngine.prototype.removeImpl =
function(type, start, end) {
if (this.segments[type] === undefined) throw new Error('unexpected type');

@@ -288,4 +349,8 @@

/** @override */
shaka.test.FakeMediaSourceEngine.prototype.clear = function(type) {
/**
* @param {string} type
* @return {!Promise}
* @private
*/
shaka.test.FakeMediaSourceEngine.prototype.clearImpl_ = function(type) {
if (this.segments[type] === undefined) throw new Error('unexpected type');

@@ -303,3 +368,3 @@

// Cast to the ContentType enum for compatibility.
this.clear(
this.clearImpl_(
/**@type {shaka.util.ManifestParserUtils.ContentType} */('trickvideo'));

@@ -312,10 +377,10 @@ }

/** @override */
shaka.test.FakeMediaSourceEngine.prototype.flush = function(type) {
return Promise.resolve();
};
/** @override */
shaka.test.FakeMediaSourceEngine.prototype.setStreamProperties = function(
/**
* @param {string} type
* @param {number} offset
* @param {number} appendWindowEnd
* @return {!Promise}
* @private
*/
shaka.test.FakeMediaSourceEngine.prototype.setStreamPropertiesImpl_ = function(
type, offset, appendWindowEnd) {

@@ -330,15 +395,8 @@ if (this.segments[type] === undefined) throw new Error('unexpected type');

/**
* @param {string=} opt_reason
* @param {number} duration
* @return {!Promise}
* @override
* TODO: explicit "param" and "return" are needed with current Closure
* compiler, remove them once the Closure compiler is upgraded.
* @private
*/
shaka.test.FakeMediaSourceEngine.prototype.endOfStream = function(opt_reason) {
return Promise.resolve();
};
/** @override */
shaka.test.FakeMediaSourceEngine.prototype.setDuration = function(duration) {
shaka.test.FakeMediaSourceEngine.prototype.setDurationImpl_ = function(
duration) {
this.duration_ = duration;

@@ -349,4 +407,7 @@ return Promise.resolve();

/** @override */
shaka.test.FakeMediaSourceEngine.prototype.getDuration = function() {
/**
* @return {number}
* @private
*/
shaka.test.FakeMediaSourceEngine.prototype.getDurationImpl_ = function() {
return this.duration_;

@@ -377,2 +438,1 @@ };

};

@@ -54,11 +54,19 @@ /**

/** @type {!jasmine.Spy} */
this.request =
jasmine.createSpy('request').and.callFake(this.requestImpl_.bind(this));
/** @type {!jasmine.Spy} */
this.registerResponseFilter =
jasmine.createSpy('registerResponseFilter')
.and.callFake(this.registerResponseFilterImpl_.bind(this));
/** @type {!jasmine.Spy} */
this.unregisterResponseFilter =
jasmine.createSpy('unregisterResponseFilter')
.and.callFake(this.unregisterResponseFilterImpl_.bind(this));
// The prototype has already been applied; create spies for the
// methods but still call it by default.
spyOn(this, 'destroy').and.callThrough();
spyOn(this, 'request').and.callThrough();
spyOn(this, 'registerResponseFilter').and.callThrough();
spyOn(this, 'unregisterResponseFilter').and.callThrough();
};

@@ -109,4 +117,10 @@

/** @override */
shaka.test.FakeNetworkingEngine.prototype.request = function(type, request) {
/**
* @param {shaka.net.NetworkingEngine.RequestType} type
* @param {shakaExtern.Request} request
* @return {!Promise.<shakaExtern.Response>}
* @private
*/
shaka.test.FakeNetworkingEngine.prototype.requestImpl_ = function(
type, request) {
expect(request).toBeTruthy();

@@ -136,4 +150,7 @@ expect(request.uris.length).toBe(1);

/** @override */
shaka.test.FakeNetworkingEngine.prototype.registerResponseFilter =
/**
* @param {shakaExtern.RequestFilter} filter
* @private
*/
shaka.test.FakeNetworkingEngine.prototype.registerResponseFilterImpl_ =
function(filter) {

@@ -144,4 +161,7 @@ expect(filter).toEqual(jasmine.any(Function));

/** @override */
shaka.test.FakeNetworkingEngine.prototype.unregisterResponseFilter =
/**
* @param {shakaExtern.RequestFilter} filter
* @private
*/
shaka.test.FakeNetworkingEngine.prototype.unregisterResponseFilterImpl_ =
function(filter) {

@@ -148,0 +168,0 @@ expect(filter).toEqual(jasmine.any(Function));

@@ -33,3 +33,4 @@ /**

shaka.test.ManifestGenerator = function(opt_shaka) {
this.shaka_ = opt_shaka || window.shaka;
/** @private {?} */
this.shaka_ = opt_shaka || window['shaka'];

@@ -454,8 +455,14 @@ var timeline = new this.shaka_.media.PresentationTimeline(0, 0);

var create = jasmine.createSpy('createSegmentIndex').and.callFake(function() {
return Promise.resolve();
});
var find = jasmine.createSpy('findSegmentPosition').and.returnValue(null);
var get = jasmine.createSpy('getSegmentReference').and.returnValue(null);
/** @type {shakaExtern.Stream} */
var stream = {
id: id,
createSegmentIndex: jasmine.createSpy('createSegmentIndex'),
findSegmentPosition: jasmine.createSpy('findSegmentPosition'),
getSegmentReference: jasmine.createSpy('getSegmentReference'),
createSegmentIndex: shaka.test.Util.spyFunc(create),
findSegmentPosition: shaka.test.Util.spyFunc(find),
getSegmentReference: shaka.test.Util.spyFunc(get),
initSegmentReference: null,

@@ -478,9 +485,5 @@ presentationTimeOffset: 0,

containsEmsgBoxes: false,
roles: []
roles: [],
channelsCount: null
};
stream.createSegmentIndex.and.callFake(
function() { return Promise.resolve(); });
stream.findSegmentPosition.and.returnValue(null);
stream.getSegmentReference.and.returnValue(null);
return stream;

@@ -811,2 +814,14 @@ };

};
/**
* Sets the count of the channels of the current stream.
* @param {number} count
* @return {!shaka.test.ManifestGenerator}
*/
shaka.test.ManifestGenerator.prototype.channelsCount = function(count) {
var stream = this.currentStream_();
stream.channelsCount = count;
return this;
};
// }}}

@@ -25,6 +25,16 @@ /**

goog.provide('shaka.test.FakeStreamingEngine');
goog.provide('shaka.test.FakeTextDisplayer');
goog.provide('shaka.test.FakeTextTrack');
goog.provide('shaka.test.FakeVideo');
/**
* @fileoverview Defines simple mocks for library types.
* @suppress {checkTypes} Suppress errors about missmatches between the
* definition and the interface. This allows us to have the members be
* |jasmine.Spy|. BE CAREFUL IN THIS FILE.
*/
/**

@@ -35,97 +45,73 @@ * A fake AbrManager.

* @struct
* @implements {shakaExtern.AbrManager}
* @extends {shaka.abr.SimpleAbrManager}
* @return {!Object}
*/
shaka.test.FakeAbrManager = function() {
/** @type {number} */
this.chooseIndex = 0;
var ret = jasmine.createSpyObj('FakeAbrManager', [
'stop', 'init', 'enable', 'disable', 'segmentDownloaded',
'getBandwidthEstimate', 'chooseVariant', 'setVariants', 'configure'
]);
/** @type {!Array.<shakaExtern.Variant>} */
this.variants = [];
var variants = [];
/** @type {!Array.<shakaExtern.Stream>} */
this.textStreams = [];
ret.chooseIndex = 0;
spyOn(this, 'chooseStreams').and.callThrough();
spyOn(this, 'stop');
spyOn(this, 'init');
spyOn(this, 'enable');
spyOn(this, 'disable');
spyOn(this, 'segmentDownloaded');
spyOn(this, 'getBandwidthEstimate');
spyOn(this, 'setDefaultEstimate');
spyOn(this, 'setRestrictions');
spyOn(this, 'setTextStreams').and.callThrough();
spyOn(this, 'setVariants').and.callThrough();
ret.init.and.callFake(function(switchCallback) {
ret.switchCallback = switchCallback;
});
ret.setVariants.and.callFake(function(arg) { variants = arg; });
ret.chooseVariant.and.callFake(function() {
return variants[ret.chooseIndex];
});
return ret;
};
/** @override */
shaka.test.FakeAbrManager.prototype.stop = function() {};
/** @type {number} */
shaka.test.FakeAbrManager.prototype.chooseIndex;
/** @override */
shaka.test.FakeAbrManager.prototype.init = function() {};
/** @type {shakaExtern.AbrManager.SwitchCallback} */
shaka.test.FakeAbrManager.prototype.switchCallback;
/** @override */
shaka.test.FakeAbrManager.prototype.enable = function() {};
/** @type {!jasmine.Spy} */
shaka.test.FakeAbrManager.prototype.stop;
/** @override */
shaka.test.FakeAbrManager.prototype.disable = function() {};
/** @type {!jasmine.Spy} */
shaka.test.FakeAbrManager.prototype.init;
/** @override */
shaka.test.FakeAbrManager.prototype.segmentDownloaded = function() {};
/** @type {!jasmine.Spy} */
shaka.test.FakeAbrManager.prototype.enable;
/** @override */
shaka.test.FakeAbrManager.prototype.getBandwidthEstimate = function() {};
/** @type {!jasmine.Spy} */
shaka.test.FakeAbrManager.prototype.disable;
/** @override */
shaka.test.FakeAbrManager.prototype.setDefaultEstimate = function() {};
/** @type {!jasmine.Spy} */
shaka.test.FakeAbrManager.prototype.segmentDownloaded;
/** @override */
shaka.test.FakeAbrManager.prototype.setRestrictions = function() {};
/** @type {!jasmine.Spy} */
shaka.test.FakeAbrManager.prototype.getBandwidthEstimate;
/** @override */
shaka.test.FakeAbrManager.prototype.chooseStreams = function(
mediaTypesToUpdate) {
var ContentType = shaka.util.ManifestParserUtils.ContentType;
var ret = {};
var variant = this.variants[this.chooseIndex];
/** @type {!jasmine.Spy} */
shaka.test.FakeAbrManager.prototype.chooseVariant;
var textStream = null;
if (this.textStreams.length > this.chooseIndex)
textStream = this.textStreams[this.chooseIndex];
if (mediaTypesToUpdate.indexOf(ContentType.AUDIO) > -1 ||
mediaTypesToUpdate.indexOf(ContentType.VIDEO) > -1) {
if (variant.audio) ret[ContentType.AUDIO] = variant.audio;
if (variant.video) ret[ContentType.VIDEO] = variant.video;
}
/** @type {!jasmine.Spy} */
shaka.test.FakeAbrManager.prototype.setVariants;
if (mediaTypesToUpdate.indexOf(ContentType.TEXT) > -1 && textStream)
ret[ContentType.TEXT] = textStream;
return ret;
};
/** @type {!jasmine.Spy} */
shaka.test.FakeAbrManager.prototype.configure;
/** @override */
shaka.test.FakeAbrManager.prototype.setVariants = function(variants) {
this.variants = variants;
};
/** @override */
shaka.test.FakeAbrManager.prototype.setTextStreams = function(streams) {
this.textStreams = streams;
};
/**

@@ -200,6 +186,7 @@ * A fake DrmEngine.

* @extends {shaka.media.StreamingEngine}
* @param {function():shaka.media.StreamingEngine.ChosenStreams} onChooseStreams
* @param {function()} onCanSwitch
* @return {!Object}
*/
shaka.test.FakeStreamingEngine = function() {
var ContentType = shaka.util.ManifestParserUtils.ContentType;
shaka.test.FakeStreamingEngine = function(onChooseStreams, onCanSwitch) {
var resolve = Promise.resolve.bind(Promise);

@@ -210,3 +197,4 @@ var activeStreams = {};

'destroy', 'configure', 'init', 'getCurrentPeriod', 'getActivePeriod',
'getActiveStreams', 'notifyNewTextStream', 'switch', 'seeked'
'getActiveStreams', 'notifyNewTextStream', 'switchVariant',
'switchTextStream', 'seeked'
]);

@@ -219,16 +207,23 @@ ret.destroy.and.callFake(resolve);

ret.init.and.callFake(function() {
var period = ret.getCurrentPeriod();
var variant = period.variants[0];
var chosen = onChooseStreams();
return Promise.resolve().then(function() {
if (chosen.variant && chosen.variant.video)
activeStreams['video'] = chosen.variant.video;
if (chosen.variant && chosen.variant.audio)
activeStreams['audio'] = chosen.variant.audio;
if (chosen.text)
activeStreams['text'] = chosen.text;
});
});
ret.switchVariant.and.callFake(function(variant) {
if (variant.video)
activeStreams['video'] = variant.video;
if (variant.audio)
activeStreams[ContentType.AUDIO] = variant.audio;
if (variant.video)
activeStreams[ContentType.VIDEO] = variant.video;
var text = period.textStreams[0];
if (text)
activeStreams[ContentType.TEXT] = text;
return Promise.resolve();
activeStreams['audio'] = variant.audio;
});
ret.switch.and.callFake(function(type, stream) {
activeStreams[type] = stream;
ret.switchTextStream.and.callFake(function(textStream) {
activeStreams['text'] = textStream;
});
ret.onChooseStreams = onChooseStreams;
ret.onCanSwitch = onCanSwitch;
return ret;

@@ -242,3 +237,23 @@ };

/** @type {jasmine.Spy} */
shaka.test.FakeStreamingEngine.prototype.switchVariant;
/** @type {jasmine.Spy} */
shaka.test.FakeStreamingEngine.prototype.switchTextStream;
/** @type {jasmine.Spy} */
shaka.test.FakeStreamingEngine.prototype.getCurrentPeriod;
/** @type {function()} */
shaka.test.FakeStreamingEngine.prototype.onChooseStreams;
/** @type {function()} */
shaka.test.FakeStreamingEngine.prototype.onCanSwitch;
/**

@@ -251,39 +266,44 @@ * Creates a fake manifest parser.

* @implements {shakaExtern.ManifestParser}
* @return {!Object}
*/
shaka.test.FakeManifestParser = function(manifest) {
/** @private {shakaExtern.Manifest} */
this.manifest_ = manifest;
spyOn(this, 'start').and.callThrough();
spyOn(this, 'stop').and.callThrough();
spyOn(this, 'configure');
spyOn(this, 'update');
var ret = jasmine.createSpyObj('FakeManifestParser', [
'start', 'stop', 'configure', 'update', 'onExpirationUpdated'
]);
ret.start.and.callFake(function(manifestUri, playerInterface) {
ret.playerInterface = playerInterface;
return Promise.resolve().then(function() {
return manifest;
});
});
ret.stop.and.returnValue(Promise.resolve());
return ret;
};
/** @override */
shaka.test.FakeManifestParser.prototype.start = function() {
return Promise.resolve(this.manifest_);
};
/** @type {!jasmine.Spy} */
shaka.test.FakeManifestParser.prototype.start;
/** @override */
shaka.test.FakeManifestParser.prototype.stop = function() {
return Promise.resolve();
};
/** @type {!jasmine.Spy} */
shaka.test.FakeManifestParser.prototype.stop;
/** @override */
shaka.test.FakeManifestParser.prototype.update = function() {};
/** @type {!jasmine.Spy} */
shaka.test.FakeManifestParser.prototype.update;
/** @override */
shaka.test.FakeManifestParser.prototype.onExpirationUpdated = function() {};
/** @type {!jasmine.Spy} */
shaka.test.FakeManifestParser.prototype.onExpirationUpdated;
/** @override */
shaka.test.FakeManifestParser.prototype.configure = function() {};
/** @type {!jasmine.Spy} */
shaka.test.FakeManifestParser.prototype.configure;
/** @type {shaka.media.StreamingEngine.PlayerInterface} */
shaka.test.FakeManifestParser.prototype.playerInterface;
/**

@@ -326,4 +346,3 @@ * Creates a fake video element.

video.addTextTrack.and.callFake(function(kind, id) {
// TODO: mock TextTrack, if/when Player starts directly accessing it.
var track = {};
var track = new shaka.test.FakeTextTrack();
video.textTracks.push(track);

@@ -344,2 +363,10 @@ return track;

/** @type {!jasmine.Spy} */
shaka.test.FakeVideo.prototype.play;
/** @type {!jasmine.Spy} */
shaka.test.FakeVideo.prototype.setMediaKeys;
/**

@@ -379,3 +406,3 @@ * Creates a fake buffered ranges object.

getSafeStart.and.callFake(function(delay) {
return getStart() + delay;
return shaka.test.Util.invokeSpy(getStart) + delay;
});

@@ -481,3 +508,31 @@

/** @type {!jasmine.Spy} */
shaka.test.FakePlayhead.prototype.destroy;
/** @type {!jasmine.Spy} */
shaka.test.FakePlayhead.prototype.setRebufferingGoal;
/** @type {!jasmine.Spy} */
shaka.test.FakePlayhead.prototype.setStartTime;
/** @type {!jasmine.Spy} */
shaka.test.FakePlayhead.prototype.getTime;
/** @type {!jasmine.Spy} */
shaka.test.FakePlayhead.prototype.setBuffering;
/** @type {!jasmine.Spy} */
shaka.test.FakePlayhead.prototype.getPlaybackRate;
/** @type {!jasmine.Spy} */
shaka.test.FakePlayhead.prototype.setPlaybackRate;
/**

@@ -511,1 +566,80 @@ * Creates a fake PlayheadObserver object.

shaka.test.FakePlayheadObserver.prototype.addTimelineRegion;
/**
* Creates a text track.
*
* @constructor
* @struct
* @extends {TextTrack}
* @return {!Object}
*/
shaka.test.FakeTextTrack = function() {
var track = {
addCue: jasmine.createSpy('addCue'),
removeCue: jasmine.createSpy('removeCue'),
cues: []
};
track.addCue.and.callFake(function(cue) {
track.cues.push(cue);
});
track.removeCue.and.callFake(function(cue) {
var idx = track.cues.indexOf(cue);
expect(idx).not.toBeLessThan(0);
track.cues.splice(idx, 1);
});
return track;
};
/** @type {!jasmine.Spy} */
shaka.test.FakeTextTrack.prototype.addCue;
/** @type {!jasmine.Spy} */
shaka.test.FakeTextTrack.prototype.removeCue;
/**
* Creates a text track.
*
* @constructor
* @struct
* @extends {shaka.text.SimpleTextDisplayer}
* @return {!Object}
*/
shaka.test.FakeTextDisplayer = function() {
var displayer = {
append: jasmine.createSpy('append'),
remove: jasmine.createSpy('remove').and.returnValue(true),
destroy:
jasmine.createSpy('destroy').and.returnValue(Promise.resolve()),
isTextVisible: jasmine.createSpy('isTextVisible'),
setTextVisibility: jasmine.createSpy('setTextVisibility'),
textVisible: false
};
displayer.isTextVisible.and.callFake(function() {
return displayer.textVisible;
});
displayer.setTextVisibility.and.callFake(function(on) {
displayer.textVisible = on;
});
return displayer;
};
/** @type {!jasmine.Spy} */
shaka.test.FakeTextDisplayer.prototype.remove;
/** @type {!jasmine.Spy} */
shaka.test.FakeTextDisplayer.prototype.append;
/** @type {!jasmine.Spy} */
shaka.test.FakeTextDisplayer.prototype.destroy;

@@ -34,6 +34,6 @@ /**

*
* @param {function(string, number): ArrayBuffer} getInitSegment Init segment
* @param {function(string, number): BufferSource} getInitSegment Init segment
* generator: takes a content type and a Period number; returns an init
* segment.
* @param {function(string, number, number): ArrayBuffer} getSegment Media
* @param {function(string, number, number): BufferSource} getSegment Media
* segment generator: takes a content type, a Period number, and a segment

@@ -153,3 +153,3 @@ * position; returns a media segment.

timeline.getSeekRangeEnd.and.callFake(function() {
return timeline.getSegmentAvailabilityEnd();
return shaka.test.Util.invokeSpy(timeline.getSegmentAvailabilityEnd);
});

@@ -188,3 +188,3 @@

* type of segment.
* @return {!Object}
* @return {shakaExtern.Manifest}
*/

@@ -263,3 +263,3 @@ shaka.test.StreamingEngineUtil.createManifest = function(

return manifest;
return /** @type {shakaExtern.Manifest} */ (manifest);
};

@@ -266,0 +266,0 @@

@@ -257,2 +257,5 @@ /**

shaka.test.TestScheme.createManifests = function(shaka, suffix) {
/** @type {?} */
var windowShaka = window['shaka'];
/**

@@ -263,3 +266,3 @@ * @param {Object} metadata

function createStreamGenerator(metadata) {
return new window.shaka.test.DashVodStreamGenerator(
return new windowShaka.test.DashVodStreamGenerator(
metadata.initSegmentUri, metadata.mvhdOffset, metadata.segmentUri,

@@ -299,6 +302,6 @@ metadata.tfdtOffset, metadata.segmentDuration,

// library.
var DATA = window.shaka.test.TestScheme.DATA;
var GENERATORS = window.shaka.test.TestScheme.GENERATORS;
var MANIFESTS = window.shaka.test.TestScheme.MANIFESTS;
var ContentType = window.shaka.util.ManifestParserUtils.ContentType;
var DATA = windowShaka.test.TestScheme.DATA;
var GENERATORS = windowShaka.test.TestScheme.GENERATORS;
var MANIFESTS = windowShaka.test.TestScheme.MANIFESTS;
var ContentType = windowShaka.util.ManifestParserUtils.ContentType;

@@ -314,3 +317,3 @@ for (var name in DATA) {

var gen = new window.shaka.test.ManifestGenerator(shaka)
var gen = new windowShaka.test.ManifestGenerator(shaka)
.setPresentationDuration(data.duration)

@@ -345,3 +348,3 @@ .addPeriod(0)

var num_periods = 10;
var gen = new window.shaka.test.ManifestGenerator(shaka)
var gen = new windowShaka.test.ManifestGenerator(shaka)
.setPresentationDuration(period_duration * num_periods);

@@ -348,0 +351,0 @@

@@ -18,6 +18,30 @@ /**

goog.provide('shaka.test.StatusPromise');
goog.provide('shaka.test.Util');
/**
* @param {!Promise} p
* @constructor
* @struct
* @extends {Promise}
* @return {!Promise}
*/
shaka.test.StatusPromise = function(p) {
p.status = 'pending';
p.then(function() {
p.status = 'resolved';
}, function() {
p.status = 'rejected';
});
return p;
};
/** @type {string} */
shaka.test.StatusPromise.prototype.status;
/**
* Fakes an event loop. Each tick processes some number of instantaneous

@@ -52,10 +76,6 @@ * operations and advances the simulated clock forward by 1 second. Calls

* @param {!Promise} promise
* @return {!shaka.test.StatusPromise}
*/
shaka.test.Util.capturePromiseStatus = function(promise) {
promise.status = 'pending';
promise.then(function() {
promise.status = 'resolved';
}, function() {
promise.status = 'rejected';
});
return new shaka.test.StatusPromise(promise);
};

@@ -101,2 +121,90 @@

/**
* Registers a custom matcher for Element objects, called 'toEqualElement'.
* @private
*/
shaka.test.Util.registerElementMatcher_ = function() {
jasmine.addMatchers({
toEqualElement: function(util, customEqualityTesters) {
return {
compare: shaka.test.Util.expectToEqualElementCompare_
};
}
});
};
/**
* @param {?} actual
* @param {!Element} expected
* @return {!Object} result
* @private
*/
shaka.test.Util.expectToEqualElementCompare_ = function(actual, expected) {
var diff = shaka.test.Util.expectToEqualElementRecursive_(actual, expected);
var result = {};
result.pass = diff == null;
if (result.pass) {
result.message = 'Expected ' + actual.innerHTML + ' not to match ';
result.message += expected.innerHTML + '.';
} else {
result.message = 'Expected ' + actual.innerHTML + ' to match ';
result.message += expected.innerHTML + '. ' + diff;
}
return result;
};
/**
* @param {?} actual
* @param {!Node} expected
* @return {?string} failureReason
* @private
*/
shaka.test.Util.expectToEqualElementRecursive_ = function(actual, expected) {
var prospectiveDiff = 'The difference was in ' +
(actual.outerHTML || actual.textContent) + ' vs ' +
(expected['outerHTML'] || expected.textContent) + ': ';
if (!(actual instanceof Element) && !(expected instanceof Element)) {
// Compare them as nodes.
if (actual.textContent != expected.textContent)
return prospectiveDiff + 'Nodes are different.';
} else if (!(actual instanceof Element) || !(expected instanceof Element)) {
return prospectiveDiff + 'One is element, one isn\'t.';
} else {
// Compare them as elements.
if (actual.tagName != expected.tagName)
return prospectiveDiff + 'Different tagName.';
if (actual.attributes.length != expected.attributes.length)
return prospectiveDiff + 'Different attribute list length.';
for (var i = 0; i < actual.attributes.length; i++) {
var aAttrib = actual.attributes[i].nodeName;
var aAttribVal = actual.getAttribute(aAttrib);
var eAttrib = expected.attributes[i].nodeName;
var eAttribVal = expected.getAttribute(eAttrib);
if (aAttrib != eAttrib || aAttribVal != eAttribVal) {
var diffNote =
aAttrib + '=' + aAttribVal + ' vs ' + eAttrib + '=' + eAttribVal;
return prospectiveDiff + 'Attribute #' + i +
' was different (' + diffNote + ').';
}
}
if (actual.childNodes.length != expected.childNodes.length)
return prospectiveDiff + 'Different child node list length.';
for (var i = 0; i < actual.childNodes.length; i++) {
var aNode = actual.childNodes[i];
var eNode = expected.childNodes[i];
var diff = shaka.test.Util.expectToEqualElementRecursive_(aNode, eNode);
if (diff)
return diff;
}
}
return null;
};
/**
* Custom comparer for segment references.

@@ -181,4 +289,24 @@ * @param {*} first

/**
* @param {!jasmine.Spy} spy
* @return {!Function}
*/
shaka.test.Util.spyFunc = function(spy) {
return spy;
};
/**
* @param {!jasmine.Spy} spy
* @param {...*} var_args
* @return {*}
*/
shaka.test.Util.invokeSpy = function(spy, var_args) {
return spy.apply(null, Array.prototype.slice.call(arguments, 1));
};
beforeEach(function() {
jasmine.addCustomEqualityTester(shaka.test.Util.compareReferences);
shaka.test.Util.registerElementMatcher_();
});

@@ -19,2 +19,3 @@ /**

describe('CancelableChain', function() {
/** @type {!shaka.util.CancelableChain} */
var chain;

@@ -170,3 +171,3 @@

finalComplete = true;
shaka.test.Util.expectToEqualError(cannedError, err);
shaka.test.Util.expectToEqualError(err, cannedError);
});

@@ -173,0 +174,0 @@

@@ -19,4 +19,8 @@ /**

describe('DataViewReader', function() {
/** @const */
var Code = shaka.util.Error.Code;
// |data| as interpreted as a 64 bit integer must not be larger than 2^53-1.
// decimal digits.
/** @const */
var data = new Uint8Array([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]);

@@ -26,15 +30,14 @@ // |data2| is small enough in little-endian to be read as a 64-bit number,

// return negative values.
/** @const */
var data2 = new Uint8Array([0xde, 0xad, 0xbe, 0xef, 0xff, 0xff, 0x01, 0x00]);
/** @type {!shaka.util.DataViewReader} */
var bigEndianReader;
/** @type {!shaka.util.DataViewReader} */
var littleEndianReader;
/** @type {!shaka.util.DataViewReader} */
var bigEndianReader2;
/** @type {!shaka.util.DataViewReader} */
var littleEndianReader2;
var Code;
beforeAll(function() {
Code = shaka.util.Error.Code;
});
beforeEach(function() {

@@ -41,0 +44,0 @@ bigEndianReader = new shaka.util.DataViewReader(

@@ -19,6 +19,14 @@ /**

describe('EventManager', function() {
/** @const */
var Util = shaka.test.Util;
/** @type {!shaka.util.EventManager} */
var eventManager;
/** @type {!Event} */
var event1;
/** @type {!Event} */
var event2;
/** @type {!EventTarget} */
var target1;
/** @type {!EventTarget} */
var target2;

@@ -32,5 +40,5 @@

// new Event() is current, but document.createEvent() works back to IE11.
event1 = document.createEvent('Event');
event1 = /** @type {!Event} */ (document.createEvent('Event'));
event1.initEvent('eventtype1', false, false);
event2 = document.createEvent('Event');
event2 = /** @type {!Event} */ (document.createEvent('Event'));
event2.initEvent('eventtype2', false, false);

@@ -46,3 +54,3 @@ });

eventManager.listen(target1, 'eventtype1', listener);
eventManager.listen(target1, 'eventtype1', Util.spyFunc(listener));
target1.dispatchEvent(event1);

@@ -57,4 +65,4 @@

eventManager.listen(target1, 'eventtype1', listener1);
eventManager.listen(target2, 'eventtype1', listener2);
eventManager.listen(target1, 'eventtype1', Util.spyFunc(listener1));
eventManager.listen(target2, 'eventtype1', Util.spyFunc(listener2));

@@ -72,4 +80,4 @@ target1.dispatchEvent(event1);

eventManager.listen(target1, 'eventtype1', listener1);
eventManager.listen(target1, 'eventtype2', listener2);
eventManager.listen(target1, 'eventtype1', Util.spyFunc(listener1));
eventManager.listen(target1, 'eventtype2', Util.spyFunc(listener2));

@@ -87,4 +95,4 @@ target1.dispatchEvent(event1);

eventManager.listen(target1, 'eventtype1', listener1);
eventManager.listen(target2, 'eventtype2', listener2);
eventManager.listen(target1, 'eventtype1', Util.spyFunc(listener1));
eventManager.listen(target2, 'eventtype2', Util.spyFunc(listener2));

@@ -102,4 +110,4 @@ target1.dispatchEvent(event1);

eventManager.listen(target1, 'eventtype1', listener1);
eventManager.listen(target1, 'eventtype1', listener2);
eventManager.listen(target1, 'eventtype1', Util.spyFunc(listener1));
eventManager.listen(target1, 'eventtype1', Util.spyFunc(listener2));

@@ -115,3 +123,3 @@ target1.dispatchEvent(event1);

eventManager.listen(target1, 'eventtype1', listener);
eventManager.listen(target1, 'eventtype1', Util.spyFunc(listener));
eventManager.unlisten(target1, 'eventtype1');

@@ -128,4 +136,4 @@

eventManager.listen(target1, 'eventtype1', listener1);
eventManager.listen(target2, 'eventtype1', listener2);
eventManager.listen(target1, 'eventtype1', Util.spyFunc(listener1));
eventManager.listen(target2, 'eventtype1', Util.spyFunc(listener2));
eventManager.unlisten(target2, 'eventtype1');

@@ -142,6 +150,6 @@

eventManager.listen(target1, 'eventtype1', listener1);
eventManager.listen(target1, 'eventtype2', listener2);
eventManager.listen(target1, 'eventtype1', Util.spyFunc(listener1));
eventManager.listen(target1, 'eventtype2', Util.spyFunc(listener2));
eventManager.removeAll(target1);
eventManager.removeAll();

@@ -159,6 +167,6 @@ target1.dispatchEvent(event1);

eventManager.listen(target1, 'eventtype1', listener1);
eventManager.listen(target1, 'eventtype1', listener2);
eventManager.listen(target1, 'eventtype1', Util.spyFunc(listener1));
eventManager.listen(target1, 'eventtype1', Util.spyFunc(listener2));
eventManager.removeAll(target1);
eventManager.removeAll();

@@ -165,0 +173,0 @@ target1.dispatchEvent(event1);

@@ -19,10 +19,15 @@ /**

describe('FakeEventTarget', function() {
/** @const */
var Util = shaka.test.Util;
/** @const */
var originalLogError = shaka.log.error;
/** @type {!shaka.util.FakeEventTarget} */
var target;
/** @type {!jasmine.Spy} */
var logErrorSpy;
var originalLogError;
beforeAll(function() {
originalLogError = shaka.log.error;
logErrorSpy = jasmine.createSpy('shaka.log.error');
shaka.log.error = logErrorSpy;
shaka.log.error = Util.spyFunc(logErrorSpy);
});

@@ -54,4 +59,4 @@

target.addEventListener('event', listener1);
target.addEventListener('event', listener2);
target.addEventListener('event', Util.spyFunc(listener1));
target.addEventListener('event', Util.spyFunc(listener2));

@@ -71,4 +76,4 @@ target.dispatchEvent(new shaka.util.FakeEvent('event'));

target.addEventListener('event', listener1);
target.addEventListener('event', listener2);
target.addEventListener('event', Util.spyFunc(listener1));
target.addEventListener('event', Util.spyFunc(listener2));

@@ -92,4 +97,4 @@ listener1.and.callFake(function(event) {

target.addEventListener('event', listener1);
target.addEventListener('event', listener2);
target.addEventListener('event', Util.spyFunc(listener1));
target.addEventListener('event', Util.spyFunc(listener2));

@@ -113,4 +118,4 @@ listener1.and.throwError('whoops');

target.addEventListener('event', listener1);
target.addEventListener('event', listener2);
target.addEventListener('event', Util.spyFunc(listener1));
target.addEventListener('event', Util.spyFunc(listener2));

@@ -120,3 +125,3 @@ var target2 = new shaka.util.FakeEventTarget();

target2.addEventListener('event', target2Listener);
target2.addEventListener('event', Util.spyFunc(target2Listener));

@@ -123,0 +128,0 @@ listener1.and.callFake(function(event) {

@@ -19,2 +19,4 @@ /**

describe('Mp4Parser', function() {
var Util = shaka.test.Util;
var boxData;

@@ -111,3 +113,3 @@ var fullBoxData;

new shaka.util.Mp4Parser()
.box('b001', callback).parse(boxData);
.box('b001', Util.spyFunc(callback)).parse(boxData);

@@ -126,3 +128,3 @@ expect(callback).toHaveBeenCalled();

new shaka.util.Mp4Parser()
.fullBox('b001', callback).parse(fullBoxData);
.fullBox('b001', Util.spyFunc(callback)).parse(fullBoxData);

@@ -146,4 +148,4 @@ expect(callback).toHaveBeenCalled();

new shaka.util.Mp4Parser()
.box('b003', parentBox)
.box('b031', childBox).parse(boxWithChildData);
.box('b003', Util.spyFunc(parentBox))
.box('b031', Util.spyFunc(childBox)).parse(boxWithChildData);

@@ -178,5 +180,5 @@ expect(parentBox).toHaveBeenCalled();

new shaka.util.Mp4Parser()
.box('b003', parentBox)
.box('b032', childBox1)
.box('b033', childBox2).parse(boxWithSampleDescription);
.box('b003', Util.spyFunc(parentBox))
.box('b032', Util.spyFunc(childBox1))
.box('b033', Util.spyFunc(childBox2)).parse(boxWithSampleDescription);

@@ -196,5 +198,5 @@ expect(parentBox).toHaveBeenCalledTimes(1);

new shaka.util.Mp4Parser()
.box('b001', box1)
.box('b002', box2)
.box('b003', box3).parse(multipleSingleLevelBoxes);
.box('b001', Util.spyFunc(box1))
.box('b002', Util.spyFunc(box2))
.box('b003', Util.spyFunc(box3)).parse(multipleSingleLevelBoxes);

@@ -214,4 +216,4 @@ expect(box1).toHaveBeenCalled();

new shaka.util.Mp4Parser()
.box('b001', box1)
.box('b003', box3).parse(multipleSingleLevelBoxes);
.box('b001', Util.spyFunc(box1))
.box('b003', Util.spyFunc(box3)).parse(multipleSingleLevelBoxes);

@@ -230,5 +232,5 @@ expect(box1).toHaveBeenCalled();

new shaka.util.Mp4Parser()
.box('b010', box1)
.box('b021', box2Child)
.box('b030', box3).parse(twoLevelBoxStructure);
.box('b010', Util.spyFunc(box1))
.box('b021', Util.spyFunc(box2Child))
.box('b030', Util.spyFunc(box3)).parse(twoLevelBoxStructure);

@@ -235,0 +237,0 @@ expect(box1).toHaveBeenCalled();

@@ -473,3 +473,3 @@ /**

describe('filterPeriod', function() {
describe('filterNewPeriod', function() {
var fakeDrmEngine;

@@ -491,3 +491,3 @@

var activeStreams = {};
shaka.util.StreamUtils.filterPeriod(
shaka.util.StreamUtils.filterNewPeriod(
fakeDrmEngine, activeStreams, manifest.periods[0]);

@@ -494,0 +494,0 @@

@@ -550,3 +550,3 @@ // Copyright 2006 The Closure Library Authors. All Rights Reserved.

for (var i = scripts.length - 1; i >= 0; --i) {
var src = scripts[i].src;
var src = (/** @type {!HTMLScriptElement} */ (scripts[i])).src;
var qmark = src.lastIndexOf('?');

@@ -553,0 +553,0 @@ var l = qmark == -1 ? src.length : qmark;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc