mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-23 17:27:16 +03:00
Various fixes for offline playback.
* Remove warnings for incorrect argument counts in configure. * Duration incorrect for multi-Period. * Multi-Period does not always work with multi-codec. * Progress meter visible from start and after done. * Offline buttons enable while storing if asset is switched. b/29777213 Change-Id: I934bec0e6b5be2d69a908629b187459a6289f7a7
This commit is contained in:
@@ -182,6 +182,10 @@ summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#progressDiv {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#logSection {
|
||||
display: none;
|
||||
}
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@
|
||||
<summary>Offline</summary>
|
||||
<button id="storeOffline">Store</button>
|
||||
<button id="deleteOffline" disabled>Delete</button>
|
||||
<div>
|
||||
<div id="progressDiv">
|
||||
<label for="progress">Progress:</label>
|
||||
<span id="progress">0</span>%
|
||||
</div>
|
||||
|
||||
+40
-23
@@ -24,9 +24,21 @@ var shakaDemo = shakaDemo || {};
|
||||
shakaDemo.offlineOptGroup_ = null;
|
||||
|
||||
|
||||
/** @private */
|
||||
shakaDemo.updateButtons_ = function() {
|
||||
/** @private {boolean} */
|
||||
shakaDemo.offlineOperationInProgress_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* @param {boolean} canHide True to hide the progress value if there isn't an
|
||||
* operation going.
|
||||
* @private
|
||||
*/
|
||||
shakaDemo.updateButtons_ = function(canHide) {
|
||||
var assetList = document.getElementById('assetList');
|
||||
var inProgress = shakaDemo.offlineOperationInProgress_;
|
||||
|
||||
document.getElementById('progressDiv').style.display =
|
||||
canHide && !inProgress ? 'none' : 'block';
|
||||
|
||||
var option = assetList.options[assetList.selectedIndex];
|
||||
var storedContent = option.storedContent;
|
||||
@@ -39,14 +51,23 @@ shakaDemo.updateButtons_ = function() {
|
||||
});
|
||||
|
||||
var storeBtn = document.getElementById('storeOffline');
|
||||
storeBtn.disabled = (!supportsDrm || storedContent != null);
|
||||
storeBtn.title = !supportsDrm ?
|
||||
'This browser does not support persistent licenses' :
|
||||
(storeBtn.disabled ? 'Selected asset is already stored offline' : '');
|
||||
storeBtn.disabled = (inProgress || !supportsDrm || storedContent != null);
|
||||
if (inProgress)
|
||||
storeBtn.title = 'There is already an operation in progress';
|
||||
else if (!supportsDrm)
|
||||
storeBtn.title = 'This browser does not support persistent licenses';
|
||||
else if (storeBtn.disabled)
|
||||
storeBtn.title = 'Selected asset is already stored offline';
|
||||
else
|
||||
storeBtn.title = '';
|
||||
var deleteBtn = document.getElementById('deleteOffline');
|
||||
deleteBtn.disabled = (storedContent == null);
|
||||
deleteBtn.title =
|
||||
deleteBtn.disabled ? 'Selected asset is not stored offline' : '';
|
||||
deleteBtn.disabled = (inProgress || storedContent == null);
|
||||
if (inProgress)
|
||||
deleteBtn.title = 'There is already an operation in progress';
|
||||
else if (deleteBtn.disabled)
|
||||
deleteBtn.title = 'Selected asset is not stored offline';
|
||||
else
|
||||
deleteBtn.title = '';
|
||||
};
|
||||
|
||||
|
||||
@@ -57,8 +78,8 @@ shakaDemo.setupOffline_ = function() {
|
||||
document.getElementById('deleteOffline')
|
||||
.addEventListener('click', shakaDemo.deleteAsset_);
|
||||
document.getElementById('assetList')
|
||||
.addEventListener('change', shakaDemo.updateButtons_);
|
||||
shakaDemo.updateButtons_();
|
||||
.addEventListener('change', shakaDemo.updateButtons_.bind(null, true));
|
||||
shakaDemo.updateButtons_(true);
|
||||
};
|
||||
|
||||
|
||||
@@ -109,24 +130,21 @@ shakaDemo.setupOfflineAssets_ = function() {
|
||||
/** @private */
|
||||
shakaDemo.storeAsset_ = function() {
|
||||
shakaDemo.closeError();
|
||||
shakaDemo.offlineOperationInProgress_ = true;
|
||||
shakaDemo.updateButtons_(false);
|
||||
|
||||
var assetList = document.getElementById('assetList');
|
||||
var progress = document.getElementById('progress');
|
||||
var storeBtn = document.getElementById('storeOffline');
|
||||
var deleteBtn = document.getElementById('deleteOffline');
|
||||
var option = assetList.options[assetList.selectedIndex];
|
||||
|
||||
var asset = shakaDemo.preparePlayer_(option.asset);
|
||||
|
||||
progress.textContent = '0';
|
||||
storeBtn.disabled = true;
|
||||
deleteBtn.disabled = true;
|
||||
|
||||
var metadata = {name: asset.name || asset.manifestUri};
|
||||
var storage = new shaka.offline.Storage(shakaDemo.player_);
|
||||
storage.configure(/** @type {shakaExtern.OfflineConfiguration} */ ({
|
||||
progressCallback: function(data, percent) {
|
||||
var progress = document.getElementById('progress');
|
||||
progress.textContent = (percent * 100).toFixed(2);
|
||||
}
|
||||
}));
|
||||
@@ -137,7 +155,8 @@ shakaDemo.storeAsset_ = function() {
|
||||
var error = /** @type {!shaka.util.Error} */(reason);
|
||||
shakaDemo.onError_(error);
|
||||
}).then(function() {
|
||||
shakaDemo.updateButtons_();
|
||||
shakaDemo.offlineOperationInProgress_ = false;
|
||||
shakaDemo.updateButtons_(false);
|
||||
return storage.destroy();
|
||||
});
|
||||
};
|
||||
@@ -146,15 +165,12 @@ shakaDemo.storeAsset_ = function() {
|
||||
/** @private */
|
||||
shakaDemo.deleteAsset_ = function() {
|
||||
shakaDemo.closeError();
|
||||
shakaDemo.offlineOperationInProgress_ = true;
|
||||
shakaDemo.updateButtons_(false);
|
||||
|
||||
var assetList = document.getElementById('assetList');
|
||||
var storeBtn = document.getElementById('storeOffline');
|
||||
var deleteBtn = document.getElementById('deleteOffline');
|
||||
var option = assetList.options[assetList.selectedIndex];
|
||||
|
||||
storeBtn.disabled = true;
|
||||
deleteBtn.disabled = true;
|
||||
|
||||
var storage = new shaka.offline.Storage(shakaDemo.player_);
|
||||
storage.configure(/** @type {shakaExtern.OfflineConfiguration} */ ({
|
||||
progressCallback: function(data, percent) {
|
||||
@@ -169,7 +185,8 @@ shakaDemo.deleteAsset_ = function() {
|
||||
var error = /** @type {!shaka.util.Error} */(reason);
|
||||
shakaDemo.onError_(error);
|
||||
}).then(function() {
|
||||
shakaDemo.updateButtons_();
|
||||
shakaDemo.offlineOperationInProgress_ = false;
|
||||
shakaDemo.updateButtons_(false);
|
||||
return storage.destroy();
|
||||
});
|
||||
};
|
||||
|
||||
+42
-5
@@ -62,6 +62,9 @@ shaka.offline.Storage = function(player) {
|
||||
/** @private {boolean} */
|
||||
this.storeInProgress_ = false;
|
||||
|
||||
/** @private {Array.<shakaExtern.Track>} */
|
||||
this.firstPeriodTracks_ = null;
|
||||
|
||||
/**
|
||||
* The IDs of the segments that have been stored for an in-progress store().
|
||||
* This is used to cleanup in destroy().
|
||||
@@ -514,7 +517,7 @@ shaka.offline.Storage.prototype.defaultTrackSelect_ = function(tracks) {
|
||||
shaka.offline.Storage.prototype.defaultConfig_ = function() {
|
||||
return {
|
||||
trackSelectionCallback: this.defaultTrackSelect_.bind(this),
|
||||
progressCallback: function() {}
|
||||
progressCallback: new Function('storedContent', 'percent', '')
|
||||
};
|
||||
};
|
||||
|
||||
@@ -537,8 +540,33 @@ shaka.offline.Storage.prototype.initIfNeeded_ = function() {
|
||||
* @private
|
||||
*/
|
||||
shaka.offline.Storage.prototype.filterPeriod_ = function(period) {
|
||||
function getFirstStreamOfType(period, tracks, contentType) {
|
||||
var tracksOfType =
|
||||
tracks.filter(function(track) { return track.type == contentType; });
|
||||
if (tracksOfType.length == 0)
|
||||
return null;
|
||||
var data =
|
||||
shaka.util.StreamUtils.findStreamForTrack(period, tracksOfType[0]);
|
||||
goog.asserts.assert(
|
||||
data, 'Could not find stream with id ' + tracksOfType[0].id);
|
||||
return data.stream;
|
||||
}
|
||||
|
||||
var StreamUtils = shaka.util.StreamUtils;
|
||||
StreamUtils.filterPeriod(this.drmEngine_, /* activeStreams */ {}, period);
|
||||
var activeStreams = {};
|
||||
if (this.firstPeriodTracks_) {
|
||||
// Use the first stream of each content type as the "active stream". This
|
||||
// is then used to filter out the streams that are not compatible with it.
|
||||
// This ensures that in multi-Period content, all Periods have streams
|
||||
// with compatible MIME types.
|
||||
activeStreams = {
|
||||
'video': getFirstStreamOfType(
|
||||
this.manifest_.periods[0], this.firstPeriodTracks_, 'video'),
|
||||
'audio': getFirstStreamOfType(
|
||||
this.manifest_.periods[0], this.firstPeriodTracks_, 'audio')
|
||||
};
|
||||
}
|
||||
StreamUtils.filterPeriod(this.drmEngine_, activeStreams, period);
|
||||
StreamUtils.applyRestrictions(
|
||||
period, this.player_.getConfiguration().restrictions);
|
||||
};
|
||||
@@ -556,6 +584,7 @@ shaka.offline.Storage.prototype.cleanup_ = function() {
|
||||
this.drmEngine_ = null;
|
||||
this.manifest_ = null;
|
||||
this.storeInProgress_ = false;
|
||||
this.firstPeriodTracks_ = null;
|
||||
this.inProgressSegmentIds_ = [];
|
||||
this.manifestId_ = -1;
|
||||
return ret;
|
||||
@@ -631,13 +660,19 @@ shaka.offline.Storage.prototype.createOfflineManifest_ = function(
|
||||
shaka.offline.Storage.prototype.createPeriod_ = function(period) {
|
||||
var allTracks = shaka.util.StreamUtils.getTracks(period, null);
|
||||
var tracks = this.config_.trackSelectionCallback(allTracks);
|
||||
if (this.firstPeriodTracks_ == null) {
|
||||
this.firstPeriodTracks_ = tracks;
|
||||
// Now that the first tracks are chosen, filter again. This ensures all
|
||||
// Periods have compatible content types.
|
||||
this.manifest_.periods.forEach(this.filterPeriod_.bind(this));
|
||||
}
|
||||
// TODO(modmaker): Issue a warning if multiple tracks of the same variety
|
||||
// are selected (type, kind, and language).
|
||||
|
||||
var streams = tracks.map(function(track) {
|
||||
var data = shaka.util.StreamUtils.findStreamForTrack(period, track);
|
||||
goog.asserts.assert(data, 'Could not find track with id ' + track.id);
|
||||
return this.createStream_(data.streamSet, data.stream);
|
||||
return this.createStream_(period, data.streamSet, data.stream);
|
||||
}.bind(this));
|
||||
|
||||
return {
|
||||
@@ -651,12 +686,14 @@ shaka.offline.Storage.prototype.createPeriod_ = function(period) {
|
||||
* Converts a manifest stream to a database stream. This will search the
|
||||
* segment index and add all the segments to the download manager.
|
||||
*
|
||||
* @param {shakaExtern.Period} period
|
||||
* @param {shakaExtern.StreamSet} streamSet
|
||||
* @param {shakaExtern.Stream} stream
|
||||
* @return {shakaExtern.StreamDB}
|
||||
* @private
|
||||
*/
|
||||
shaka.offline.Storage.prototype.createStream_ = function(streamSet, stream) {
|
||||
shaka.offline.Storage.prototype.createStream_ = function(
|
||||
period, streamSet, stream) {
|
||||
/** @type {!Array.<shakaExtern.SegmentDB>} */
|
||||
var segmentsDb = [];
|
||||
var startTime = this.manifest_.presentationTimeline.getEarliestStart();
|
||||
@@ -686,7 +723,7 @@ shaka.offline.Storage.prototype.createStream_ = function(streamSet, stream) {
|
||||
uri: 'offline:' + this.manifestId_ + '/' + stream.id + '/' + id
|
||||
});
|
||||
|
||||
endTime = ref.endTime;
|
||||
endTime = ref.endTime + period.startTime;
|
||||
ref = stream.getSegmentReference(++i);
|
||||
}
|
||||
|
||||
|
||||
+1
-7
@@ -1167,13 +1167,7 @@ shaka.Player.prototype.defaultConfig_ = function() {
|
||||
manifest: {
|
||||
retryParameters: shaka.net.NetworkingEngine.defaultRetryParameters(),
|
||||
dash: {
|
||||
customScheme: function(node) {
|
||||
// Reference node to keep closure from removing it.
|
||||
// If the argument is removed, it breaks our function length check
|
||||
// in mergeConfigObjects_().
|
||||
// TODO: Find a better solution if possible.
|
||||
if (node) return null;
|
||||
},
|
||||
customScheme: new Function('node', ''),
|
||||
clockSyncUri: ''
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user