Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 'use strict'; | 5 'use strict'; |
| 6 | 6 |
| 7 // 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.
| |
| 8 var MEDIA_UPDATE_INTERVAL = 250; | |
| 9 | |
| 7 /** | 10 /** |
| 8 * This class is the dummy class which has same interface as VideoElement. This | 11 * This class is the dummy class which has same interface as VideoElement. This |
| 9 * behaves like VideoElement, and is used for making Chromecast player | 12 * behaves like VideoElement, and is used for making Chromecast player |
| 10 * controlled instead of the true Video Element tag. | 13 * controlled instead of the true Video Element tag. |
| 11 * | 14 * |
| 12 * @constructor | 15 * @constructor |
|
hirono
2014/07/24 02:12:36
nit: @param
yoshiki
2014/07/24 02:57:32
Done.
| |
| 13 */ | 16 */ |
| 14 function CastVideoElement() { | 17 function CastVideoElement(mediaInfo, session) { |
| 15 this.duration_ = null; | 18 this.mediaInfo_ = mediaInfo; |
| 19 this.castMedia_ = null; | |
| 20 this.castSession_ = session; | |
| 21 this.lastDuration_ = null; | |
| 16 this.currentTime_ = null; | 22 this.currentTime_ = null; |
| 17 this.src_ = ''; | 23 this.src_ = ''; |
| 18 this.volume_ = 100; | 24 this.volume_ = 100; |
| 25 this.currentMediaPlayerState_ = null; | |
| 26 this.currentMediaCurrentTime_ = null; | |
| 27 this.currentMediaDuration_ = null; | |
| 28 | |
| 29 this.onCastMediaUpdatedBound_ = this.onCastMediaUpdated_.bind(this); | |
| 19 } | 30 } |
| 20 | 31 |
| 21 CastVideoElement.prototype = { | 32 CastVideoElement.prototype = { |
| 22 __proto__: cr.EventTarget.prototype, | 33 __proto__: cr.EventTarget.prototype, |
| 23 | 34 |
| 24 /** | 35 /** |
| 25 * Returns a parent node. This must always be null. | 36 * Returns a parent node. This must always be null. |
| 26 * @return {Element} | 37 * @return {Element} |
| 27 */ | 38 */ |
| 28 get parentNode() { | 39 get parentNode() { |
| 29 return null; | 40 return null; |
| 30 }, | 41 }, |
| 31 | 42 |
| 32 /** | 43 /** |
| 33 * The total time of the video. | 44 * The total time of the video. |
|
hirono
2014/07/24 02:12:36
Comment about the unit of time is useful.
yoshiki
2014/07/24 02:57:32
Done.
| |
| 34 * @type {number} | 45 * @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.
| |
| 35 */ | 46 */ |
| 36 get duration() { | 47 get duration() { |
| 37 return this.duration_; | 48 if (this.castMedia_ && this.castMedia_.media) { |
| 49 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.
| |
| 50 return this.castMedia_.media.duration; | |
| 51 } | |
| 52 | |
| 53 return this.lastDuration_; | |
| 38 }, | 54 }, |
| 39 | 55 |
| 40 /** | 56 /** |
| 41 * The current timestamp of the video. | 57 * The current timestamp of the video. |
| 42 * @type {number} | 58 * @type {number} |
|
hirono
2014/07/24 02:12:36
@return {?number} ?
yoshiki
2014/07/24 02:57:32
Done.
| |
| 43 */ | 59 */ |
| 44 get currentTime() { | 60 get currentTime() { |
| 45 return this.currentTime_; | 61 return this.castMedia_ ? this.castMedia_.getEstimatedTime() : null; |
| 46 }, | 62 }, |
| 47 set currentTime(currentTime) { | 63 set currentTime(currentTime) { |
| 48 this.currentTime_ = currentTime; | 64 // 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.
| |
| 49 }, | 65 }, |
| 50 | 66 |
| 51 /** | 67 /** |
| 52 * If this video is pauses or not. | 68 * If this video is pauses or not. |
| 53 * @type {boolean} | 69 * @type {boolean} |
| 54 */ | 70 */ |
| 55 get paused() { | 71 get paused() { |
| 56 return false; | 72 if (!this.castMedia_) |
| 73 return false; | |
| 74 | |
| 75 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.
| |
| 57 }, | 76 }, |
| 58 | 77 |
| 59 /** | 78 /** |
| 60 * If this video is ended or not. | 79 * If this video is ended or not. |
| 61 * @type {boolean} | 80 * @type {boolean} |
| 62 */ | 81 */ |
| 63 get ended() { | 82 get ended() { |
| 64 return false; | 83 if (!this.castMedia_) |
| 84 return true; | |
| 85 | |
| 86 return this.castMedia_.idleReason == chrome.cast.media.IdleReason.FINISHED; | |
| 65 }, | 87 }, |
| 66 | 88 |
| 67 /** | 89 /** |
| 68 * If this video is seelable or not. | 90 * If this video is seelable or not. |
| 69 * @type {boolean} | 91 * @type {boolean} |
| 70 */ | 92 */ |
| 71 get seekable() { | 93 get seekable() { |
| 94 // TODO(yoshiki): Support seek. | |
| 72 return false; | 95 return false; |
| 73 }, | 96 }, |
| 74 | 97 |
| 75 /** | 98 /** |
| 76 * Value of the volume | 99 * Value of the volume |
| 77 * @type {number} | 100 * @type {number} |
| 78 */ | 101 */ |
| 79 get volume() { | 102 get volume() { |
| 80 return this.volume_; | 103 return this.castSession_.receiver.volume.muted ? |
| 104 0 : | |
| 105 this.castSession_.receiver.volume.level; | |
| 81 }, | 106 }, |
| 82 set volume(volume) { | 107 set volume(volume) { |
| 83 this.volume_ = volume; | 108 var VOLUME_EPS = 0.01; // Threshold for ignoring a small change. |
| 109 | |
| 110 // Ignores < 1% change. | |
| 111 if (Math.abs(this.castSession_.receiver.volume.level - volume) < VOLUME_EPS) | |
| 112 return; | |
| 113 | |
| 114 if (this.castSession_.receiver.volume.muted) { | |
| 115 if (volume < VOLUME_EPS) | |
| 116 return; | |
| 117 | |
| 118 // Unmute before setting volume. | |
| 119 this.castSession_.setReceiverMuted(false, | |
| 120 function() {}, | |
| 121 this.onCastCommandError_.wrap(this)); | |
| 122 | |
| 123 this.castSession_.setReceiverVolumeLevel(volume, | |
| 124 function() {}, | |
| 125 this.onCastCommandError_.wrap(this)); | |
| 126 } else { | |
| 127 if (volume < VOLUME_EPS) { | |
| 128 this.castSession_.setReceiverMuted(true, | |
| 129 function() {}, | |
| 130 this.onCastCommandError_.wrap(this)); | |
| 131 return; | |
| 132 } | |
| 133 | |
| 134 this.castSession_.setReceiverVolumeLevel(volume, | |
| 135 function() {}, | |
| 136 this.onCastCommandError_.wrap(this)); | |
| 137 } | |
| 84 }, | 138 }, |
| 85 | 139 |
| 86 /** | 140 /** |
| 87 * Returns the source of the current video. | 141 * Returns the source of the current video. |
| 88 * @return {string} | 142 * @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.
| |
| 89 */ | 143 */ |
| 90 get src() { | 144 get src() { |
| 91 return this.src_; | 145 return null; |
| 92 }, | 146 }, |
| 93 | 147 |
| 94 /** | 148 /** |
| 95 * Plays the video. | 149 * Plays the video. |
| 96 */ | 150 */ |
| 97 play: function() { | 151 play: function() { |
| 98 // TODO(yoshiki): Implement this. | 152 if (!this.castMedia_) { |
| 153 this.load(function() { | |
| 154 this.castMedia_.play(null, | |
| 155 function () {}, | |
| 156 this.onCastCommandError_.wrap(this)); | |
| 157 }.wrap(this)); | |
| 158 return; | |
| 159 } | |
| 160 | |
| 161 this.castMedia_.play(null, | |
| 162 function () {}, | |
| 163 this.onCastCommandError_.wrap(this)); | |
| 99 }, | 164 }, |
| 100 | 165 |
| 101 /** | 166 /** |
| 102 * Pauses the video. | 167 * Pauses the video. |
| 103 */ | 168 */ |
| 104 pause: function() { | 169 pause: function() { |
| 105 // TODO(yoshiki): Implement this. | 170 if (!this.castMedia_) |
| 171 return; | |
| 172 | |
| 173 this.castMedia_.pause(null, | |
| 174 function () {}, | |
| 175 this.onCastCommandError_.wrap(this)); | |
| 106 }, | 176 }, |
| 107 | 177 |
| 108 /** | 178 /** |
| 109 * Loads the video. | 179 * Loads the video. |
| 110 */ | 180 */ |
| 111 load: function() { | 181 load: function(opt_callback) { |
| 112 // TODO(yoshiki): Implement this. | 182 var request = new chrome.cast.media.LoadRequest(this.mediaInfo_); |
| 183 this.castSession_.loadMedia(request, | |
| 184 function(media) { | |
| 185 this.onMediaDiscovered_(media); | |
| 186 if (opt_callback) | |
| 187 opt_callback(); | |
| 188 }.bind(this), | |
| 189 this.onCastCommandError_.wrap(this)); | |
| 190 }, | |
| 191 | |
| 192 /** | |
| 193 * Unloads the video. | |
| 194 * @private | |
| 195 */ | |
| 196 unloadMedia_: function() { | |
| 197 this.castMedia_.removeUpdateListener(this.onCastMediaUpdatedBound_); | |
| 198 this.castMedia_ = null; | |
| 199 clearInterval(this.updateTimerId_); | |
| 200 }, | |
| 201 | |
| 202 /** | |
| 203 * This method is called periodically to update media information while the | |
| 204 * media is loaded. | |
| 205 * @private | |
| 206 */ | |
| 207 onPeriodicalUpdateTimer_: function() { | |
| 208 if (!this.castMedia_) | |
| 209 return; | |
| 210 | |
| 211 if (this.castMedia_.playerState == chrome.cast.media.PlayerState.PLAYING) | |
| 212 this.onCastMediaUpdated_(true); | |
| 213 }, | |
| 214 | |
| 215 /** | |
| 216 * This method should be called when a media file is loaded. | |
| 217 * @param {chrome.cast.Media} media Media object which was discovered. | |
| 218 * @private | |
| 219 */ | |
| 220 onMediaDiscovered_: function(media) { | |
| 221 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.
| |
| 222 this.onCastMediaUpdated_(true); | |
| 223 media.addUpdateListener(this.onCastMediaUpdatedBound_); | |
| 224 this.updateTimerId_ = setInterval(this.onPeriodicalUpdateTimer_.bind(this), | |
| 225 MEDIA_UPDATE_INTERVAL); | |
| 226 }, | |
| 227 | |
| 228 /** | |
| 229 * This method should be called when a media command to cast is failed. | |
| 230 * @private | |
| 231 */ | |
| 232 onCastCommandError_: function() { | |
| 233 this.unloadMedia_(); | |
| 234 this.dispatchEvent(new Event('error')); | |
| 235 }, | |
| 236 | |
| 237 /** | |
| 238 * This is called when any media data is updated and by the periodical timer | |
| 239 * is fired. | |
| 240 * | |
| 241 * @param {boolean} alive Media availability. False if it's unavailable. | |
| 242 * @private | |
| 243 */ | |
| 244 onCastMediaUpdated_: function(alive) { | |
| 245 if (!this.castMedia_) | |
| 246 return; | |
| 247 | |
| 248 var media = this.castMedia_; | |
| 249 if (this.currentMediaPlayerState_ != media.playerState) { | |
| 250 var oldPlayState = false; | |
| 251 var oldState = this.currentMediaPlayerState_; | |
| 252 if (oldState == chrome.cast.media.PlayerState.BUFFERING || | |
| 253 oldState == chrome.cast.media.PlayerState.PLAYING) { | |
| 254 oldPlayState = true; | |
| 255 } | |
| 256 var newPlayState = false; | |
| 257 var newState = media.playerState; | |
| 258 if (newState == chrome.cast.media.PlayerState.BUFFERING || | |
| 259 newState == chrome.cast.media.PlayerState.PLAYING) { | |
| 260 newPlayState = true; | |
| 261 } | |
| 262 if (!oldPlayState && newPlayState) | |
| 263 this.dispatchEvent(new Event('play')); | |
| 264 if (oldPlayState && !newPlayState) | |
| 265 this.dispatchEvent(new Event('pause')); | |
| 266 | |
| 267 this.currentMediaPlayerState_ = newState; | |
| 268 } | |
| 269 if (this.currentMediaCurrentTime_ != media.getEstimatedTime()) { | |
| 270 this.dispatchEvent(new Event('timeupdate')); | |
| 271 this.currentMediaCurrentTime_ = media.getEstimatedTime(); | |
| 272 } | |
| 273 | |
| 274 if (this.currentMediaDuration_ != media.media.duration) { | |
| 275 this.dispatchEvent(new Event('durationchange')); | |
| 276 this.currentMediaDuration_ = media.media.duration; | |
| 277 } | |
| 278 | |
| 279 // Media is being unloaded. | |
| 280 if (!alive) { | |
| 281 this.unloadMedia_(); | |
| 282 return; | |
| 283 } | |
| 113 }, | 284 }, |
| 114 }; | 285 }; |
| OLD | NEW |