In the day and age of online streaming, Google Chromecast is probably the most affordable living room OTT solution on the market. Google originally introduced the dongle with the intent to connect phone apps to a not so “Smart” TVs back in the day, so users can stream content from their phones onto a larger screen. Since then, Chromecast has grown and evolved to support 1000+ cast enabled apps that can stream 200,000+ TV shows and movies. Today, Chromecast comes automatically built into modern TV’s and displays like Vizio, Sony, Sharp etc which makes it all the more important for content providers to support Chromecast in their video offering.
The Bitmovin HTML5 player and features are supported on Chromecast and Cast enabled devices. We also provide reference Receiver implementation so developers can get started quickly on this platform. The following guide runs through the details of set up, customization and troubleshooting tips for Chromecast.
Developing for Chromecast
This guide assumes that you have an existing understanding of implementing the Bitmovin Player. If this is the first time diving into our player, please take a look at our guide on getting started with the Bitmovin player (https://bitmovin.com/docs/player/quickstarts/get-started-with-the-bitmovin-player).
Using Chromecast with the Bitmovin Player
The Bitmovin Player supports Chromecast natively, enabling users to easily cast their viewing session onto any supported device. To enable this functionality on the player, simply add the remotecontrol configuration parameter to the player config, i.e.:
remotecontrol: { type: 'googlecast', }
With this configuration added, our player will then actively try to connect to any Chromecast device on the network. If we detect a device is available, we will automatically display the Chromecast button on the player’s control bar that can be used to activate casting.
Once casting is activated by the user, we’ll pass through the source video properties configured to the player experience on the Chromecast without any extra code or configuration parameters needed.
Our integration is intended to provide the same features our customers enjoy from the desktop and mobile player to the Chromecast device, such as:
- DRM and non-DRM playout in DASH, HLS, Progressive and Smooth formats
- Captions and separate audio tracks
- Our advanced ABR adaptation algorithm
- A consistent UI experience
- … and more
To achieve a consistent UI experience, we use the same CSS styles on Chromecast as we do on the desktop and mobile players. As a result, if you find the need to modify or override certain styles on the Chromecast to match your primary player’s styles, you can specify the CSS overrides in an optional parameter in the remotecontrol config, i.e.:
remotecontrol: { type: 'googlecast', customReceiverConfig: { receiverStylesheetUrl: "https://example.com/path/to/your/custom-receiver.css" }, }
The stylesheet override offers a convenient way to make styling adjustments, but does not offer the ability to make more advanced UI changes. For example, you may want to change where the pause icon is placed in the Chromecast UI, or overlay additional metadata while the video is paused. Or, as in the case of one of our customers, build a custom playback overlay to debug playback issues.
These and other more advanced changes require building your own Chromecast Receiver.
If you’re not familiar with a Chromecast Receiver, don’t worry! We’re going to take a deeper look into how to setup a Receiver in this blog and how to connect it with the Bitmovin player. Before we dive deep, let’s take a look at what’s happening behind the scenes between our player, the Chromecast SDK and a Receiver out-of-the-box.
Behind the Scenes
There are 3 key components to a Chromecast experience:
- A “Sender” which is a player experience (web page or native SDK) that users interact with to start watching video on their device. The sender also acts as the remote control when a Chromecast session is active.
- A “Receiver” which is a HTML5 page hosted on a web server that loads a player framework. The receiver is loaded onto the Chromecast device and listens for directives from the sender in order to manage the playback session on the Chromecast device itself.
- The Google Cast SDK framework that’s used to manage the interactions between the Sender API and Receiver API.
Bitmovin’s player integrates with the Cast SDK v2 framework; however, instead of using the Media Player Library (MPL) that’s part of the v2 framework, our receiver loads the Bitmovin player framework instead. This allows us to offer advanced features that may not be supported by MPL, and also allows us to address playback issues or to innovate without waiting for changes to the v2 framework.
Our goal is to simplify and abstract the complexities between the interaction of these 3 components, and to enable you to focus on implementing the needs of your business, instead of the needs of the technology.
Components and their Responsibilities
The Sender is the web page or native SDK experience that initializes the Bitmovin player for traditional non-Chromecast video experiences. The player configuration you define at the Sender is only used to initialize the Bitmovin player in the Sender only, but the SourceConfig used is shared with the Receiver.
The Sender has the following responsibilities:
- Initialize the Bitmovin player to manage playback on the device loading the sender
- Establishing a connection to a Chromecast device
- Instructing the Chromecast to load a particular Receiver
- Passing video configuration data to the Receiver
- Controlling the user experience on the Receiver and ultimately the device playing the video
- Receive player events fired from the Receiver and roll them up to appear as events fired from the Sender
The Google Cast SDK is simply the middleman that offers API methods that enable sending messages to and from Sender and Receiver appropriately. The Bitmovin Player wraps these APIs in order to streamline the interaction between all the components.
Our Receiver is a HTML5 page that loads a separate instance of the Bitmovin player. This instance has its own player configuration and is generally much more barebones than the Sender’s player configuration. The Receiver configuration also does not require any SourceConfig configuration as that’s passed into the Receiver via the Sender.
The Receiver’s responsibilities are to :
- Initialize the Bitmovin player components to manage playback on the device connected to the Chromecast
- Process messages received from the sender to start playback of the target video
- Communicate back to the Sender events that are happening on the Chromecast
The sequence diagram below provides a high-level overview of the initial load and playback sequence between the 3 components:
Your responsibility as an integrator of the Bitmovin player is simple:
- Configure the player like any other Bitmovin player
- Configure casting per the getting started section above
- Configure the SourceConfig that defines the video URLs and DRM attributes of your streams
- Load the SourceConfig configuration into the player
From here, we’ll take care of the rest to ensure that the source configuration details you’ve defined in the Sender are correctly processed by the Receiver and that user control actions from the Sender are mirrored on the Chromecast.
Limitations of the out-of-the-box Receiver
Since the Receiver loads a separate Bitmovin player from the Sender, there are limitations in what configurations are shared between the Sender player and the Receiver player primarily due to the asynchronous nature in which the Sender and Receiver are loaded.
Other than the SourceConfig object, all the other player configuration objects are not passed into the Receiver. e.g. AdaptationConfig, NetworkConfig, TweaksConfig, etc.
As a result of this, it may be necessary to build your own Custom Receiver to bake in these additional configurations. Some additional use cases that may prompt Custom Receiver development are:
- You’ve built a custom Bitmovin UI
- You have advanced DRM authentication requirements that need to make use of our Network API to manipulate the HTTP requests
- You want to use a different adaptation config on the Chromecast because the device may struggle to handle the top-end bitrate at 60fps
While these would require building a Custom Receiver, there’s still considerable benefit in using the Bitmovin player as the foundation for the Receiver and we’ll walk through this in the next section.
Building a Custom Receiver
If you have not built a Custom Receiver before, we recommend using our stock Receiver HTML page as a starting point. Since there are a few moving pieces to a functional end-to-end custom receiver setup, it is helpful to start with a configuration that’s known to work before adding modifications.
Bitmovin’s Stock Receiver
You can locate the Bitmovin stock receiver in our Github repository here: https://github.com/bitmovin/bitmovin-player-web-samples/blob/master/castReceiver/receiverApp.html
There’s only one modification you need to make in this source and that is to replace the text “YOUR_PLAYER_LICENSE_KEY” with your Bitmovin License Key retrieved from the Dashboard here: https://bitmovin.com/dashboard/player/licenses.
Once the key has been updated, you now need to host this page on a web server that is accessible to the Chromecast device.
This example is fully functional and can be used as-is without further changes as a starting point for your development. It is an exact copy of the default receiver activated by the stock Bitmovin player.
Adding your Custom Receiver to the Google Cast SDK Developer Console
Before you can start configuring any player to work with a Custom Receiver, you must first register to the Google Cast SDK Developer Console: https://cast.google.com/publish/#/overview. Once registered, you will have the ability to add “Applications” which is where you model details about your Custom Receiver. This is a requirement for all Custom Receivers, and not specific to Bitmovin’s implementation.
Adding a new Application
The steps below will cover adding your Custom Receiver as an Application to the Google Cast SDK Developer Console:
- Log into https://cast.google.com/publish/#/overview
- Click on “Add New Application”
- Select “Custom Receiver”
- Under Basics > Name, enter the name to give your application, e.g. “My Custom Receiver”
- Under Receiver Details > Receiver Application URL, provide the full HTTP(S) path to your receiver, e.g. http://www.mydomain.com/path/to/receiver.html
- Click “Save”
You will receive a registration summary if your application was successfully registered. Within this summary you should receive an Application ID generated by Google. This ID will be used in the Bitmovin Player configuration to identify which receiver to load.
By default, a new application has a status of Unpublished. This means that only registered Chromecast devices can work with this Receiver. This is perfectly fine for testing, but will require an extra step in the Cast SDK Developer Console.
Adding a new device
To register a new device, follow these steps in the Google Cast SDK Developer Console:
- Log into https://cast.google.com/publish/#/overview
- Click on “Add new device”
- Populate the “Serial Number” field from your Chromecast device
- Click “OK”
Adding a device is also required if you want to debug the receiver using Chrome’s Inspect tools.
Updating the Bitmovin Player to point to your Application ID
With the receiver and your Chromcast device registered with the Cast SDK Developer Console, you can now point the Bitmovin Player config to your Application ID in order to load the receiver on your Chromecast instead of Bitmovin’s default receiver.
Within the remotecontrol configuration on our player, set the receiverApplicationId property with your Application ID, e.g.
remotecontrol: { type: 'googlecast', receiverApplicationId: 'ABCD1234' }
That’s it! Our player is now ready to load, and interact with your custom receiver.
Debugging a Custom Receiver
If you’ve added your Chromecast device via the “Add new device” flow in the Google Cast SDK Developer Console, it’s very easy to open Chrome’s Developer Tools on the receiver session itself.
Once you’ve activated a casting session from the Sender, go to chrome://inspect/#devices in Chrome and you should see your Chromecast listed with an “Inspect” link.
Once clicked, a detached Developer Tools session will open up where you’ll be able to see the network traffic, console log output and other developer tools features for the receiver on the Chromecast itself.
Example Use Cases for Receiver Customization
Below are a collection of examples where the receiver has been modified to achieve a particular goal.
Pass Custom Properties from Sender to Receiver
As you begin developing a custom receiver, you may find the need to pass variables to the receiver from the sender. For example, let’s assume you want to send a user ID down into the Receiver.
Without our API, you’d need to interact with the Google Cast SDK directly in order to send messages to and from, but with our player you can take advantage of the player’s `addMetadata` API method to send data.
To send a collection of key/value pairs to the Receiver, pass a JSON blob into the addMetadata call like so:
player.addMetadata('CAST', {'userId': '123456'})
This metadata is then sent to the Receiver and can be handled via the Metadata event. As a result, if we attach a metadata listener to the events config of the Receiver player, we can then extract this metadata for use:
const conf = { key: 'YOUR_PLAYER_LICENSE_KEY', tweaks: { max_buffer_level: 20, }, events: { metadata: (details) => { console.log('The user ID from the sender is: ' + details.data.userId); } }, ui: false, };
Add Bitmovin Analytics
By default, if you enable Bitmovin Analytics on the sender config using our out-of-the-box receiver, we’ll still track playback events for that session; however, certain properties in the analytics session, such as Operating System and Browser, will be based on the sender information, and not the Chromecast device.
If you’re looking to report on granular Chromecast metrics across your user base, such as player load starts, or specific Chromecast models being used, you’ll want to add Bitmovin Analytics to the receiver config as well.
To do this, you’ll just need to add the analytics config to the receiver player config, e.g.
const conf = { analytics: { key: 'YOUR_ANALYTICS_LICENSE_KEY', }, key: 'YOUR_PLAYER_LICENSE_KEY', tweaks: { max_buffer_level: 20, }, events: {}, ui: false, };
Limit Playable Bitrates
From our experience, we’ve found that certain Chromecast models can struggle to play high bitrate, high frame rate content smoothly. As a result, customers of ours have needed the flexibility to limit the playback experience on the Chromecast without impacting the playback experience on the more powerful sender devices.
To address this particular situation, you can take advantage of Bitmovin’s AdaptationConfig on the receiver to set a max bitrate the device can use.
For example, if you offer a HLS or DASH package where the top 3 bitrates are:
- 10Mbit @ 60fps
- 8Mbit @ 60fps
- 5Mbit @ 30fps
And you find through user testing that the two 60fps streams cause Chromecast models to struggle to play these smoothly, you could add the following AdaptationConfig to the receiver config:
const conf = { adaptation: { exclude: true, bitrates: { maxSelectableVideoBitrate: '5000kbps' } }, key: 'YOUR_PLAYER_LICENSE_KEY', tweaks: { max_buffer_level: 20, }, events: {}, ui: false, };
This should now prevent the Chromecast from adjusting to a bitrate higher than 5000kbps. Since this config does not live on the Sender, users who are not using the Chromecast to playback content are not affected by this change.
DRM Authentication using the Network API
If you’re not familiar with the Network API feature in our player, it’s a mechanism in which you can intercept the HTTP calls being made by the player in order to modify the request parameters or response payload before it’s processed by the player itself.
A common use case is to add authentication query strings or headers onto DRM license calls or manifest playlist/segment URLs.
Currently, the Network API configuration on the sender is not passed into the receiver configuration, so it’s necessary to mirror this logic in the receiver player. Because the receiver utilizes a full-featured Bitmovin player, it’s easy to port the logic down into the receiver without any additional changes.
In this use case, we’ll apply a HTTP Header to a WideVine DRM license request, where the HTTP Header is based on the response of another HTTP call made via the Network API implementation.
Assume you have a licensing server that takes in a “key” query string and returns a JSON response with a base64’ed header value, e.g.:
{ “licenseHeader”: “tMMPDlsR00cMMI2hOCMKJA==” }
This header value then needs to be included in the request headers of the WideVine DRM license call. However, this header only has a short time-to-live, so we need to ensure that we retrieve this value just before the license call is made to ensure validity.
Using the NetworkConfig on the receiver’s player configuration, we can add a handler to the preprocessHttpRequest function to make a call out to our licensing server, retrieve the JSON, add the “licenseHeader” value to the request header, then return the updated request object back to the player so that it can proceed with making the WideVine DRM license call.
const conf = { key: 'YOUR_PLAYER_LICENSE_KEY', tweaks: { max_buffer_level: 20, }, network: { preprocessHttpRequest: function(type, request) { console.log(type, request); if (type === bitmovin.player.HttpRequestType.DRM_LICENSE_WIDEVINE) { function getDrmHeader() { return fetch('http://license-server.com/getLicenseHeader?key=1234'); };
return getDrmHeader().then(function (response) { return response.json(); }).then(function (data) { request.headers['drmheader'] = data.licenseHeader; return request; }); } } }, events: {}, ui: false, };
With this configuration, every Widevine DRM license call made by the Chromecast will have a fresh header appended to the request without any need to contiunously pass license data from the sender, to the receiver.
Load a Custom UI
If you have opted to build a custom Bitmovin Player UI for the Receiver experience (see https://github.com/bitmovin/bitmovin-player-ui for more information on how to build custom UIs), then you’ll need to load that UI in the Receiver page.
The out-of-the-box receiver includes the following call to load the built-in Bitmovin-themed Cast Receiver UI:
const uimanager = bitmovin.playerui.UIFactory.buildDefaultCastReceiverUI(player);
Once you’ve built your own UI, you can load it by invoking the method you’ve defined on the UIFactory to load your UI, e.g.
const uimanager = bitmovin.playerui.UIFactory.buildMyCustomCastReceiverUI(player);
Well, that was a long read! Phew 🙂
We hope this guide helped answer any questions about implementing Bitmovin player on Chromecast. We would love to hear your feedback.