Skip to content

Commit

Permalink
v2 Chromecast support
Browse files Browse the repository at this point in the history
This introduces Chromecast support directly in the v2 library, as well
as in the demo app.

See the included design doc for details.

Issue #261

Change-Id: I26a707e7fa6bd829c3ebc70e4c9345ec25eed000
  • Loading branch information
joeyparrish committed Jul 6, 2016
1 parent d68e8dd commit 0c8f744
Show file tree
Hide file tree
Showing 30 changed files with 5,030 additions and 156 deletions.
6 changes: 6 additions & 0 deletions build/types/cast
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Cast support.

+../../lib/cast/cast_proxy.js
+../../lib/cast/cast_receiver.js
+../../lib/cast/cast_sender.js
+../../lib/cast/cast_utils.js
1 change: 1 addition & 0 deletions build/types/complete
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# The complete library, containing every available plugin.

+@cast
+@networking
+@manifests
+@polyfill
Expand Down
51 changes: 12 additions & 39 deletions demo/asset_section.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ shakaDemo.preparePlayer_ = function(asset) {
var player = shakaDemo.player_;

var config = /** @type {shakaExtern.PlayerConfiguration} */(
{ abr: {}, drm: {}, manifest: { dash: {} } });
{ abr: {}, manifest: { dash: {} } });
config.manifest.dash.clockSyncUri =
'//shaka-player-demo.appspot.com/time.txt';

Expand All @@ -137,13 +137,14 @@ shakaDemo.preparePlayer_ = function(asset) {
});
}

player.resetConfiguration();

// Add config from this asset.
if (asset.licenseServers)
config.drm.servers = asset.licenseServers;
if (asset.drmCallback)
config.manifest.dash.customScheme = asset.drmCallback;
if (asset.clearKeys)
config.drm.clearKeys = asset.clearKeys;
ShakaDemoUtils.setupAssetMetadata(asset, player);
shakaDemo.castProxy_.setAppData({
'asset': asset,
'isYtDrm': asset.drmCallback == shakaAssets.YouTubeCallback
});

// Add configuration from the UI.
config.preferredAudioLanguage =
Expand All @@ -153,23 +154,8 @@ shakaDemo.preparePlayer_ = function(asset) {
config.abr.enabled =
document.getElementById('enableAdaptation').checked;

player.resetConfiguration();
player.configure(config);

// Configure network filters.
var networkingEngine = player.getNetworkingEngine();
networkingEngine.clearAllRequestFilters();
networkingEngine.clearAllResponseFilters();

if (asset.licenseRequestHeaders) {
var filter = shakaDemo.addLicenseRequestHeaders_.bind(
null, asset.licenseRequestHeaders);
networkingEngine.registerRequestFilter(filter);
}

if (asset.licenseProcessor) {
networkingEngine.registerResponseFilter(asset.licenseProcessor);
}
return asset;
};

Expand All @@ -184,6 +170,10 @@ shakaDemo.load = function() {

// Load the manifest.
player.load(asset.manifestUri).then(function() {
// Disallow casting of offline content.
var isOffline = asset.manifestUri.indexOf('offline:') == 0;
shakaDemo.controls_.allowCast(!isOffline);

(asset.extraText || []).forEach(function(extraText) {
player.addTextTrack(extraText.uri, extraText.language, extraText.kind,
extraText.mime, extraText.codecs);
Expand All @@ -197,20 +187,3 @@ shakaDemo.load = function() {
}
});
};


/**
* @param {!Object.<string, string>} headers
* @param {shaka.net.NetworkingEngine.RequestType} requestType
* @param {shakaExtern.Request} request
* @private
*/
shakaDemo.addLicenseRequestHeaders_ = function(headers, requestType, request) {
if (requestType != shaka.net.NetworkingEngine.RequestType.LICENSE) return;

// Add these to the existing headers. Do not clobber them!
// For PlayReady, there will already be headers in the request.
for (var k in headers) {
request.headers[k] = headers[k];
}
};
12 changes: 12 additions & 0 deletions demo/controls.css
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
#videoContainer:-ms-fullscreen { width: 100%; height: 100%; }

#controls button {
color: white;
height: 32px;
width: 32px;
padding: 0;
Expand Down Expand Up @@ -142,6 +143,17 @@
}


/* Always show controls while casting */
#controls.casting {
opacity: 1;
}

