This page contains code snippets and descriptions of the features available for a Custom Web Receiver app.
- A
cast-media-player
element that represents the built-in player UI provided with Web Receiver. - Custom CSS-like styling for the
cast-media-player
element to style various UI elements such as thebackground-image
,splash-image
, andfont-family
. - A script element to load the Web Receiver framework.
- JavaScript code to intercepting messages and handling events.
- Queue for autoplay.
- Options to configure playback.
- Options to set the Web Receiver context.
- Options to set commands which are supported by the Web Receiver app.
- A JavaScript call to start the Web Receiver application.
Application configuration and options
Configure the application
The
CastReceiverContext
is the outermost class exposed to the developer, and it manages loading of
underlying libraries and handles initialization of the Web Receiver SDK. The SDK
provides APIs that allow application developers to configure the SDK through
CastReceiverOptions
.
These configurations are evaluated once per application launch and are passed to
the SDK when setting the optional parameter in the call to
start
.
The example below shows how to override the default behavior for detecting if a
sender connection is still actively connected. When the Web Receiver has not
been able to communicate with a sender for
maxInactivity
seconds, a SENDER_DISCONNECTED
event is dispatched. The configuration below
overrides this timeout. This can be useful when debugging issues as it prevents
the Web Receiver app from closing the Chrome Remote Debugger session when there
are zero connected senders in an IDLE
state.
const context = cast.framework.CastReceiverContext.getInstance();
const options = new cast.framework.CastReceiverOptions();
options.maxInactivity = 3600; // Development only
context.start(options);
Configure the player
When loading content, the Web Receiver SDK provides a way to configure playback
variables such as DRM
information,
retry configurations, and request handlers using
cast.framework.PlaybackConfig
.
This information is handled by
PlayerManager
and is evaluated at the time that the players are created. Players are created
each time a new load is passed to the Web Receiver SDK. Modifications to the
PlaybackConfig
after the player has been created are evaluated in the next
content load. The SDK provides the following methods for modifying the
PlaybackConfig
.
CastReceiverOptions.playbackConfig
to override the default configuration options when initializing theCastReceiverContext
.PlayerManager.getPlaybackConfig()
to obtain the current configuration.PlayerManager.setPlaybackConfig()
to override the current configuration. This setting is applied to all subsequent loads or until it is overridden again.PlayerManager.setMediaPlaybackInfoHandler()
to apply additional configurations only for the media item being loaded on top of the current configurations. The handler is called just before player creation. Changes made here are not permanent and aren't included in queries togetPlaybackConfig()
. When the next media item is loaded, this handler is called again.
The example below shows how to set the PlaybackConfig
when initializing the
CastReceiverContext
. The configuration overrides outgoing requests for
obtaining manifests. The handler specifies that CORS Access-Control requests
should be made using credentials such as cookies or authorization headers.
const playbackConfig = new cast.framework.PlaybackConfig();
playbackConfig.manifestRequestHandler = requestInfo => {
requestInfo.withCredentials = true;
};
context.start({playbackConfig: playbackConfig});
The example below shows how to override the PlaybackConfig
using the getter
and setter provided in PlayerManager
. The setting configures the player to
resume content playback after 1 segment has been loaded.
const playerManager =
cast.framework.CastReceiverContext.getInstance().getPlayerManager();
const playbackConfig = (Object.assign(
new cast.framework.PlaybackConfig(), playerManager.getPlaybackConfig()));
playbackConfig.autoResumeNumberOfSegments = 1;
playerManager.setPlaybackConfig(playbackConfig);
The example below shows how to override the PlaybackConfig
for a specific load
request using the media playback info handler. The handler calls an application
implemented method getLicenseUrlForMedia
to obtain the licenseUrl
from the
current item's contentId
.
playerManager.setMediaPlaybackInfoHandler((loadRequestData, playbackConfig) => {
const mediaInformation = loadRequestData.media;
playbackConfig.licenseUrl = getLicenseUrlForMedia(mediaInformation.contentId);
return playbackConfig;
});
Event listener
The Web Receiver SDK allows your Web Receiver app to handle player events. The
event listener takes a
cast.framework.events.EventType
parameter (or an array of these parameters) that specifies the event(s) that
should trigger the listener. Preconfigured arrays of
cast.framework.events.EventType
that are useful for debugging can be found in
cast.framework.events.category
.
The event parameter provides additional information about the event.
For example, if you want to know when a
mediaStatus
change is being broadcasted, you can use the following logic to handle the
event:
const playerManager =
cast.framework.CastReceiverContext.getInstance().getPlayerManager();
playerManager.addEventListener(
cast.framework.events.EventType.MEDIA_STATUS, (event) => {
// Write your own event handling code, for example
// using the event.mediaStatus value
});
Message interception
The Web Receiver SDK allows your Web Receiver app to intercept messages and
execute custom code on those messages. The message interceptor takes a
cast.framework.messages.MessageType
parameter that specifies what type of message should be intercepted.
The interceptor should return the modified request or a Promise that resolves
with the modified request value. Returning null
will prevent calling the
default message handler. See Loading media for more details.
For example, if you want to change the load request data, you can use the following logic to intercept and modify it:
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD, loadRequestData => {
const error = new cast.framework.messages.ErrorData(
cast.framework.messages.ErrorType.LOAD_FAILED);
if (!loadRequestData.media) {
error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
return error;
}
if (!loadRequestData.media.entity) {
return loadRequestData;
}
return thirdparty.fetchAssetAndAuth(loadRequestData.media.entity,
loadRequestData.credentials)
.then(asset => {
if (!asset) {
throw cast.framework.messages.ErrorReason.INVALID_REQUEST;
}
loadRequestData.media.contentUrl = asset.url;
loadRequestData.media.metadata = asset.metadata;
loadRequestData.media.tracks = asset.tracks;
return loadRequestData;
}).catch(reason => {
error.reason = reason; // cast.framework.messages.ErrorReason
return error;
});
});
context.start();
Error handling
When errors happen in message interceptor, your Web Receiver app should return
an appropriate
cast.framework.messages.ErrorType
and
cast.framework.messages.ErrorReason
.
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD, loadRequestData => {
const error = new cast.framework.messages.ErrorData(
cast.framework.messages.ErrorType.LOAD_CANCELLED);
if (!loadRequestData.media) {
error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
return error;
}
...
return fetchAssetAndAuth(loadRequestData.media.entity,
loadRequestData.credentials)
.then(asset => {
...
return loadRequestData;
}).catch(reason => {
error.reason = reason; // cast.framework.messages.ErrorReason
return error;
});
});
Message interception vs event listener
Some key differences between message interception and event listener are as follows:
- An event listener does not allow you to modify the request data.
- An event listener is best used to trigger analytics or a custom function.
playerManager.addEventListener(cast.framework.events.category.CORE,
event => {
console.log(event);
});
- Message interception allows you to listen to a message, intercept it, and modify the request data itself.
- Message interception is best used to handle custom logic with regards to request data.
Loading media
MediaInformation
provides numerous properties to load media in the
cast.framework.messages.MessageType.LOAD
message including the entity
,
contentUrl
, and contentId
.
- The
entity
is the suggested property to use in your implementation for both your sender and receiver apps. The property is a deep link URL that can be either a playlist or media content. You application should parse this URL and populate at least one of the other two fields. - The
contentUrl
corresponds to the playable URL that the player will use to load the content. For example, this URL could point to a DASH manifest. - The
contentId
can be either a playable content URL (similar to that of thecontentUrl
property) or a unique identifier for the content or playlist being loaded. If using this property as an identifier, your application should populate a playable URL in thecontentUrl
.
The suggestion is to use entity
to store the real ID or key parameters, and
use contentUrl
for the URL of the media. An example of this is shown in the
following snippet where the entity
is present in the LOAD
request and the
playable contentUrl
is retrieved:
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD, loadRequestData => {
...
if (!loadRequestData.media.entity) {
// Copy the value from contentId for legacy reasons if needed
loadRequestData.media.entity = loadRequestData.media.contentId;
}
return thirdparty.fetchAssetAndAuth(loadRequestData.media.entity,
loadRequestData.credentials)
.then(asset => {
loadRequestData.media.contentUrl = asset.url;
...
return loadRequestData;
});
});
Device capabilities
The
getDeviceCapabilities
method provides device information on the connected Cast device and the video or
audio device attached to it. The getDeviceCapabilities
method provides support
information for Google Assistant, Bluetooth, and the connected display and audio
devices.
This method returns an object which you can query by passing in one of the
specified enums to get the device capability for that enum. The enums are
defined in
cast.framework.system.DeviceCapabilities
.
This example checks if the Web Receiver device is capable of playing HDR and
DolbyVision (DV) with the IS_HDR_SUPPORTED
and IS_DV_SUPPORTED
keys,
respectively.
const context = cast.framework.CastReceiverContext.getInstance();
context.addEventListener(cast.framework.system.EventType.READY, () => {
const deviceCapabilities = context.getDeviceCapabilities();
if (deviceCapabilities &&
deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_HDR_SUPPORTED]) {
// Write your own event handling code, for example
// using the deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_HDR_SUPPORTED] value
}
if (deviceCapabilities &&
deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_DV_SUPPORTED]) {
// Write your own event handling code, for example
// using the deviceCapabilities[cast.framework.system.DeviceCapabilities.IS_DV_SUPPORTED] value
}
});
context.start();
Handling user interaction
A user can interact with your Web Receiver application through sender applications (Web, Android, and iOS), voice commands on Assistant-enabled devices, touch controls on smart displays, and remote controls on Android TV devices. The Cast SDK provides various APIs to allow the Web Receiver app to handle these interactions, update the application UI through user action states, and optionally send the changes to update any backend services.
Supported media commands
The UI controls states are driven by the
MediaStatus.supportedMediaCommands
for iOS and Android sender expanded controllers, receiver and remote control
apps running on touch devices, and receiver apps on Android TV devices. When a
particular bitwise Command
is enabled in the property, the buttons which are
related to that action are enabled. If the value is not set, then the button is
disabled. These values can be changed on the Web Receiver by:
- Using
PlayerManager.setSupportedMediaCommands
to set the specificCommands
- Adding a new command using
addSupportedMediaCommands
- Removing an existing command using
removeSupportedMediaCommands
.
playerManager.setSupportedMediaCommands(cast.framework.messages.Command.SEEK |
cast.framework.messages.Command.PAUSE);
When the receiver prepares the updated MediaStatus
, it will include the
changes in the supportedMediaCommands
property. When the status is
broadcasted, the connected sender apps will update the buttons in their UI
accordingly.
For more information about supported media commands and touch devices see
Accessing UI controls
guide.
Managing user action states
When users interact with the UI or send voice commands, they can control the
playback of the content and properties related to the item playing. Requests
that control the playback are handled automatically by the SDK. Requests that
modify properties for the current item playing, such as a LIKE
command,
require that the receiver application handle them. The SDK provides a series of
APIs to handle these types of requests. To support these requests, the following
must be done:
- Set the
MediaInformation
userActionStates
with a user's preferences when loading a media item. - Intercept
USER_ACTION
messages and determine the action requested. - Update the
MediaInformation
UserActionState
to update the UI.
The following snippet intercepts the LOAD
request and populates the
LoadRequestData
's MediaInformation
. In this case, the user likes the
content that is being loaded.
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD, (loadRequestData) => {
const userActionLike = new cast.framework.messages.UserActionState(
cast.framework.messages.UserAction.LIKE);
loadRequestData.media.userActionStates = [userActionLike];
return loadRequestData;
});
The following snippet intercepts the USER_ACTION
message and handles calling
the backend with the requested change. It then makes a call to update the
UserActionState
on the receiver.
playerManager.setMessageInterceptor(cast.framework.messages.MessageType.USER_ACTION,
(userActionRequestData) => {
// Obtain the media information of the current content to associate the action to.
let mediaInfo = playerManager.getMediaInformation();
// If there is no media info return an error and ignore the request.
if (!mediaInfo) {
console.error('Not playing media, user action is not supported');
return new cast.framework.messages.ErrorData(messages.ErrorType.BAD_REQUEST);
}
// Reach out to backend services to store user action modifications. See sample below.
return sendUserAction(userActionRequestData, mediaInfo)
// Upon response from the backend, update the client's UserActionState.
.then(backendResponse => updateUserActionStates(backendResponse))
// If any errors occurred in the backend return them to the cast receiver.
.catch((error) => {
console.error(error);
return error;
});
});
The following snippet simulates a call to a backend service. The function checks
the UserActionRequestData
to see the type of change that the user requested
and only makes a network call if the action is supported by the backend.
function sendUserAction(userActionRequestData, mediaInfo) {
return new Promise((resolve, reject) => {
switch (userActionRequestData.userAction) {
// Handle user action changes supported by the backend.
case cast.framework.messages.UserAction.LIKE:
case cast.framework.messages.UserAction.DISLIKE:
case cast.framework.messages.UserAction.FOLLOW:
case cast.framework.messages.UserAction.UNFOLLOW:
case cast.framework.messages.UserAction.FLAG:
case cast.framework.messages.UserAction.SKIP_AD:
let backendResponse = {userActionRequestData: userActionRequestData, mediaInfo: mediaInfo};
setTimeout(() => {resolve(backendResponse)}, 1000);
break;
// Reject all other user action changes.
default:
reject(
new cast.framework.messages.ErrorData(cast.framework.messages.ErrorType.INVALID_REQUEST));
}
});
}
The following snippet takes the UserActionRequestData
and either adds or
removes the UserActionState
from the MediaInformation
. Updating the
UserActionState
of the MediaInformation
changes the state of the button that
is associated with the requested action. This change is reflected in the smart
display controls UI, remote control app, and Android TV UI. It is also
broadcasted through outgoing MediaStatus
messages to update the UI of the
expanded controller for iOS and Android senders.
function updateUserActionStates(backendResponse) {
// Unwrap the backend response.
let mediaInfo = backendResponse.mediaInfo;
let userActionRequestData = backendResponse.userActionRequestData;
// If the current item playing has changed, don't update the UserActionState for the current item.
if (playerManager.getMediaInformation().entity !== mediaInfo.entity) {
return;
}
// Check for existing userActionStates in the MediaInformation.
// If none, initialize a new array to populate states with.
let userActionStates = mediaInfo.userActionStates || [];
// Locate the index of the UserActionState that will be updated in the userActionStates array.
let index = userActionStates.findIndex((currUserActionState) => {
return currUserActionState.userAction == userActionRequestData.userAction;
});
if (userActionRequestData.clear) {
// Remove the user action state from the array if cleared.
if (index >= 0) {
userActionStates.splice(index, 1);
}
else {
console.warn("Could not find UserActionState to remove in MediaInformation");
}
} else {
// Add the UserActionState to the array if enabled.
userActionStates.push(
new cast.framework.messages.UserActionState(userActionRequestData.userAction));
}
// Update the UserActionState array and set the new MediaInformation
mediaInfo.userActionStates = userActionStates;
playerManager.setMediaInformation(mediaInfo, true);
return;
}
Voice commands
The following media commands are currently supported in the Web Receiver SDK for
Assistant-enabled devices. The default implementations of these commands are
found in
cast.framework.PlayerManager
.
Command | Description |
---|---|
Play | Play or resume playback from paused state. |
Pause | Pause currently playing content. |
Previous | Skip to the previous media item in your media queue. |
Next | Skip to the next media item in your media queue. |
Stop | Stop the currently playing media. |
Repeat None | Disable repeating of media items in the queue once the last item in the queue is done playing. |
Repeat Single | Repeat the currently playing media indefinitely. |
Repeat All | Repeat all items in the queue once the last item in the queue is played. |
Repeat All and Shuffle | Once the last item in the queue is done playing, shuffle the queue and repeat all items in the queue. |
Shuffle | Shuffle media items in your media queue. |
Closed Captions ON / OFF | Enable / Disable Closed Captioning for your media. Enable / Disable is also available by language. |
Seek to absolute time | Jumps to the specified absolute time. |
Seek to time relative to current time | Jumps forward or backward by the specified time period relative to the current playback time. |
Play Again | Restart the currently playing media or play the last played media item if nothing is currently playing. |
Set playback rate | Vary media playback rate. This should be handled by default. You can use the SET_PLAYBACK_RATE message interceptor to override incoming rate requests. |
Supported media commands with voice
To prevent a voice command from triggering a media command on an Assistant-
enabled device, you must first set the
supported media commands
that you plan on supporting. Then you must enforce those commands by enabling
the
CastReceiverOptions.enforceSupportedCommands
property. The UI on Cast SDK senders and touch-enabled devices will change to
reflect these configurations. If the flag is not enabled the incoming voice
commands will execute.
For example, if you allow PAUSE
from your sender applications and
touch-enabled devices, you must also configure your receiver to reflect those
settings. When configured, any incoming voice commands will be dropped if not
included in the list of supported commands.
In the example below we are supplying the CastReceiverOptions
when starting
the CastReceiverContext
. We've added support for the PAUSE
command and
enforced the player to support only that command. Now if a voice command
requests another operation such as SEEK
it will be denied. The user will be
notified that the command is not supported yet.
const context = cast.framework.CastReceiverContext.getInstance();
context.start({
enforceSupportedCommands: true,
supportedCommands: cast.framework.messages.Command.PAUSE
});
You can apply separate logic for each command that you want to restrict. Remove
the enforceSupportedCommands
flag and for each command that you want to
restrict you can intercept the incoming message. Here we intercept the request
provided by the SDK so that SEEK
commands issued to Assistant-enabled devices
do not trigger a seek in your Web Receiver application.
For media commands your application does not support, return an appropriate
error reason, such as
NOT_SUPPORTED
.
playerManager.setMessageInterceptor(cast.framework.messages.MessageType.SEEK,
seekData => {
// Block seeking if the SEEK supported media command is disabled
if (!(playerManager.getSupportedMediaCommands() & cast.framework.messages.Command.SEEK)) {
let e = new cast.framework.messages.ErrorData(cast.framework.messages.ErrorType
.INVALID_REQUEST);
e.reason = cast.framework.messages.ErrorReason.NOT_SUPPORTED;
return e;
}
return seekData;
});
Backgrounding from voice activity
If the Cast platform backgrounds your application's sound due to Assistant
activity such as listening to user speech or talking back, a
FocusState
message of NOT_IN_FOCUS
is sent to the Web Receiver application when the
activity starts. Another message with IN_FOCUS
is sent when the activity ends.
Depending on your application and the media being played, you might want to
pause media when the FocusState
is NOT_IN_FOCUS
by intercepting the message
type FOCUS_STATE
.
For example, it's a good user experience to pause audiobook playback if the Assistant is responding to a user query.
playerManager.setMessageInterceptor(cast.framework.messages.MessageType.FOCUS_STATE,
focusStateRequestData => {
// Pause content when the app is out of focus. Resume when focus is restored.
if (focusStateRequestData.state == cast.framework.messages.FocusState.NOT_IN_FOCUS) {
playerManager.pause();
} else {
playerManager.play();
}
return focusStateRequestData;
});
Voice-specified caption language
When a user does not explicitly state the language for the captions, the
language used for captions is the same language in which the command was spoken.
In these scenarios, the
isSuggestedLanguage
parameter of the incoming message indicates whether the associated language was
suggested or explicitly requested by user.
For example, isSuggestedLanguage
is set to true
for the command "OK Google,
turn captions on," because the language was inferred by the language the
command was spoken in. If the language is explicitly requested, such as in "OK
Google, turn on English captions," isSuggestedLanguage
is set to false
.
Metadata and voice casting
While voice commands are handled by the Web Receiver by default, you should ensure the metadata for your content is complete and accurate. This ensures that voice commands are handled properly by the Assistant and that the metadata surfaces properly across new types of interfaces such as the Google Home app and smart displays like the Google Home Hub.
Stream transfer
Preserving session state is the basis of stream transfer, where users can move existing audio and video streams across devices using voice commands, Google Home App, or smart displays. Media stops playing on one device (the source) and continues on another (the destination). Any Cast device with the latest firmware can serve as sources or destinations in a stream transfer.
The event flow for stream transfer is:
- On the source device:
- Media stops playing.
- The Web Receiver application receives a command to save the current media state.
- The Web Receiver application is shut down.
- On the destination device:
- The Web Receiver application is loaded.
- The Web Receiver application receives a command to restore the saved media state.
- Media resumes playing.
Elements of media state include:
- Specific position or timestamp of the song, video, or media item.
- Its place in a broader queue (such as a playlist or artist radio).
- The authenticated user.
- Playback state (for example, playing or paused).
Enabling stream transfer
To implement stream transfer for your Web Receiver:
- Update
supportedMediaCommands
with theSTREAM_TRANSFER
command:playerManager.addSupportedMediaCommands( cast.framework.messages.Command.STREAM_TRANSFER, true);
- Optionally override the
SESSION_STATE
andRESUME_SESSION
message interceptors as described in Preserving session state. Only override these if custom data needs to be stored as part of the session snapshot. Otherwise, the default implementation for preserving session states will support stream transfer.
Preserving session state
The Web Receiver SDK provides a default implementation for Web Receiver apps to preserve session states by taking a snapshot of current media status, converting the status into a load request, and resuming the session with the load request.
The load request generated by the Web Receiver can be overridden in the
SESSION_STATE
message interceptor if necessary. If you want to add custom data
into the load request, we suggest putting them in
loadRequestData.customData
.
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.SESSION_STATE,
function (sessionState) {
// Override sessionState.loadRequestData if needed.
const newCredentials = updateCredentials_(sessionState.loadRequestData.credentials);
sessionState.loadRequestData.credentials = newCredentials;
// Add custom data if needed.
sessionState.loadRequestData.customData = {
'membership': 'PREMIUM'
};
return sessionState;
});
The custom data can be retrieved from
loadRequestData.customData
in the RESUME_SESSION
message interceptor.
let cred_ = null;
let membership_ = null;
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.RESUME_SESSION,
function (resumeSessionRequest) {
let sessionState = resumeSessionRequest.sessionState;
// Modify sessionState.loadRequestData if needed.
cred_ = sessionState.loadRequestData.credentials;
// Retrieve custom data.
membership_ = sessionState.loadRequestData.customData.membership;
return resumeSessionRequest;
});
Content preload
The Web Receiver supports preloading of media items after the current playback item in the queue.
The preload operation pre-downloads several segments of the upcoming items. The specification is done on the preloadTime value in the QueueItem object (default to 20 seconds if not provided). The time is expressed in seconds, relative to the end of the currently playing item . Only positive values are valid. For example, if the value is 10 seconds, this item will be preloaded 10 seconds before the previous item has finished. If the time to preload is higher than the time left on the currentItem, the preload will just happen as soon as possible. So if a very large value of preload is specified on the queueItem, one could achieve the effect of whenever we are playing the current item we are already preloading the next item. However, we leave the setting and choice of this to developer as this value can affect bandwidth and streaming performance of the current playing item.
Preloading will work for HLS, DASH, and Smooth streaming content by default.
Regular MP4 video and audio files such as MP3 will not be preloaded as Cast devices support one media element only and cannot be used to preload while an existing content item is still playing.
Custom messages
Message exchange is the key interaction method for Web Receiver applications.
A sender issues messages to a Web Receiver using the sender APIs for the
platform the sender is running (Android, iOS, Web). The event object (which
is the manifestation of a message) that is passed to the event listeners has a
data element (event.data
) where the data takes on the properties of the
specific event type.
A Web Receiver application may choose to listen for messages on a specified namespace. By virtue of doing so, the Web Receiver application is said to support that namespace protocol. It is then up to any connected senders wishing to communicate on that namespace to use the appropriate protocol.
All namespaces are defined by a string and must begin with "urn:x-cast:
"
followed by any string. For example,
"urn:x-cast:com.example.cast.mynamespace
".
Here is a code snippet for the Web Receiver to listen to custom messages from connected senders:
const context = cast.framework.CastReceiverContext.getInstance();
const CUSTOM_CHANNEL = 'urn:x-cast:com.example.cast.mynamespace';
context.addCustomMessageListener(CUSTOM_CHANNEL, function(customEvent) {
// handle customEvent.
});
context.start();
Similarly, Web Receiver applications can keep senders informed about the state
of the Web Receiver by sending messages to connected senders. A Web Receiver
application can send messages using
sendCustomMessage(namespace, senderId, message)
on
CastReceiverContext
.
A Web Receiver can send messages to an individual sender, either in response to
a received message or due to an application state change. Beyond point-to-point
messaging (with a limit of 64kb), a Web Receiver may also broadcast messages to
all connected senders.
Cast for audio devices
See Google Cast for audio devices guide for support on audio only playback.
Android TV
This section discusses how the Google Web Receiver uses your inputs as playback, and Android TV compatibility.
Integrating your application with the remote control
The Google Web Receiver running on the Android TV device translates input from
the device's control inputs (i.e. hand-held remote control) as media playback
messages defined for the urn:x-cast:com.google.cast.media
namespace, as
described in Media Playback Messages. Your
application must support these messages to control the application media
playback in order to allow basic playback control from Android TV’s control
inputs.
Guidelines for Android TV compatibility
Here are some recommendations and common pitfalls to avoid in order to ensure your application is compatible with Android TV:
- Be aware that the user-agent string contains both "Android" and "CrKey"; some sites may redirect to a mobile-only site because they detect the "Android" label. Don't assume that "Android" in the user-agent string always indicates a mobile user.
- Android's media stack may use transparent GZIP for fetching data. Make sure
your media data can respond to
Accept-Encoding: gzip
. - Android TV HTML5 media events may be triggered in different timings than Chromecast, this may reveal issues that were hidden on Chromecast.
- When updating the media, use media related events fired by
<audio>/<video>
elements, liketimeupdate
,pause
andwaiting
. Avoid using networking related events likeprogress
,suspend
andstalled
, as these tend to be platform dependent. See Media events for more information about handling media events in your receiver. - When configuring your receiver site’s HTTPS certificates, be sure to include intermediate CA certificates. See the Qualsys SSL test page to verify: if the trusted certification path for your site includes a CA certificate labelled “extra download”, then it may not load on Android-based platforms.
- While Chromecast displays the receiver page on a 720p graphics plane, other Cast platforms including Android TV may display the page up to 1080p. Ensure your receiver page scales gracefully at different resolutions.