Skip to content

Commit

Permalink
Video Player: Support Media Router to cast Drive videos.
Browse files Browse the repository at this point in the history
This CL includes following changes to support both media router and legacy cast extensions to cast Drive video.
- Added media router's component extensions to the cast extension list we use.
- when we use media router, we use a cast button which requests a cast session via shared cast dialog, instead of dropdown cast menu button .
This CL includes some minor bug fixes. (Added inline comments for them)

BUG=605390
TEST=enable media-router from chrome://flags and cast video from video player.

Review-Url: https://codereview.chromium.org/1987173002
Cr-Commit-Position: refs/heads/master@{#394737}
  • Loading branch information
fukino authored and Commit bot committed May 19, 2016
1 parent 5582243 commit 54e8895
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 15 deletions.
8 changes: 7 additions & 1 deletion ui/file_manager/externs/chrome_cast.js
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,13 @@ chrome.cast.ReceiverActionListener;
/**
* @type {boolean}
*/
chrome.cast.isAvailable = false;
chrome.cast.isAvailable;


/**
* @type {boolean}
*/
chrome.cast.usingPresentationApi;


/**
Expand Down
9 changes: 6 additions & 3 deletions ui/file_manager/video_player/css/media_controls.css
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ paper-slider.volume {

/* Cast button. */

.media-button.cast {
.media-button.cast,
.media-button.cast-button {
background-image: -webkit-image-set(
url(../images/media/media_chromecast.png) 1x,
url(../images/media/2x/media_chromecast.png) 2x);
Expand All @@ -147,11 +148,13 @@ paper-slider.volume {
outline: none;
}

#video-player[cast-available][castable] .media-button.cast {
#video-player[cast-available][castable] .media-button.cast,
#video-player[mr-cast-available][castable] .media-button.cast-button {
display: block;
}

#video-player[casting] .media-button.cast {
#video-player[casting] .media-button.cast,
#video-player[casting] .media-button.cast-button {
background-image: -webkit-image-set(
url(../images/media/media_chromecast_casting.png) 1x,
url(../images/media/2x/media_chromecast_casting.png) 2x);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ function CastExtensionDiscoverer() {}
* @const
*/
CastExtensionDiscoverer.CAST_EXTENSION_IDS = [
'pkedcjkdefgpdelpbcmbmeomcjbeemfm', // MR official
'boadgeojelhgndaghljhdicfkmllpafd', // release
'dliochdbjfkdbacpmhlcpmleaejidimm', // beta
'hfaagokkkhdbgiakmmlclaapfelnkoah',
'fmfcbgogabcbclcofgocippekhfcmgfj',
'enhhojjnijigcajfphajepfemndkmdlo'
'enhhojjnijigcajfphajepfemndkmdlo', // dev
'fmfcbgogabcbclcofgocippekhfcmgfj', // staging
'fjhoaacokmgbjemoflkofnenfaiekifl' // stable used during MR development.
];

/**
Expand Down
4 changes: 3 additions & 1 deletion ui/file_manager/video_player/js/cast/cast_video_element.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,10 @@ CastVideoElement.prototype = /** @struct */ {
function() {},
function(error) {
// Ignores session error, since session may already be closed.
if (error.code !== chrome.cast.ErrorCode.SESSION_ERROR)
if (error.code !== chrome.cast.ErrorCode.SESSION_ERROR &&
error.code !== chrome.cast.ErrorCode.INVALID_PARAMETER) {
this.onCastCommandError_(error);
}
}.wrap(this));

this.castMedia_.removeUpdateListener(this.onCastMediaUpdatedBound_);
Expand Down
6 changes: 6 additions & 0 deletions ui/file_manager/video_player/js/cast/caster.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ function initializeApi() {
* @param {Array<Object>} receivers List of casts.
*/
function onReceiver(availability, receivers) {
if (chrome.cast.usingPresentationApi) {
player.setCastAvailability(
availability === chrome.cast.ReceiverAvailability.AVAILABLE);
return;
}

if (availability === chrome.cast.ReceiverAvailability.AVAILABLE) {
if (!receivers) {
console.error('Receiver list is empty.');
Expand Down
6 changes: 5 additions & 1 deletion ui/file_manager/video_player/js/media_controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ function VideoControls(
this.initVolumeControls();
this.initSubtitlesButton();

// Create the cast button.
// Create the cast menu button.
// We need to use <button> since cr.ui.MenuButton.decorate modifies prototype
// chain, by which <files-icon-button> will not work correctly.
// TODO(fukino): Find a way to use files-icon-button consistently.
Expand All @@ -873,6 +873,10 @@ function VideoControls(
this.castButton_.appendChild(document.createElement('files-ripple'));
cr.ui.decorate(this.castButton_, cr.ui.MenuButton);

// Create the cast button, which is a normal button and is used when we cast
// videos usign Media Router.
this.createButton('cast-button');

if (opt_fullScreenToggle) {
this.fullscreenButton_ =
this.createButton('fullscreen', opt_fullScreenToggle);
Expand Down
80 changes: 74 additions & 6 deletions ui/file_manager/video_player/js/video_player.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,10 @@ VideoPlayer.prototype.prepare = function(videos) {
else
videoPlayerElement.removeAttribute('multiple');

var castButton = queryRequiredElement('.cast-button');
castButton.addEventListener('click',
this.onCastButtonClicked_.wrap(this));

document.addEventListener('keydown', reloadVideo);
document.addEventListener('click', reloadVideo);
};
Expand Down Expand Up @@ -359,8 +363,6 @@ VideoPlayer.prototype.loadVideo_ = function(video, opt_callback) {
if (this.currentCast_) {
metrics.recordPlayType(metrics.PLAY_TYPE.CAST);

videoPlayerElement.setAttribute('casting', true);

getRequiredElement('cast-name').textContent =
this.currentCast_.friendlyName;

Expand All @@ -372,9 +374,14 @@ VideoPlayer.prototype.loadVideo_ = function(video, opt_callback) {
return Promise.reject('No casts are available.');

return new Promise(function(fulfill, reject) {
chrome.cast.requestSession(
fulfill, reject, undefined, this.currentCast_.label);
if (this.currentSession_) {
fulfill(this.currentSession_);
} else {
chrome.cast.requestSession(
fulfill, reject, undefined, this.currentCast_.label);
}
}.bind(this)).then(function(session) {
videoPlayerElement.setAttribute('casting', true);
session.addUpdateListener(this.onCastSessionUpdateBound_);

this.currentSession_ = session;
Expand Down Expand Up @@ -503,7 +510,12 @@ VideoPlayer.prototype.unloadVideo = function(opt_keepSession) {
this.videoElement_ = null;

if (!opt_keepSession && this.currentSession_) {
this.currentSession_.stop(callback, callback);
// We should not request stop() if the current session is not connected to
// the receiver.
if (this.currentSession_.status === chrome.cast.SessionStatus.CONNECTED)
this.currentSession_.stop(callback, callback);
else
setTimeout(callback);
this.currentSession_.removeUpdateListener(this.onCastSessionUpdateBound_);
this.currentSession_ = null;
} else {
Expand Down Expand Up @@ -652,6 +664,50 @@ VideoPlayer.prototype.setCastList = function(casts) {
videoPlayerElement.setAttribute('cast-available', true);
};

/**
* Tells the availability of cast receivers to VideoPlayeru topdate the
* visibility of the cast button..
* @param {boolean} available Whether at least one cast receiver is available.
*/
VideoPlayer.prototype.setCastAvailability = function(available) {
var videoPlayerElement = getRequiredElement('video-player');
if (available) {
videoPlayerElement.setAttribute('mr-cast-available', true);
} else {
videoPlayerElement.removeAttribute('mr-cast-available');
if (this.currentCast_)
this.onCurrentCastDisappear_();
}
};

/**
* Handles click event on cast button to request a session.
* @private
*/
VideoPlayer.prototype.onCastButtonClicked_ = function() {
// This method is called only when Media Router is enabled.
// In this case, requestSession() will open a built-in dialog (not a dropdown
// menu) to choose the receiver, and callback is called with the session
// object after user's operation..
chrome.cast.requestSession(
function(session) {
this.unloadVideo(true);
this.loadQueue_.run(function(callback) {
this.currentCast_ = {
label: session.receiver.label,
friendlyName: session.receiver.friendlyName
};
this.currentSession_ = session;
this.reloadCurrentVideo();
callback();
}.bind(this));
}.bind(this),
function(error) {
if (error.code !== chrome.cast.ErrorCode.CANCEL)
console.error('requestSession from cast button failed', error);
});
};

/**
* Updates the check status of the cast menu items.
* @private
Expand Down Expand Up @@ -696,8 +752,20 @@ VideoPlayer.prototype.onCurrentCastDisappear_ = function() {
* @private
*/
VideoPlayer.prototype.onCastSessionUpdate_ = function(alive) {
if (!alive)
if (!alive) {
var videoPlayerElement = getRequiredElement('video-player');
videoPlayerElement.removeAttribute('casting');

// Loads the current video in local player.
this.unloadVideo();
this.loadQueue_.run(function(callback) {
this.currentCast_ = null;
if (!chrome.cast.usingPresentationApi)
this.updateCheckOnCastMenu_();
this.reloadCurrentVideo();
callback();
}.wrap(this));
}
};

/**
Expand Down

0 comments on commit 54e8895

Please sign in to comment.