/* Hide fullscreen button while casting */
#controls.casting #fullscreenButton {
display: none;
}


/* NOTE: pseudo-elements for different browsers can't be combined with commas.
* Browsers will ignore styles if any pseudo-element in the list is unknown.
*/
Expand Down
93 changes: 76 additions & 17 deletions demo/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,16 @@
* @suppress {missingProvide}
*/
function ShakaControls() {
/** @private {HTMLVideoElement} */
/** @private {shaka.cast.CastProxy} */
this.castProxy_ = null;

/** @private {boolean} */
this.castAllowed_ = true;

/** @private {?function(!shaka.util.Error)} */
this.onError_ = null;

/** @private {HTMLMediaElement} */
this.video_ = null;

/** @private {shaka.Player} */
Expand Down Expand Up @@ -88,15 +97,15 @@ function ShakaControls() {

/**
* Initializes the player controls.
* @param {HTMLVideoElement} video
* @param {shaka.Player} player
* @param {shaka.cast.CastProxy} castProxy
* @param {function(!shaka.util.Error)} onError
* @param {function(boolean)} notifyCastStatus
*/
ShakaControls.prototype.init = function(video, player) {
// TODO: Make these resettable for switching to/from Chromecast
this.video_ = video;
this.player_ = player;
// TODO: Method to tear these down
// TODO: Event manager?
ShakaControls.prototype.init = function(castProxy, onError, notifyCastStatus) {
this.castProxy_ = castProxy;
this.onError_ = onError;
this.notifyCastStatus_ = notifyCastStatus;
this.initMinimal(castProxy.getVideo(), castProxy.getPlayer());

// IE11 doesn't treat the 'input' event correctly.
// https://connect.microsoft.com/IE/Feedback/Details/856998
Expand Down Expand Up @@ -146,8 +155,6 @@ ShakaControls.prototype.init = function(video, player) {
this.fullscreenButton_.addEventListener(
'click', this.onFullscreenClick_.bind(this));

window.setInterval(this.updateTimeAndSeekRange_.bind(this), 125);

this.currentTime_.addEventListener(
'click', this.onCurrentTimeClick_.bind(this));

Expand All @@ -159,9 +166,6 @@ ShakaControls.prototype.init = function(video, player) {
this.castButton_.addEventListener(
'click', this.onCastClick_.bind(this));

this.player_.addEventListener(
'buffering', this.onBufferingStateChange_.bind(this));

this.videoContainer_.addEventListener(
'click', this.onPlayPauseClick_.bind(this));

Expand All @@ -180,6 +184,35 @@ ShakaControls.prototype.init = function(video, player) {
'mousemove', this.onMouseMove_.bind(this));
this.videoContainer_.addEventListener(
'mouseout', this.onMouseOut_.bind(this));

this.castProxy_.addEventListener(
'caststatuschanged', this.onCastStatusChange_.bind(this));
};


/**
* Initializes minimal player controls. Used on both sender (indirectly) and
* receiver (directly).
* @param {HTMLMediaElement} video
* @param {shaka.Player} player
*/
ShakaControls.prototype.initMinimal = function(video, player) {
this.video_ = video;
this.player_ = player;
this.player_.addEventListener(
'buffering', this.onBufferingStateChange_.bind(this));
window.setInterval(this.updateTimeAndSeekRange_.bind(this), 125);
};


/**
* This allows the application to inhibit casting.
*
* @param {boolean} allow
*/
ShakaControls.prototype.allowCast = function(allow) {
this.castAllowed_ = allow;
this.onCastStatusChange_(null);
};


Expand Down Expand Up @@ -291,7 +324,7 @@ ShakaControls.prototype.onSeekInput_ = function() {
/** @private */
ShakaControls.prototype.onSeekInputTimeout_ = function() {
this.seekTimeoutId_ = null;
this.video_.currentTime = this.seekBar_.value;
this.video_.currentTime = parseFloat(this.seekBar_.value);
};


Expand Down Expand Up @@ -338,7 +371,7 @@ ShakaControls.prototype.onVolumeStateChange_ = function() {

/** @private */
ShakaControls.prototype.onVolumeInput_ = function() {
this.video_.volume = this.volumeBar_.value;
this.video_.volume = parseFloat(this.volumeBar_.value);
this.video_.muted = false;
};

Expand Down Expand Up @@ -411,7 +444,33 @@ ShakaControls.prototype.onFastForwardClick_ = function() {

/** @private */
ShakaControls.prototype.onCastClick_ = function() {
// TODO: cast, cast_connected
if (this.castProxy_.isCasting()) {
this.castProxy_.disconnect();
} else {
// TODO: disable other controls while connecting?
this.castProxy_.cast().then(function() {
// Success!
}, function(error) {
if (error.code != shaka.util.Error.Code.CAST_CANCELED_BY_USER) {
this.onError_(error);
}
}.bind(this));
}
};


/**
* @param {Event} event
* @private
*/
ShakaControls.prototype.onCastStatusChange_ = function(event) {
var canCast = this.castProxy_.canCast() && this.castAllowed_;
var isCasting = this.castProxy_.isCasting();

this.notifyCastStatus_(isCasting);
this.castButton_.style.display = canCast ? 'inherit' : 'none';
this.castButton_.textContent = isCasting ? 'cast_connected' : 'cast';
this.controls_.classList.toggle('casting', this.castProxy_.isCasting());
};


Expand Down
72 changes: 72 additions & 0 deletions demo/demo_utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @license
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


/** @namespace */
var ShakaDemoUtils = {};


/**
* @param {shakaAssets.AssetInfo} asset
* @param {shaka.Player} player
*/
ShakaDemoUtils.setupAssetMetadata = function(asset, player) {
var config = /** @type {shakaExtern.PlayerConfiguration} */(
{ drm: {}, manifest: { dash: {} } });

// Add config from this asset.
if (asset.licenseServers)
config.drm.servers = asset.licenseServers;
if (asset.drmCallback)
config.manifest.dash.customScheme = asset.drmCallback;
if (asset.clearKeys)
config.drm.clearKeys = asset.clearKeys;
player.configure(config);

// Configure network filters.
var networkingEngine = player.getNetworkingEngine();
networkingEngine.clearAllRequestFilters();
networkingEngine.clearAllResponseFilters();

if (asset.licenseRequestHeaders) {
var filter = ShakaDemoUtils.addLicenseRequestHeaders_.bind(
null, asset.licenseRequestHeaders);
networkingEngine.registerRequestFilter(filter);
}

if (asset.licenseProcessor) {
networkingEngine.registerResponseFilter(asset.licenseProcessor);
}
};


/**
* @param {!Object.<string, string>} headers
* @param {shaka.net.NetworkingEngine.RequestType} requestType
* @param {shakaExtern.Request} request
* @private
*/
ShakaDemoUtils.addLicenseRequestHeaders_ =
function(headers, requestType, request) {
if (requestType != shaka.net.NetworkingEngine.RequestType.LICENSE) return;

// Add these to the existing headers. Do not clobber them!
// For PlayReady, there will already be headers in the request.
for (var k in headers) {
request.headers[k] = headers[k];
}
};
Binary file added demo/idle1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/idle2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demo/idle3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<script src="//www.gstatic.com/cv/js/sender/v1/cast_sender.js"></script>
<script src="load.js"></script>
<script src="assets.js"></script>
<script src="demo_utils.js"></script>
<script src="main.js"></script>
<script src="asset_section.js"></script>
<script src="configuration_section.js"></script>
Expand Down
Loading

0 comments on commit 0c8f744

Please sign in to comment.