Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(502)

Unified Diff: ui/file_manager/video_player/js/cast/cast_video_element.js

Issue 412813002: Video Player: Support casting a video (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/file_manager/video_player/js/cast/cast_video_element.js
diff --git a/ui/file_manager/video_player/js/cast/cast_video_element.js b/ui/file_manager/video_player/js/cast/cast_video_element.js
index 299fa2bb5d3f1c3740861a3e1ff6379eae9091b6..70f355893fdbdcc31c289177b19486480f0f7cff 100644
--- a/ui/file_manager/video_player/js/cast/cast_video_element.js
+++ b/ui/file_manager/video_player/js/cast/cast_video_element.js
@@ -4,6 +4,9 @@
'use strict';
+// Inverval for updating media info (in ms).
hirono 2014/07/24 02:12:36 nit: jsdoc @const, @type
yoshiki 2014/07/24 02:57:32 Done.
+var MEDIA_UPDATE_INTERVAL = 250;
+
/**
* This class is the dummy class which has same interface as VideoElement. This
* behaves like VideoElement, and is used for making Chromecast player
@@ -11,11 +14,19 @@
*
* @constructor
hirono 2014/07/24 02:12:36 nit: @param
yoshiki 2014/07/24 02:57:32 Done.
*/
-function CastVideoElement() {
- this.duration_ = null;
+function CastVideoElement(mediaInfo, session) {
+ this.mediaInfo_ = mediaInfo;
+ this.castMedia_ = null;
+ this.castSession_ = session;
+ this.lastDuration_ = null;
this.currentTime_ = null;
this.src_ = '';
this.volume_ = 100;
+ this.currentMediaPlayerState_ = null;
+ this.currentMediaCurrentTime_ = null;
+ this.currentMediaDuration_ = null;
+
+ this.onCastMediaUpdatedBound_ = this.onCastMediaUpdated_.bind(this);
}
CastVideoElement.prototype = {
@@ -34,7 +45,12 @@ CastVideoElement.prototype = {
* @type {number}
hirono 2014/07/24 02:12:36 nit: @return Same for the followings.
yoshiki 2014/07/24 02:57:31 I think we don't need it because it's a getter met
hirono 2014/07/24 03:42:49 nit: So please just make #37 consistent with other
yoshiki 2014/07/24 05:19:00 Done.
*/
get duration() {
- return this.duration_;
+ if (this.castMedia_ && this.castMedia_.media) {
+ this.lastDuration_ = this.castMedia_.media.duration;
hirono 2014/07/24 02:12:36 Changing a value in a getter is a bit strange. Can
yoshiki 2014/07/24 02:57:31 Done.
+ return this.castMedia_.media.duration;
+ }
+
+ return this.lastDuration_;
},
/**
@@ -42,10 +58,10 @@ CastVideoElement.prototype = {
* @type {number}
hirono 2014/07/24 02:12:36 @return {?number} ?
yoshiki 2014/07/24 02:57:32 Done.
*/
get currentTime() {
- return this.currentTime_;
+ return this.castMedia_ ? this.castMedia_.getEstimatedTime() : null;
},
set currentTime(currentTime) {
- this.currentTime_ = currentTime;
+ // TODO(yoshiki): Support seek.
hirono 2014/07/24 02:12:36 How about adding throw new Error('Not implemented'
yoshiki 2014/07/24 02:57:32 Please leave it as it is, since this setter is cal
hirono 2014/07/24 03:42:49 SGTM.
},
/**
@@ -53,7 +69,10 @@ CastVideoElement.prototype = {
* @type {boolean}
*/
get paused() {
- return false;
+ if (!this.castMedia_)
+ return false;
+
+ return this.castMedia_.playerState == chrome.cast.media.PlayerState.PAUSED;
hirono 2014/07/24 02:12:36 === ? Same for others.
yoshiki 2014/07/24 02:57:31 Done.
},
/**
@@ -61,7 +80,10 @@ CastVideoElement.prototype = {
* @type {boolean}
*/
get ended() {
- return false;
+ if (!this.castMedia_)
+ return true;
+
+ return this.castMedia_.idleReason == chrome.cast.media.IdleReason.FINISHED;
},
/**
@@ -69,6 +91,7 @@ CastVideoElement.prototype = {
* @type {boolean}
*/
get seekable() {
+ // TODO(yoshiki): Support seek.
return false;
},
@@ -77,38 +100,186 @@ CastVideoElement.prototype = {
* @type {number}
*/
get volume() {
- return this.volume_;
+ return this.castSession_.receiver.volume.muted ?
+ 0 :
+ this.castSession_.receiver.volume.level;
},
set volume(volume) {
- this.volume_ = volume;
+ var VOLUME_EPS = 0.01; // Threshold for ignoring a small change.
+
+ // Ignores < 1% change.
+ if (Math.abs(this.castSession_.receiver.volume.level - volume) < VOLUME_EPS)
+ return;
+
+ if (this.castSession_.receiver.volume.muted) {
+ if (volume < VOLUME_EPS)
+ return;
+
+ // Unmute before setting volume.
+ this.castSession_.setReceiverMuted(false,
+ function() {},
+ this.onCastCommandError_.wrap(this));
+
+ this.castSession_.setReceiverVolumeLevel(volume,
+ function() {},
+ this.onCastCommandError_.wrap(this));
+ } else {
+ if (volume < VOLUME_EPS) {
+ this.castSession_.setReceiverMuted(true,
+ function() {},
+ this.onCastCommandError_.wrap(this));
+ return;
+ }
+
+ this.castSession_.setReceiverVolumeLevel(volume,
+ function() {},
+ this.onCastCommandError_.wrap(this));
+ }
},
/**
* Returns the source of the current video.
- * @return {string}
+ * @return {null}
hirono 2014/07/24 02:12:36 Maybe string is OK because it's nullable.
yoshiki 2014/07/24 02:57:32 changed to '?string'
hirono 2014/07/24 03:42:49 Yes, I misunderstood.
*/
get src() {
- return this.src_;
+ return null;
},
/**
* Plays the video.
*/
play: function() {
- // TODO(yoshiki): Implement this.
+ if (!this.castMedia_) {
+ this.load(function() {
+ this.castMedia_.play(null,
+ function () {},
+ this.onCastCommandError_.wrap(this));
+ }.wrap(this));
+ return;
+ }
+
+ this.castMedia_.play(null,
+ function () {},
+ this.onCastCommandError_.wrap(this));
},
/**
* Pauses the video.
*/
pause: function() {
- // TODO(yoshiki): Implement this.
+ if (!this.castMedia_)
+ return;
+
+ this.castMedia_.pause(null,
+ function () {},
+ this.onCastCommandError_.wrap(this));
},
/**
* Loads the video.
*/
- load: function() {
- // TODO(yoshiki): Implement this.
+ load: function(opt_callback) {
+ var request = new chrome.cast.media.LoadRequest(this.mediaInfo_);
+ this.castSession_.loadMedia(request,
+ function(media) {
+ this.onMediaDiscovered_(media);
+ if (opt_callback)
+ opt_callback();
+ }.bind(this),
+ this.onCastCommandError_.wrap(this));
+ },
+
+ /**
+ * Unloads the video.
+ * @private
+ */
+ unloadMedia_: function() {
+ this.castMedia_.removeUpdateListener(this.onCastMediaUpdatedBound_);
+ this.castMedia_ = null;
+ clearInterval(this.updateTimerId_);
+ },
+
+ /**
+ * This method is called periodically to update media information while the
+ * media is loaded.
+ * @private
+ */
+ onPeriodicalUpdateTimer_: function() {
+ if (!this.castMedia_)
+ return;
+
+ if (this.castMedia_.playerState == chrome.cast.media.PlayerState.PLAYING)
+ this.onCastMediaUpdated_(true);
+ },
+
+ /**
+ * This method should be called when a media file is loaded.
+ * @param {chrome.cast.Media} media Media object which was discovered.
+ * @private
+ */
+ onMediaDiscovered_: function(media) {
+ this.castMedia_ = media;
hirono 2014/07/24 02:12:36 Should we check this.castMedia_ == null since it m
yoshiki 2014/07/24 02:57:32 Done.
+ this.onCastMediaUpdated_(true);
+ media.addUpdateListener(this.onCastMediaUpdatedBound_);
+ this.updateTimerId_ = setInterval(this.onPeriodicalUpdateTimer_.bind(this),
+ MEDIA_UPDATE_INTERVAL);
+ },
+
+ /**
+ * This method should be called when a media command to cast is failed.
+ * @private
+ */
+ onCastCommandError_: function() {
+ this.unloadMedia_();
+ this.dispatchEvent(new Event('error'));
+ },
+
+ /**
+ * This is called when any media data is updated and by the periodical timer
+ * is fired.
+ *
+ * @param {boolean} alive Media availability. False if it's unavailable.
+ * @private
+ */
+ onCastMediaUpdated_: function(alive) {
+ if (!this.castMedia_)
+ return;
+
+ var media = this.castMedia_;
+ if (this.currentMediaPlayerState_ != media.playerState) {
+ var oldPlayState = false;
+ var oldState = this.currentMediaPlayerState_;
+ if (oldState == chrome.cast.media.PlayerState.BUFFERING ||
+ oldState == chrome.cast.media.PlayerState.PLAYING) {
+ oldPlayState = true;
+ }
+ var newPlayState = false;
+ var newState = media.playerState;
+ if (newState == chrome.cast.media.PlayerState.BUFFERING ||
+ newState == chrome.cast.media.PlayerState.PLAYING) {
+ newPlayState = true;
+ }
+ if (!oldPlayState && newPlayState)
+ this.dispatchEvent(new Event('play'));
+ if (oldPlayState && !newPlayState)
+ this.dispatchEvent(new Event('pause'));
+
+ this.currentMediaPlayerState_ = newState;
+ }
+ if (this.currentMediaCurrentTime_ != media.getEstimatedTime()) {
+ this.dispatchEvent(new Event('timeupdate'));
+ this.currentMediaCurrentTime_ = media.getEstimatedTime();
+ }
+
+ if (this.currentMediaDuration_ != media.media.duration) {
+ this.dispatchEvent(new Event('durationchange'));
+ this.currentMediaDuration_ = media.media.duration;
+ }
+
+ // Media is being unloaded.
+ if (!alive) {
+ this.unloadMedia_();
+ return;
+ }
},
};
« no previous file with comments | « no previous file | ui/file_manager/video_player/js/cast/caster.js » ('j') | ui/file_manager/video_player/js/video_player.js » ('J')

Powered by Google App Engine
This is Rietveld 408576698