diff --git a/demo/controls.css b/demo/controls.css
index 26a86ff57a..feaf4f6584 100644
--- a/demo/controls.css
+++ b/demo/controls.css
@@ -100,6 +100,21 @@
cursor: pointer;
}
+#castReceiverName {
+ display: none;
+
+ background-color: rgba(0, 0, 0, 0.5);
+ color: white;
+ font-size: 150%;
+ padding: 5px;
+
+ bottom: 50px;
+ left: 0;
+ right: 0;
+ margin: auto;
+ width: max-content;
+}
+
#giantPlayButtonContainer {
margin: auto;
width: 200px;
diff --git a/demo/controls.js b/demo/controls.js
index 03ba52987d..4314d680b3 100644
--- a/demo/controls.js
+++ b/demo/controls.js
@@ -74,6 +74,9 @@ function ShakaControls() {
/** @private {Element} */
this.castButton_ = document.getElementById('castButton');
+ /** @private {Element} */
+ this.castReceiverName_ = document.getElementById('castReceiverName');
+
/** @private {Element} */
this.bufferingSpinner_ = document.getElementById('bufferingSpinner');
@@ -477,6 +480,10 @@ ShakaControls.prototype.onCastStatusChange_ = function(event) {
this.notifyCastStatus_(isCasting);
this.castButton_.style.display = canCast ? 'inherit' : 'none';
this.castButton_.textContent = isCasting ? 'cast_connected' : 'cast';
+ this.castReceiverName_.style.display =
+ isCasting ? 'inherit' : 'none';
+ this.castReceiverName_.textContent =
+ isCasting ? 'Casting to ' + this.castProxy_.receiverName() : '';
this.controls_.classList.toggle('casting', this.castProxy_.isCasting());
};
diff --git a/demo/index.html b/demo/index.html
index d238750425..50bcfbaccb 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -169,6 +169,7 @@
diff --git a/externs/chromecast.js b/externs/chromecast.js
index dcb8901b3f..a1c7817054 100644
--- a/externs/chromecast.js
+++ b/externs/chromecast.js
@@ -233,6 +233,18 @@ chrome.cast.Error.prototype.details;
+/**
+ * @constructor
+ * @struct
+ */
+chrome.cast.Receiver = function() {};
+
+
+/** @const {string} */
+chrome.cast.Receiver.prototype.friendlyName;
+
+
+
/**
* @constructor
* @struct
@@ -248,6 +260,10 @@ chrome.cast.Session.prototype.sessionId;
chrome.cast.Session.prototype.status;
+/** @type {chrome.cast.Receiver} */
+chrome.cast.Session.prototype.receiver;
+
+
/**
* @param {string} namespace
* @param {Function} listener
diff --git a/lib/cast/cast_proxy.js b/lib/cast/cast_proxy.js
index 2b453b34e3..952cb5b41c 100644
--- a/lib/cast/cast_proxy.js
+++ b/lib/cast/cast_proxy.js
@@ -157,6 +157,15 @@ shaka.cast.CastProxy.prototype.isCasting = function() {
};
+/**
+ * @return {string} The name of the Cast receiver device, if isCasting().
+ * @export
+ */
+shaka.cast.CastProxy.prototype.receiverName = function() {
+ return this.sender_ ? this.sender_.receiverName() : '';
+};
+
+
/**
* @return {!Promise} Resolved when connected to a receiver. Rejected if the
* connection fails or is canceled by the user.
diff --git a/lib/cast/cast_sender.js b/lib/cast/cast_sender.js
index 903ab12a8a..b2b4d5380b 100644
--- a/lib/cast/cast_sender.js
+++ b/lib/cast/cast_sender.js
@@ -61,6 +61,9 @@ shaka.cast.CastSender =
/** @private {boolean} */
this.isCasting_ = false;
+ /** @private {string} */
+ this.receiverName_ = '';
+
/** @private {Object} */
this.appData_ = null;
@@ -128,6 +131,14 @@ shaka.cast.CastSender.prototype.isCasting = function() {
};
+/**
+ * @return {string} The name of the Cast receiver device, if isCasting().
+ */
+shaka.cast.CastSender.prototype.receiverName = function() {
+ return this.receiverName_;
+};
+
+
/**
* @return {boolean} True if we have a cache of remote properties from the
* receiver.
@@ -449,6 +460,7 @@ shaka.cast.CastSender.prototype.onConnectionStatusChanged_ = function() {
}
this.isCasting_ = connected;
+ this.receiverName_ = connected ? this.session_.receiver.friendlyName : '';
this.onStatusChanged_();
};
diff --git a/test/cast/cast_proxy_unit.js b/test/cast/cast_proxy_unit.js
index 55bc7ae108..71acd299c2 100644
--- a/test/cast/cast_proxy_unit.js
+++ b/test/cast/cast_proxy_unit.js
@@ -100,6 +100,15 @@ describe('CastProxy', function() {
});
});
+ describe('receiverName', function() {
+ it('delegates directly to the sender', function() {
+ mockSender.receiverName.and.returnValue('abc');
+ expect(proxy.receiverName()).toBe('abc');
+ mockSender.receiverName.and.returnValue('xyz');
+ expect(proxy.receiverName()).toBe('xyz');
+ });
+ });
+
describe('setAppData', function() {
it('delegates directly to the sender', function() {
var fakeAppData = {key: 'value'};
@@ -645,6 +654,7 @@ describe('CastProxy', function() {
apiReady: jasmine.createSpy('apiReady'),
hasReceivers: jasmine.createSpy('hasReceivers'),
isCasting: jasmine.createSpy('isCasting'),
+ receiverName: jasmine.createSpy('receiverName'),
hasRemoteProperties: jasmine.createSpy('hasRemoteProperties'),
setAppData: jasmine.createSpy('setAppData'),
disconnect: jasmine.createSpy('disconnect'),
diff --git a/test/cast/cast_sender_unit.js b/test/cast/cast_sender_unit.js
index 7c1c351866..acf0b0299b 100644
--- a/test/cast/cast_sender_unit.js
+++ b/test/cast/cast_sender_unit.js
@@ -619,6 +619,7 @@ describe('CastSender', function() {
var session = {
messages: [],
status: 'connected',
+ receiver: { friendlyName: 'SomeDevice' },
addUpdateListener: jasmine.createSpy('Session.addUpdateListener'),
addMessageListener: jasmine.createSpy('Session.addMessageListener'),
sendMessage: jasmine.createSpy('Session.sendMessage'),