Skip to content

Commit

Permalink
Set src= Load Mode
Browse files Browse the repository at this point in the history
Create the src= load branches for the remaining public methods. This
should ensure that it is now safe to call the public methods when
playing src= content and expect to get the intended return values.

The tests to verify this are in a follow-up CL.

Issue #816
Issue #997

Change-Id: I088b6bbd2489b3960457030846debae07fd86d16
  • Loading branch information
vaage committed Apr 10, 2019
1 parent ed2c5c3 commit 31b94e8
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 47 deletions.
111 changes: 84 additions & 27 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1508,7 +1508,7 @@ shaka.Player.prototype.onLoad_ = async function(has, wants) {

// Stats are for a single playback/load session. Stats must be initialized
// before we allow calls to |updateStateHistory|.
this.stats_ = new shaka.util.Stats(wants.startTimeOfLoad);
this.stats_ = new shaka.util.Stats();

const updateStateHistory = () => this.updateStateHistory_();
this.eventManager_.listen(mediaElement, 'playing', updateStateHistory);
Expand Down Expand Up @@ -1618,7 +1618,10 @@ shaka.Player.prototype.onLoad_ = async function(has, wants) {

// Wait for the 'loadeddata' event to measure load() latency.
this.eventManager_.listenOnce(mediaElement, 'loadeddata', () => {
this.stats_.markEndOfLoad();
const now = Date.now() / 1000;
const delta = now - wants.startTimeOfLoad;

this.stats_.setLoadLatency(delta);
});
};

Expand Down Expand Up @@ -1660,7 +1663,7 @@ shaka.Player.prototype.onSrcEquals_ = function(has, wants) {
this.assetUri_ = has.uri;

// Stats are for a single playback/load session.
this.stats_ = new shaka.util.Stats(wants.startTimeOfLoad);
this.stats_ = new shaka.util.Stats();

this.playhead_ = new shaka.media.SrcEqualsPlayhead(has.mediaElement);

Expand All @@ -1671,21 +1674,38 @@ shaka.Player.prototype.onSrcEquals_ = function(has, wants) {
// We need to start the buffer management code near the end because it will
// set the initial buffering state and that depends on other components being
// initialized.
this.startBufferManagement_(this.config_.streaming.rebufferingGoal);
//
// With src= we are not in-charge of the buffering logic, so we must respect
// what the browser wants to do. Playing 4K MP4 Sintel on Chrome, the media
// element appears to limit itself to 2.5 seconds. For now, lets put our
// rebuffer to 2 seconds.
//
// TODO: If 2 seconds appears to be a poor choice, explore different options
// for what we can do to detect/track buffering with src=.
this.startBufferManagement_(/* rebuffer= */ 2);

// Add all media element listeners.
const updateStateHistory = () => this.updateStateHistory_();
this.eventManager_.listen(has.mediaElement, 'playing', updateStateHistory);
this.eventManager_.listen(has.mediaElement, 'pause', updateStateHistory);
this.eventManager_.listen(has.mediaElement, 'ended', updateStateHistory);

// Wait for the 'loadeddata' event to measure load() latency.
this.eventManager_.listenOnce(has.mediaElement, 'loadeddata', () => {
this.stats_.markEndOfLoad();
const now = Date.now() / 1000;
const delta = now - wants.startTimeOfLoad;

this.stats_.setLoadLatency(delta);
});

// By setting |src| we are done "loading" with src=. We don't need to set the
// current time because |playhead| will do that for us.
has.mediaElement.src = has.uri;

// Set the load mode last so that we know that all our components are
// initialized.
this.loadMode_ = shaka.Player.LoadMode.SRC_EQUALS;

return shaka.util.AbortableOperation.completed(undefined);
};

Expand Down Expand Up @@ -2233,9 +2253,10 @@ shaka.Player.prototype.getNetworkingEngine = function() {
* @export
*/
shaka.Player.prototype.getAssetUri = function() {
return this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ?
this.assetUri_ :
null;
const loaded = this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ||
this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS;

return loaded ? this.assetUri_ : null;
};


Expand Down Expand Up @@ -2295,6 +2316,9 @@ shaka.Player.prototype.isInProgress = function() {
* @export
*/
shaka.Player.prototype.isAudioOnly = function() {
// TODO: When the audio tracks api on HTMLMediaElement is generally supported
// we should be able to check if content is audio only.

if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE) {
return false;
}
Expand Down Expand Up @@ -2322,6 +2346,13 @@ shaka.Player.prototype.isAudioOnly = function() {
* @export
*/
shaka.Player.prototype.seekRange = function() {
// If we have loaded content with src=, we assume that the whole presentation
// is seekable. For mp4, this is always the case. For HLS, this may not always
// be the case, but there is no way for us to get this information.
if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
return {'start': 0, 'end': this.video_.duration};
}

if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE) {
return {'start': 0, 'end': 0};
}
Expand Down Expand Up @@ -2392,7 +2423,10 @@ shaka.Player.prototype.getExpiration = function() {
shaka.Player.prototype.isBuffering = function() {
const State = shaka.media.BufferingObserver.State;

return this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ?
const loaded = this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ||
this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS;

return loaded ?
this.bufferObserver_.getState() == State.STARVING :
false;
};
Expand All @@ -2410,9 +2444,10 @@ shaka.Player.prototype.isBuffering = function() {
* @export
*/
shaka.Player.prototype.getPlaybackRate = function() {
return this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ?
this.playhead_.getPlaybackRate() :
0;
const loaded = this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ||
this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS;

return loaded ? this.playhead_.getPlaybackRate() : 0;
};


Expand Down Expand Up @@ -2470,6 +2505,9 @@ shaka.Player.prototype.cancelTrickPlay = function() {
* @export
*/
shaka.Player.prototype.getVariantTracks = function() {
// TODO: Once we get consistent behaviour from the tracks api across browsers
// we should be able to provide variant track information for src=.

if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE) {
return [];
}
Expand Down Expand Up @@ -2502,6 +2540,9 @@ shaka.Player.prototype.getVariantTracks = function() {
* @export
*/
shaka.Player.prototype.getTextTracks = function() {
// TODO: Once we get consistent behaviour from the tracks api across browsers
// we should be able to provide variant track information for src=.

if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE) {
return [];
}
Expand Down Expand Up @@ -2828,6 +2869,8 @@ shaka.Player.prototype.selectTextLanguage = function(language, role) {
* @export
*/
shaka.Player.prototype.isTextTrackVisible = function() {
// Right now we don't support text with src=. So the only time that text could
// be visible is when we have loaded content with media source.
if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE) {
return false;
}
Expand Down Expand Up @@ -2994,6 +3037,11 @@ shaka.Player.prototype.getBufferedInfo = function() {
text: [],
};

if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
const getBufferedInfo = shaka.media.TimeRangesUtils.getBufferedInfo;
info.total = getBufferedInfo(this.video_.buffered);
}

if (this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE) {
this.mediaSourceEngine_.getBufferedInfo(info);
}
Expand All @@ -3014,7 +3062,10 @@ shaka.Player.prototype.getStats = function() {
// never fail.
//
// TODO: Should we include the bandwidth information with the empty blob?
if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE) {
const loaded = this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ||
this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS;

if (!loaded) {
return shaka.util.Stats.getEmptyBlob();
}

Expand All @@ -3031,23 +3082,25 @@ shaka.Player.prototype.getStats = function() {
Number(info.totalVideoFrames));
}

// Event through we are load, it is still possible that we don't have a
// presentation variant yet because we set the load mode before we select the
// first variant to stream.
const variant = this.getPresentationVariant_();
if (this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE) {
// Event through we are loaded, it is still possible that we don't have a
// presentation variant yet because we set the load mode before we select
// the first variant to stream.
const variant = this.getPresentationVariant_();

if (variant) {
this.stats_.setVariantBandwidth(variant.bandwidth);
}
if (variant) {
this.stats_.setVariantBandwidth(variant.bandwidth);
}

if (variant && variant.video) {
this.stats_.setResolution(
/* width= */ variant.video.width || NaN,
/* height= */ variant.video.height || NaN);
}
if (variant && variant.video) {
this.stats_.setResolution(
/* width= */ variant.video.width || NaN,
/* height= */ variant.video.height || NaN);
}

const estimate = this.abrManager_.getBandwidthEstimate();
this.stats_.setBandwidthEstimate(estimate);
const estimate = this.abrManager_.getBandwidthEstimate();
this.stats_.setBandwidthEstimate(estimate);
}

return this.stats_.getBlob();
};
Expand All @@ -3071,6 +3124,10 @@ shaka.Player.prototype.getStats = function() {
shaka.Player.prototype.addTextTrack = async function(
uri, language, kind, mime, codec, label) {
// TODO: Add an actual error for this.
if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
shaka.log.error('Cannot add text when loaded with src=');
return Promise.reject();
}
if (this.loadMode_ != shaka.Player.LoadMode.MEDIA_SOURCE) {
shaka.log.error(
'Must call load() and wait for it to resolve before adding text ' +
Expand Down
29 changes: 9 additions & 20 deletions lib/util/stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ goog.require('shaka.util.SwitchHistory');
* @final
*/
shaka.util.Stats = class {
/**
* @param {number} startOfLoadSeconds
*/
constructor(startOfLoadSeconds) {
constructor() {
/** @private {number} */
this.width_ = NaN;
/** @private {number} */
Expand All @@ -42,13 +39,8 @@ shaka.util.Stats = class {
/** @private {number} */
this.totalDecodedFrames_ = NaN;

// We want to track how much time elapses between when the player says it
// starts loading content and when it says its done. The meaning of "start"
// and "finish" is defined within the scope of the player, not here.
/** @private {number} */
this.loadStartedSeconds_ = startOfLoadSeconds;
/** @private {number} */
this.loadFinishedSeconds_ = NaN;
this.loadLatencySeconds_ = NaN;

/** @private {number} */
this.variantBandwidth_ = NaN;
Expand Down Expand Up @@ -86,11 +78,13 @@ shaka.util.Stats = class {
}

/**
* Mark the end of the load process. This should only be called after calling
* |markStartOfLoad|.
* Record the time it took between the user signalling "I want to play this"
* to "I am now seeing this".
*
* @param {number} seconds
*/
markEndOfLoad() {
this.loadFinishedSeconds_ = Date.now() / 1000;
setLoadLatency(seconds) {
this.loadLatencySeconds_ = seconds;
}

/**
Expand Down Expand Up @@ -128,19 +122,14 @@ shaka.util.Stats = class {
* @return {shaka.extern.Stats}
*/
getBlob() {
// Make sure that the start and end times make sense. If not, use NaN.
const loadLatency = (this.loadFinishedSeconds_ > this.loadStartedSeconds_) ?
(this.loadFinishedSeconds_ - this.loadStartedSeconds_) :
(NaN);

return {
width: this.width_,
height: this.height_,
streamBandwidth: this.variantBandwidth_,
decodedFrames: this.totalDecodedFrames_,
droppedFrames: this.totalDroppedFrames_,
estimatedBandwidth: this.bandwidthEstimate_,
loadLatency: loadLatency,
loadLatency: this.loadLatencySeconds_,
playTime: this.stateHistory_.getTimeSpentIn('playing'),
pauseTime: this.stateHistory_.getTimeSpentIn('paused'),
bufferingTime: this.stateHistory_.getTimeSpentIn('buffering'),
Expand Down

0 comments on commit 31b94e8

Please sign in to comment.