mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-24 17:35:10 +03:00
5637962073
Also now uses the same ContentDatabaseReader for offline segment requests. Change-Id: I9cebc90b46ca9067467a8a343d48b264abc7cf26
670 lines
22 KiB
JavaScript
670 lines
22 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2015 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
goog.provide('shaka.player.OfflineVideoSource');
|
|
|
|
goog.require('goog.Uri');
|
|
goog.require('shaka.asserts');
|
|
goog.require('shaka.dash.MpdProcessor');
|
|
goog.require('shaka.dash.MpdRequest');
|
|
goog.require('shaka.dash.mpd');
|
|
goog.require('shaka.features');
|
|
goog.require('shaka.log');
|
|
goog.require('shaka.media.EmeManager');
|
|
goog.require('shaka.media.IAbrManager');
|
|
goog.require('shaka.media.OfflineSegmentIndexSource');
|
|
goog.require('shaka.media.SegmentIndex');
|
|
goog.require('shaka.media.SimpleAbrManager');
|
|
goog.require('shaka.media.StreamInfo');
|
|
goog.require('shaka.player.StreamVideoSource');
|
|
goog.require('shaka.util.ContentDatabaseReader');
|
|
goog.require('shaka.util.ContentDatabaseWriter');
|
|
goog.require('shaka.util.FailoverUri');
|
|
goog.require('shaka.util.IBandwidthEstimator');
|
|
goog.require('shaka.util.TypedBind');
|
|
goog.require('shaka.util.Uint8ArrayUtils');
|
|
|
|
|
|
|
|
/**
|
|
* Creates an OfflineVideoSource.
|
|
* @param {?number} groupId The unique ID of the group of streams
|
|
* in this source.
|
|
* @param {shaka.util.IBandwidthEstimator} estimator
|
|
* @param {shaka.media.IAbrManager} abrManager
|
|
*
|
|
* @struct
|
|
* @constructor
|
|
* @extends {shaka.player.StreamVideoSource}
|
|
* @exportDoc
|
|
*/
|
|
shaka.player.OfflineVideoSource = function(groupId, estimator, abrManager) {
|
|
if (!estimator) {
|
|
// For backward compatibility, provide an instance of the default
|
|
// implementation if none is provided.
|
|
estimator = new shaka.util.EWMABandwidthEstimator();
|
|
}
|
|
if (!abrManager) {
|
|
abrManager = new shaka.media.SimpleAbrManager();
|
|
}
|
|
|
|
shaka.player.StreamVideoSource.call(this, null, estimator, abrManager);
|
|
|
|
/** @private {?number} */
|
|
this.groupId_ = groupId;
|
|
|
|
/** @private {!Array.<string>} */
|
|
this.sessionIds_ = [];
|
|
|
|
/**
|
|
* The timeout, in milliseconds, for downloading and storing offline licenses
|
|
* for encrypted content.
|
|
* @type {number}
|
|
* @expose
|
|
*/
|
|
this.timeoutMs = 30000;
|
|
|
|
/** @private {!Object.<string, *>} */
|
|
this.config_ = {};
|
|
|
|
/** @private {shaka.util.FailoverUri.NetworkCallback} */
|
|
this.networkCallback_ = null;
|
|
|
|
/** @private {shaka.player.DrmInfo.Config} */
|
|
this.overrideConfig_ = null;
|
|
};
|
|
goog.inherits(shaka.player.OfflineVideoSource, shaka.player.StreamVideoSource);
|
|
if (shaka.features.Offline) {
|
|
goog.exportSymbol('shaka.player.OfflineVideoSource',
|
|
shaka.player.OfflineVideoSource);
|
|
}
|
|
|
|
|
|
/**
|
|
* A callback to the application to choose the tracks which will be stored
|
|
* offline. Returns a Promise to an array of track IDs. This uses Promises
|
|
* so that the application can, if it chooses, display some dialog to the user
|
|
* to drive the choice of tracks.
|
|
*
|
|
* @typedef {function():!Promise.<!Array.<number>>}
|
|
* @expose
|
|
*/
|
|
shaka.player.OfflineVideoSource.ChooseTracksCallback;
|
|
|
|
|
|
/**
|
|
* Configures the OfflineVideoSource options.
|
|
* Options are set via key-value pairs.
|
|
*
|
|
* The following configuration options are supported:
|
|
* licenseRequestTimeout: number
|
|
* Sets the license request timeout in seconds.
|
|
* mpdRequestTimeout: number
|
|
* Sets the MPD request timeout in seconds.
|
|
* segmentRequestTimeout: number
|
|
* Sets the segment request timeout in seconds.
|
|
*
|
|
* @example
|
|
* offlineVideoSouce.configure({'licenseRequestTimeout': 20});
|
|
*
|
|
* @param {!Object.<string, *>} config A configuration object, which contains
|
|
* the configuration options as key-value pairs. All fields should have
|
|
* already been validated.
|
|
* @override
|
|
*/
|
|
shaka.player.OfflineVideoSource.prototype.configure = function(config) {
|
|
if (config['licenseRequestTimeout'] != null) {
|
|
this.config_['licenseRequestTimeout'] = config['licenseRequestTimeout'];
|
|
}
|
|
if (config['segmentRequestTimeout'] != null) {
|
|
this.config_['segmentRequestTimeout'] = config['segmentRequestTimeout'];
|
|
}
|
|
var baseClassConfigure = shaka.player.StreamVideoSource.prototype.configure;
|
|
baseClassConfigure.call(this, config);
|
|
};
|
|
|
|
|
|
/**
|
|
* Retrieves an array of all stored group IDs.
|
|
* @return {!Promise.<!Array.<number>>} The unique IDs of all of the
|
|
* stored groups.
|
|
* @exportDoc
|
|
*/
|
|
shaka.player.OfflineVideoSource.retrieveGroupIds = function() {
|
|
var contentDatabase = new shaka.util.ContentDatabaseReader();
|
|
|
|
var p = contentDatabase.setUpDatabase().then(
|
|
function() {
|
|
return contentDatabase.retrieveGroupIds();
|
|
});
|
|
|
|
p.then(
|
|
function() {
|
|
contentDatabase.closeDatabaseConnection();
|
|
}
|
|
).catch(
|
|
function() {
|
|
contentDatabase.closeDatabaseConnection();
|
|
}
|
|
);
|
|
|
|
return p;
|
|
};
|
|
if (shaka.features.Offline) {
|
|
goog.exportSymbol('shaka.player.OfflineVideoSource.retrieveGroupIds',
|
|
shaka.player.OfflineVideoSource.retrieveGroupIds);
|
|
}
|
|
|
|
|
|
/**
|
|
* Stores the content described by the MPD for offline playback.
|
|
* @param {string} mpdUrl The MPD URL.
|
|
* @param {string} preferredLanguage The user's preferred language tag.
|
|
* @param {?shaka.player.DashVideoSource.ContentProtectionCallback}
|
|
* interpretContentProtection A callback to interpret the ContentProtection
|
|
* elements in the MPD.
|
|
* @param {shaka.player.OfflineVideoSource.ChooseTracksCallback} chooseTracks
|
|
* @return {!Promise.<number>} The group ID of the stored content.
|
|
* @exportDoc
|
|
*/
|
|
shaka.player.OfflineVideoSource.prototype.store = function(
|
|
mpdUrl, preferredLanguage, interpretContentProtection, chooseTracks) {
|
|
var emeManager;
|
|
var error = null;
|
|
|
|
/** @type {!Object.<number, !shaka.media.StreamInfo>} */
|
|
var streamIdMap = {};
|
|
|
|
/** @type {!Array.<!shaka.media.StreamInfo>} */
|
|
var selectedStreams = [];
|
|
|
|
var failover = new shaka.util.FailoverUri(this.networkCallback_,
|
|
[new goog.Uri(mpdUrl)]);
|
|
var mpdRequest = new shaka.dash.MpdRequest(failover, this.mpdRequestTimeout);
|
|
|
|
return mpdRequest.send().then(shaka.util.TypedBind(this,
|
|
/** @param {!shaka.dash.mpd.Mpd} mpd */
|
|
function(mpd) {
|
|
var mpdProcessor =
|
|
new shaka.dash.MpdProcessor(interpretContentProtection);
|
|
this.manifestInfo = mpdProcessor.process(mpd, this.networkCallback_);
|
|
|
|
if (this.manifestInfo.live) {
|
|
var error = new Error('Unable to store live streams offline.');
|
|
error.type = 'app';
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
this.configure({'preferredLanguage': preferredLanguage});
|
|
var baseClassLoad = shaka.player.StreamVideoSource.prototype.load;
|
|
return baseClassLoad.call(this);
|
|
})
|
|
).then(shaka.util.TypedBind(this,
|
|
function() {
|
|
var fakeVideoElement = /** @type {!HTMLVideoElement} */ (
|
|
document.createElement('video'));
|
|
fakeVideoElement.src = window.URL.createObjectURL(this.mediaSource);
|
|
|
|
emeManager =
|
|
new shaka.media.EmeManager(null, fakeVideoElement, this);
|
|
if (this.config_['licenseRequestTimeout'] != null) {
|
|
emeManager.setLicenseRequestTimeout(
|
|
Number(this.config_['licenseRequestTimeout']));
|
|
}
|
|
this.eventManager.listen(
|
|
emeManager, 'sessionReady', this.onSessionReady_.bind(this));
|
|
this.eventManager.listen(emeManager, 'error', function(e) {
|
|
error = e;
|
|
});
|
|
return emeManager.initialize();
|
|
})
|
|
).then(shaka.util.TypedBind(this,
|
|
function() {
|
|
// Build a map of stream IDs.
|
|
var streamSetInfos = this.streamSetsByType.getAll();
|
|
for (var i = 0; i < streamSetInfos.length; ++i) {
|
|
var streamSetInfo = streamSetInfos[i];
|
|
for (var j = 0; j < streamSetInfo.streamInfos.length; ++j) {
|
|
var streamInfo = streamSetInfo.streamInfos[j];
|
|
streamIdMap[streamInfo.uniqueId] = streamInfo;
|
|
}
|
|
}
|
|
|
|
// Ask the application to choose streams.
|
|
return chooseTracks();
|
|
})
|
|
).then(shaka.util.TypedBind(this,
|
|
/** @param {!Array.<number>} trackIds */
|
|
function(trackIds) {
|
|
// Map the track IDs back to streams.
|
|
for (var i = 0; i < trackIds.length; ++i) {
|
|
var id = trackIds[i];
|
|
var selectedStream = streamIdMap[id];
|
|
if (selectedStream) {
|
|
selectedStreams.push(selectedStream);
|
|
} else {
|
|
return Promise.reject(new Error('Invalid stream ID chosen: ' + id));
|
|
}
|
|
}
|
|
|
|
// Only keep those which are supported types.
|
|
// TODO(natalieharris): Add text support.
|
|
var supportedTypes = ['audio', 'video'];
|
|
selectedStreams = selectedStreams.filter(function(streamInfo) {
|
|
if (supportedTypes.indexOf(streamInfo.getContentType()) < 0) {
|
|
shaka.log.warning('Ignoring track ID ' + streamInfo.uniqueId +
|
|
' due to unsupported type: ' +
|
|
streamInfo.getContentType());
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
var async = selectedStreams.map(
|
|
function(streamInfo) {
|
|
return streamInfo.segmentInitSource.create();
|
|
});
|
|
return Promise.all(async);
|
|
})
|
|
).then(shaka.util.TypedBind(this,
|
|
/** @param {!Array.<ArrayBuffer>} initDatas */
|
|
function(initDatas) {
|
|
return this.initializeStreams_(selectedStreams, initDatas);
|
|
})
|
|
).then(shaka.util.TypedBind(this,
|
|
function() {
|
|
return emeManager.allSessionsReady(this.timeoutMs);
|
|
})
|
|
).then(shaka.util.TypedBind(this,
|
|
function() {
|
|
if (error) {
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
var drmInfo = emeManager.getDrmInfo();
|
|
// TODO(story 1890046): Support multiple periods.
|
|
var duration = this.manifestInfo.periodInfos[0].duration;
|
|
if (!duration) {
|
|
shaka.log.warning('The duration of the stream being stored is null.');
|
|
}
|
|
shaka.asserts.assert(duration != Number.POSITIVE_INFINITY);
|
|
|
|
return this.insertGroup_(selectedStreams, drmInfo, duration);
|
|
})
|
|
).then(shaka.util.TypedBind(this,
|
|
/** @param {number} i */
|
|
function(i) {
|
|
this.groupId_ = i;
|
|
if (error) {
|
|
this.deleteGroupContent_();
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
return Promise.resolve(i);
|
|
})
|
|
);
|
|
};
|
|
if (shaka.features.Offline && shaka.features.Dash) {
|
|
goog.exportSymbol('shaka.player.OfflineVideoSource.prototype.store',
|
|
shaka.player.OfflineVideoSource.prototype.store);
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the callback used to intercept the URL in network requests.
|
|
*
|
|
* @param {!shaka.util.FailoverUri.NetworkCallback} callback
|
|
* @export
|
|
*/
|
|
shaka.player.OfflineVideoSource.prototype.setNetworkCallback =
|
|
function(callback) {
|
|
this.networkCallback_ = callback;
|
|
};
|
|
|
|
|
|
/**
|
|
* Creates sourceBuffers and appends init data for each of the given streams.
|
|
* This should trigger encrypted events for any encrypted streams.
|
|
* @param {!Array.<!shaka.media.StreamInfo>} streamInfos The streams to
|
|
* initialize.
|
|
* @param {!Array.<ArrayBuffer>} initDatas |streamInfos| corresponding
|
|
* initialization data.
|
|
* @return {!Promise}
|
|
* @private
|
|
*/
|
|
shaka.player.OfflineVideoSource.prototype.initializeStreams_ =
|
|
function(streamInfos, initDatas) {
|
|
shaka.asserts.assert(streamInfos.length == initDatas.length);
|
|
|
|
var sourceBuffers = [];
|
|
for (var i = 0; i < streamInfos.length; ++i) {
|
|
try {
|
|
var fullMimeType = streamInfos[i].getFullMimeType();
|
|
sourceBuffers[i] = this.mediaSource.addSourceBuffer(fullMimeType);
|
|
} catch (exception) {
|
|
shaka.log.error('addSourceBuffer() failed', exception);
|
|
}
|
|
}
|
|
|
|
if (streamInfos.length != sourceBuffers.length) {
|
|
var error = new Error('Error initializing streams.');
|
|
error.type = 'storage';
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
for (var i = 0; i < initDatas.length; ++i) {
|
|
var initData = initDatas[i];
|
|
if (initData) {
|
|
sourceBuffers[i].appendBuffer(initData);
|
|
}
|
|
}
|
|
|
|
return Promise.resolve();
|
|
};
|
|
|
|
|
|
/**
|
|
* Event handler for sessionReady events.
|
|
* @param {Event} event A sessionReady event.
|
|
* @private
|
|
*/
|
|
shaka.player.OfflineVideoSource.prototype.onSessionReady_ = function(event) {
|
|
var session = /** @type {MediaKeySession} */ (event.detail);
|
|
this.sessionIds_.push(session.sessionId);
|
|
};
|
|
|
|
|
|
/**
|
|
* Inserts a group of streams into the database.
|
|
* @param {!Array.<!shaka.media.StreamInfo>} selectedStreams The streams to
|
|
* insert.
|
|
* @param {shaka.player.DrmInfo} drmInfo
|
|
* @param {?number} duration The duration of the entire stream.
|
|
* @return {!Promise.<number>} The unique id assigned to the group.
|
|
* @private
|
|
*/
|
|
shaka.player.OfflineVideoSource.prototype.insertGroup_ =
|
|
function(selectedStreams, drmInfo, duration) {
|
|
var contentDatabase =
|
|
new shaka.util.ContentDatabaseWriter(this.estimator, this);
|
|
if (this.config_['segmentRequestTimeout'] != null) {
|
|
contentDatabase.setSegmentRequestTimeout(
|
|
Number(this.config_['segmentRequestTimeout']));
|
|
}
|
|
|
|
// Insert the group of streams into the database and close the connection.
|
|
return contentDatabase.setUpDatabase().then(shaka.util.TypedBind(this,
|
|
function() {
|
|
return contentDatabase.insertGroup(
|
|
selectedStreams, this.sessionIds_, duration, drmInfo);
|
|
})
|
|
).then(
|
|
/** @param {number} groupId */
|
|
function(groupId) {
|
|
contentDatabase.closeDatabaseConnection();
|
|
return Promise.resolve(groupId);
|
|
}
|
|
).catch(
|
|
/** @param {*} e */
|
|
function(e) {
|
|
contentDatabase.closeDatabaseConnection();
|
|
return Promise.reject(e);
|
|
}
|
|
);
|
|
};
|
|
|
|
|
|
/** @override */
|
|
shaka.player.OfflineVideoSource.prototype.load = function() {
|
|
shaka.asserts.assert(this.groupId_ >= 0);
|
|
var contentDatabase = new shaka.util.ContentDatabaseReader();
|
|
var duration, config;
|
|
|
|
return contentDatabase.setUpDatabase().then(shaka.util.TypedBind(this,
|
|
function() {
|
|
return contentDatabase.retrieveGroup(
|
|
/** @type {number} */(this.groupId_));
|
|
})
|
|
).then(shaka.util.TypedBind(this,
|
|
/** @param {shaka.util.ContentDatabase.GroupInformation} group */
|
|
function(group) {
|
|
var async = [];
|
|
this.sessionIds_ = group['session_ids'];
|
|
duration = group['duration'];
|
|
config = {
|
|
'keySystem': group['key_system'],
|
|
'distinctiveIdentifierRequired': group['distinctive_identifier'],
|
|
'persistentStorageRequired': true,
|
|
'audioRobustness': group['audio_robustness'],
|
|
'videoRobustness': group['video_robustness'],
|
|
'withCredentials': group['with_credentials'],
|
|
'licenseServerUrl': group['license_server']
|
|
};
|
|
for (var i = 0; i < group['stream_ids'].length; ++i) {
|
|
var streamId = group['stream_ids'][i];
|
|
async.push(contentDatabase.retrieveStreamIndex(streamId));
|
|
}
|
|
return Promise.all(async);
|
|
})
|
|
).then(shaka.util.TypedBind(this,
|
|
/** @param {!Array.<shaka.util.ContentDatabase.StreamIndex>} indexes */
|
|
function(indexes) {
|
|
this.manifestInfo =
|
|
this.reconstructManifestInfo_(indexes, duration, config);
|
|
|
|
var baseClassLoad = shaka.player.StreamVideoSource.prototype.load;
|
|
return baseClassLoad.call(this);
|
|
})
|
|
).then(
|
|
function() {
|
|
contentDatabase.closeDatabaseConnection();
|
|
return Promise.resolve();
|
|
}
|
|
).catch(
|
|
/** @param {*} e */
|
|
function(e) {
|
|
contentDatabase.closeDatabaseConnection();
|
|
return Promise.reject(e);
|
|
}
|
|
);
|
|
};
|
|
|
|
|
|
/**
|
|
* Reconstructs a ManifestInfo object with data from storage.
|
|
* @param {!Array.<shaka.util.ContentDatabase.StreamIndex>} indexes The indexes
|
|
* of the streams in this manifest.
|
|
* @param {number} duration The max stream's entire duration in the group.
|
|
* @param {shaka.player.DrmInfo.Config} config The config info loaded from
|
|
* storage.
|
|
* @return {!shaka.media.ManifestInfo}
|
|
* @private
|
|
*/
|
|
shaka.player.OfflineVideoSource.prototype.reconstructManifestInfo_ =
|
|
function(indexes, duration, config) {
|
|
var manifestInfo = new shaka.media.ManifestInfo();
|
|
manifestInfo.minBufferTime = 5;
|
|
// TODO(story 1890046): Support multiple periods.
|
|
var periodInfo = new shaka.media.PeriodInfo();
|
|
|
|
for (var i = 0; i < indexes.length; ++i) {
|
|
var storedStreamInfo = indexes[i];
|
|
|
|
// Will only have one streamInfo per streamSetInfo stored.
|
|
var streamInfo = new shaka.media.StreamInfo();
|
|
|
|
var segmentIndexSource = new shaka.media.OfflineSegmentIndexSource(
|
|
storedStreamInfo['references']);
|
|
|
|
var segmentInitSource = new shaka.media.SegmentInitSource(
|
|
null, storedStreamInfo['init_segment']);
|
|
|
|
streamInfo.segmentIndexSource = segmentIndexSource;
|
|
streamInfo.segmentInitSource = segmentInitSource;
|
|
streamInfo.mimeType = storedStreamInfo['mime_type'];
|
|
streamInfo.codecs = storedStreamInfo['codecs'];
|
|
streamInfo.allowedByKeySystem = true;
|
|
|
|
if (this.overrideConfig_) {
|
|
if (this.overrideConfig_['licenseServerUrl'] != null) {
|
|
config['licenseServerUrl'] = this.overrideConfig_['licenseServerUrl'];
|
|
}
|
|
if (this.overrideConfig_['withCredentials'] != null) {
|
|
config['withCredentials'] = this.overrideConfig_['withCredentials'];
|
|
}
|
|
config['licensePostProcessor'] =
|
|
this.overrideConfig_['licensePostProcessor'];
|
|
config['licensePreProcessor'] =
|
|
this.overrideConfig_['licensePreProcessor'];
|
|
config['serverCertificate'] = this.overrideConfig_['serverCertificate'];
|
|
}
|
|
|
|
var drmInfo = shaka.player.DrmInfo.createFromConfig(config);
|
|
var streamSetInfo = new shaka.media.StreamSetInfo();
|
|
streamSetInfo.streamInfos.push(streamInfo);
|
|
streamSetInfo.drmInfos.push(drmInfo);
|
|
streamSetInfo.contentType = streamInfo.mimeType.split('/')[0];
|
|
periodInfo.streamSetInfos.push(streamSetInfo);
|
|
periodInfo.duration = duration;
|
|
}
|
|
manifestInfo.periodInfos.push(periodInfo);
|
|
return manifestInfo;
|
|
};
|
|
|
|
|
|
/**
|
|
* Deletes a group of streams from storage. This destroys the VideoSource.
|
|
*
|
|
* @param {shaka.player.DrmInfo.Config=} opt_config Optional config to override
|
|
* the values stored. Can only change |licenseServerUrl|, |withCredentials|,
|
|
* |serverCertificate|, |licensePreProcessor|, and |licensePostProcessor|.
|
|
* @param {boolean=} opt_forceDelete True to delete the content even if there
|
|
* is an error when deleting the persistent session. The error is returned.
|
|
* @return {!Promise.<Error>}
|
|
* @export
|
|
*/
|
|
shaka.player.OfflineVideoSource.prototype.deleteGroup =
|
|
function(opt_config, opt_forceDelete) {
|
|
shaka.asserts.assert(this.groupId_ >= 0);
|
|
|
|
if (opt_config) {
|
|
this.overrideConfig_ = {
|
|
'licenseServerUrl': opt_config['licenseServerUrl'],
|
|
'withCredentials': opt_config['withCredentials'],
|
|
'serverCertificate': opt_config['serverCertificate'],
|
|
'licensePreProcessor': opt_config['licensePreProcessor'],
|
|
'licensePostProcessor': opt_config['licensePostProcessor']
|
|
};
|
|
}
|
|
|
|
var error = null;
|
|
return this.deletePersistentSessions_().catch(function(e) {
|
|
if (opt_forceDelete) {
|
|
error = e;
|
|
return Promise.resolve();
|
|
}
|
|
|
|
return Promise.reject(e);
|
|
}).then(shaka.util.TypedBind(this, function() {
|
|
return this.deleteGroupContent_();
|
|
})).then(function() {
|
|
return Promise.resolve(error);
|
|
});
|
|
};
|
|
|
|
|
|
/** @override */
|
|
shaka.player.OfflineVideoSource.prototype.getSessionIds = function() {
|
|
return this.sessionIds_;
|
|
};
|
|
|
|
|
|
/** @override */
|
|
shaka.player.OfflineVideoSource.prototype.isOffline = function() {
|
|
return true;
|
|
};
|
|
|
|
|
|
/**
|
|
* Deletes the offline content from the database for the given |group|.
|
|
*
|
|
* @return {!Promise}
|
|
* @private
|
|
*/
|
|
shaka.player.OfflineVideoSource.prototype.deleteGroupContent_ = function() {
|
|
var contentDatabase = new shaka.util.ContentDatabaseWriter(null, null);
|
|
|
|
return contentDatabase.setUpDatabase().then(shaka.util.TypedBind(this,
|
|
function() {
|
|
return contentDatabase.deleteGroup(
|
|
/** @type {number} */ (this.groupId_));
|
|
})
|
|
).then(
|
|
function() {
|
|
contentDatabase.closeDatabaseConnection();
|
|
return Promise.resolve();
|
|
}
|
|
).catch(
|
|
/** @param {*} e */
|
|
function(e) {
|
|
contentDatabase.closeDatabaseConnection();
|
|
return Promise.reject(e);
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Deletes any persistent sessions associated with the |groupId_|.
|
|
*
|
|
* @return {!Promise}
|
|
* @private
|
|
*/
|
|
shaka.player.OfflineVideoSource.prototype.deletePersistentSessions_ =
|
|
function() {
|
|
var fakeVideoElement = /** @type {!HTMLVideoElement} */ (
|
|
document.createElement('video'));
|
|
fakeVideoElement.src = window.URL.createObjectURL(this.mediaSource);
|
|
|
|
var emeManager = new shaka.media.EmeManager(null, fakeVideoElement, this);
|
|
if (this.config_['licenseRequestTimeout'] != null) {
|
|
emeManager.setLicenseRequestTimeout(
|
|
Number(this.config_['licenseRequestTimeout']));
|
|
}
|
|
|
|
return this.load().then(function() {
|
|
return emeManager.initialize();
|
|
}).then(shaka.util.TypedBind(this, function() {
|
|
return emeManager.allSessionsReady(this.timeoutMs);
|
|
})).then(function() {
|
|
return emeManager.deleteSessions();
|
|
}).then(shaka.util.TypedBind(this,
|
|
function() {
|
|
emeManager.destroy();
|
|
this.destroy();
|
|
return Promise.resolve();
|
|
})
|
|
).catch(shaka.util.TypedBind(this,
|
|
/** @param {*} e */
|
|
function(e) {
|
|
emeManager.destroy();
|
|
this.destroy();
|
|
return Promise.reject(e);
|
|
})
|
|
);
|
|
};
|
|
|