Cast plugin for the Native SDK for Android

In this topic, you will learn how to cast your videos to a Chromecast-connected TV, using the Cast plugin for the Brightcove Native SDK for Android.

Overview

With Google Cast technology, you can initiate and control streamed video content from your mobile devices to high-definition TV and home audio systems. From your app, tap the Cast button to stream your content on a big screen.

To build a Cast application, you need the following components:

  • A sender application - This resides on your mobile device, and detects receiver devices, establishes a secure connection, and mirrors your content. The sender app is the local player that is used to cast content to the receiver app on your Chromecast device. After you cast to the receiver app, you can think of it as a remote control for the Chromecast.

    The sender app is provided by the Cast plugin for the Brightcove Native SDK for Android. You will learn about it in this topic.

  • A receiver application - This application runs on the Chromecast device. It can be thought of as a single-page HTML app with CSS and JavaScript assets.

    For testing, follow these steps:

    1. Start with Google's Sample Cast Receiver
    2. Review Google Cast Apps documentation

    For production use, this document will illustrate how to use the Brightcove Receiver v2.0.0.

Supported SDK version

To use the Cast plugin with the new Brightcove Receiver v2.0, you must use the Brightcove Native SDK for Android version 6.16.0 and above.

Understanding the Cast plugin

The Cast plugin is built on top of the new ExoPlayer Cast Extension and the Google Play Services Cast Framework. After adding the cast plugin dependency, gradle will pull the Play Services Cast Framework dependency, ExoPlayer Cast Extension dependency along with other needed dependencies.

The Cast plugin has been redesigned to minimize your effort when integrating with Video Cloud. When using Video Cloud, the BrightcoveCastMediaManager class gathers information from the Video Cloud response, such as the Brightcove Video and Source objects, every time the EventType.SET_SOURCE event is emitted. This information is cached and ready to use when the user selects play to queue the video.

Integrating the Cast plugin

There are two ways you can integrate your app with the Native SDK for Android Cast plugin. You can integrate with either the Brightcove Cast Receiver v2.0, or with the Google Cast Demo Receiver.

For either integration, you need to add this dependency to your app project:

implementation "com.brightcove.player:android-cast-plugin:${anpVersion}"

Using the Brightcove Cast Receiver v2.0

This integration is intended for those Brightcove customers who use Brightcove APIs to deliver their content.

For a complete code sample, see the BasicCastBrightcoveReceiverSampleApp.

Using the Google Cast Demo Receiver

This integration is intended for those Brightcove customers who are new to casting.

For a complete code sample, see the BasicCastGoogleReceiverSampleApp.

The Google Cast Demo receiver app is intentionally basic and easy to use, with the following constraints:

  • DRM is not supported.

  • Captions in any video configuration are not supported.

  • Multiple Audio Tracks are not supported.

  • Client-side Advertising is not supported.

  • Server-side Advertising is not supported.

  • Live and Live DVR streams are not supported.

Specifying your own Cast Receiver App Id

The BasicCastGoogleReceiverSampleApp sets a Google Demo Receiver App ID, which can be helpful for getting started and testing, but not for production.

To override this value with your Cast Receiver Application, define the following string value in the sample app’s strings.xml file:

<string name="cast_receiver_app_id">4F8B3483</string>

The Brightcove GoogleCastComponent

The GoogleCastComponent class is the main class of the Brightcove Cast plugin. It instantiates the ExoPlayer CastPlayer and sets its listeners. It exposes some essential methods to load a video or to add it to the queue. The GoogleCastComponent class also adds several Brightcove Event listeners to handle Activity and Fragment lifecycle events, as well as other event listeners that you can use to emit Media Info to load a Video to your Chromecast device.

The GoogleCastComponent now uses a Builder pattern. In the Native SDK for Android versions before v6.16.0, you needed to instantiate the component and pass the Context and EventEmitter to the GoogleCastComponent constructor. Then, you would set the component’s options in a series of separate method calls.

Starting with the Native SDK for Android v6.16.0, use the Builder pattern to create an instance of the GoogleCastComponent and set its options, all within a single chain of Builder method calls.

CustomData

As with the GoogleCastComponent, the CustomData class uses a Builder pattern to instantiate the object and add properties to it. While the Brightcove Receiver can use CustomData to retrieve videos from your Brightcove Catalog, it is not required to send a complete CustomData object, such as for casting a remote asset. It is also important to note that when using the Google Demo Receiver, the use of CustomData is not supported. For the purposes of this discussion, we will focus on CustomData sent to the receiver used to retrieve the Video data from the Brightcove Catalog.

What is CustomData?

CustomData is a JSON object contained in the MediaInfo object. Its intended use is with the Brightcove Cast Receiver App v2.0.

CustomData with the Brightcove Receiver and Catalog Data

When integrating with the Brightcove Receiver, the CustomData JSON object will take this form:

"customData": {
	"accountId": "1234567890",
	"analyticsParams": {
		"application": "com.brightcove.player.test",
		"user": "abcde1c44b951234"
	},
	"catalogParams": {
		"adConfigId": null,
		"type": "video",
		"bcovAuthToken": null,
		"id": "2345678901",
		"policyKey": "BCpkPolicyKeyObject"
	}
}

The CustomData object example above contains all of the data elements necessary to cast a video from the Brightcove Receiver. This data is the same regardless of encryption, that is, there is no additional structure necessary for the license URL in the case of DRM videos.

You can also find an example of the CustomData object in the BrightcoveCastBrightcoveReceiverSampleApp.

CustomData with the Google Demo Receiver

As stated above, CustomData is not supported with the Google Demo Receiver.

BrightcoveCastMediaManager

It is possible to extend the BrightcoveCastMediaManager, as shown above, to override its methods or to implement your own. For examples of extending the BrightcoveCastMediaManager class, see the following:

  • Extend the BrightcoveCastMediaManager section
  • BasicCastCustomRemoteVideoSampleApp

OptionsProvider

Next, you need to specify the OptionsProvider implementation for the Google Cast framework. The OptionsProvider interface helps to set up several options needed to initialize the CastContext class. This is where you will set the Cast Receiver App ID. To learn more about integrating the OptionsProvider, see Google’s Initialize the Cast Context document.

The Brightcove Cast plugin includes a DefaultOptionsProvider class, where the Cast Receiver App ID is set through a string key set in the strings.xml resource file. For more details and to learn how to override it in your app, see the Using Your Own Cast Receiver App ID section above.

Whether you use the DefaultOptionsProvider class or your own OptionsProvider implementation, you need to set the OptionsProvider implementation class name in your app’s AndroidManifest.xml file as a key-value pair metadata, as shown here:

<meta-data android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME" android:value="com.brightcove.cast.DefaultOptionsProvider" />

If you use the DefaultOptionsProvider class, you can set the ExpandedControllerActivity to turn on/off the Cast Notification by setting similar metadata information in your AndroidManifest.

Expanded Controller Activity

The ExpandedControllerActivity comes with the Google Cast library and allows you to control the video being cast on your Chromecast device. This class provides some customization flexibility. For example, there are five slots available to show buttons, where the third slot is reserved for the non-configurable Play button. The rest of the buttons can be set as other predefined buttons or as your own customized buttons.

The Brightcove Cast plugin provides the subclass DefaultExpandedControllerActivity. We have enabled the following buttons in the following order:

  • Closed Captions
  • Skip Previous
  • Play
  • Skip Next
  • Mute Toggle

In addition, the Seek Bar sets the same default drawable as the one used in the standard BrightcoveMediaController:

  • The Progress Drawable:
    R.drawable.default_scrubber_progress_horizontal
  • The Thumb Drawable:
    R.drawable.default_scrubber_thumb

To learn how to customize the Seek Bar, see the SeekBarColorsSampleApp.

In order to set the DefaultExpandedControllerActivity or your own ExpandedControllerActivity, set the following metadata in your AndroidManifest.xml file:

<meta-data  android:name="com.brightcove.cast.DefaultOptionsProvider.EXPANDED_CONTROLLER_ACTIVITY_CLASS_NAME"
android:value="com.brightcove.cast.DefaultExpandedControllerActivity" />

Cast Notification

When the Cast Notification is enabled, the notification will appear when you cast a video and put your app in the background; for example, after pressing the home key.

AndroidManifest.xml file and provide the name of the Activity to launch when the notification is clicked:

<meta-data android:name="com.brightcove.cast.DefaultOptionsProvider.NOTIFICATION_TARGET_ACTIVITY_CLASS_NAME"
android:value="com.brightcove.cast.BrightcoveControllerActivity" />

If you do not provide com.brightcove.cast.DefaultOptionsProvider.NOTIFICATION_TARGET_ACTIVITY_CLASS_NAME or if the value has an invalid Activity name, the Cast Notification will be turned off.

The Cast Button

The cast button allows you to select a Chromecast device in the same network as your device, and enables you to connect and create a session. To add the Cast Button to your application, follow Google’s Integrate Cast: Add a Cast Button document.

The Brightcove Cast plugin provides a utility method to easily setup the Cast button. This is useful when you just want to add the Cast button to the Activity/Fragment menu. See the following code for details:

//Activity
@Override
public boolean onCreateOptionsMenu(Menu menu) {
   super.onCreateOptionsMenu(menu);
   GoogleCastComponent.setUpMediaRouteButton(MainActivity.this, menu);
   return true;
}

The Mini Controller

The Mini Controller is a Fragment that gets attached to your Activity, usually located in the bottom of the layout. The Mini Controller allows you to play and pause the video, and indicates when a video is playing in your Chromecast device. When the Mini Controller is clicked, the Expanded Controller will be launched.

To enable the Mini Controller, add the following code to your Activity’s layout.

<fragment
   android:id="@+id/castMiniController"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_alignParentBottom="true"
   android:visibility="gone"
   class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />

To learn best practices with the Mini Controller, see the Design Checklist: Sender Mini Controller document. For implementation details, see the Integrate Cast: Add Mini Controller document.

Advanced Topics

Listening for Cast session

If you want your app to react when the Cast connection starts or ends, you can add the GoogleCastEventType.CAST_SESSION_STARTED or GoogleCastEventType.CAST_SESSION_ENDED event listeners as shown here:

eventEmitter.on(GoogleCastEventType.CAST_SESSION_STARTED, new EventListener() {
   @Override
   public void processEvent(Event event) {
       // Session Started
   }
});
eventEmitter.on(GoogleCastEventType.CAST_SESSION_ENDED, new EventListener() {
   @Override
   public void processEvent(Event event) {
       // Session Ended
   }
});

Alternatively, you can call GoogleCastComponent.isSessionAvailable() to check for an available session.

Casting a Video

In order to cast a Video to Chromecast after a successful connection, you can either use the EventEmitter and emit the Media Info information or you can directly use the GoogleCastComponent methods.

If you prefer to emit the Media Info you can use the following events:

  • GoogleCastEventType.LOAD_MEDIA_INFO
  • GoogleCastEventType.LOAD_MEDIA_QUEUE_ITEM
  • GoogleCastEventType.ADD_MEDIA_INFO
  • GoogleCastEventType.ADD_MEDIA_QUEUE_ITEM

The following table shows the expected properties for each event:

Event Name Properties
Key Value Class type
LOAD_MEDIA_INFO GoogleCastComponent.CAST_MEDIA_INFO
GoogleCastComponent.CAST_MEDIA_PLAY_POSITION
MediaInfo Integer
LOAD_MEDIA_QUEUE_ITEM GoogleCastComponent.CAST_MEDIA_QUEUE_ITEM MediaQueueItem
ADD_MEDIA_INFO GoogleCastComponent.CAST_MEDIA_INFO
GoogleCastComponent.CAST_MEDIA_PLAY_POSITION
MediaInfo Integer
ADD_MEDIA_QUEUE_ITEM GoogleCastComponent.CAST_MEDIA_QUEUE_ITEM MediaQueueItem

Alternatively, you can use the following GoogleCastComponent methods:

  • public void loadMediaInfo(MediaInfo mediaInfo, long positionMs)
  • public void loadMediaInfo(MediaInfo mediaInfo)
  • public PendingResult<RemoteMediaClient.MediaChannelResult> loadItem(MediaQueueItem mediaQueue, int playheadPosition)
  • public PendingResult<RemoteMediaClient.MediaChannelResult> addItems(MediaQueueItem... mediaQueue )

Changing the default MediaInfo data

By default, the Cast plugin gathers information about the current Video and Source object emitted by the EventType.SET_SOURCE event. If you want to change or add additional information, such as adding a custom JSON object to your MediaInfo that your App receiver understands, you can do it by overwriting the loadMediaInfo() and addMediaInfo() methods from the BrightcoveCastMediaManager. Then, your BrightcoveCastMediaManager subclass is passed as a constructor parameter to the GoogleCastComponent class.

Inside these methods, you can create your MediaInfo objects and emit the appropriate events as shown previously. Be sure to check com.brightcove.cast.util.CastMediaUtil, as it provides some utility methods to create a MediaInfo from the Video and Source objects.

Configuring the Cast MediaController

To change the controller layout that appears in your Brightcove Video View when a Cast session has started, follow these steps.

  1. Extend the BrightcoveCastMediaManager
  2. Set the MediaControllerConfig
  3. Overwrite the control bar setup

Extend the BrightcoveCastMediaManager

To change the default behavior of the BrightcoveCastMediaManager, create a subclass and overwrite some key methods:

  • public void updateBrightcoveMediaController(boolean isRemote)

    This method is called by the GoogleCastComponent when the session changes; that is, when the session has started or has ended. When the session has started, the isRemote parameter will be true and the setupRemoteController method is called. Otherwise, the isRemote will be false and the resetToLocalController is called.

  • protected void setupRemoteController()

    This method emits the event EventType.SET_MEDIA_CONTROLLER_CONFIG with the MediaControllerConfig object. We will talk more about MediaControllerConfig later in this section. This method also listens to the BrightcoveMediaController.CONTROL_BAR_CREATED event and reacts by calling the setupBrightcoveControlBar method.

  • protected void resetToLocalController()

    This method is responsible for resetting the BrightcoveMediaController to the original controller layout by emitting the EventType.RESTORE_DEFAULT_MEDIA_CONTROLLER.

  • protected void setupBrightcoveControlBar(BrightcoveControlBar controlBar)

    Once the BrightcoveMediaController has been recreated with the layout provided in the MediaControllerConfig, you will get access to the BrightcoveControlBar view. From here, you can access your UI views, like Buttons, where you can add OnClickListener's to them.

Set the MediaControllerConfig

The MediaControllerConfig is a configuration class that you can use to change the default control layout of the BrightcoveMediaController class. In this class, you can set the layout and the OnTouchListener. Once created and configured, you can emit this object as shown below:

Map<String, Object> properties = new HashMap<>();
properties.put(Event.MEDIA_CONTROLLER_CONFIG, myMediaControllerConfig);
eventEmitter.emit(EventType.SET_MEDIA_CONTROLLER_CONFIG,properties);

The default MediaControllerConfig object sets the R.layout.cast_media_controller as the layout with a single Play button. When clicked, it will open a dialog with two options:

  • Play Now - When selected, the loadMediaInfo() method is called and the video will load and play in Chromecast.
  • Add to Queue - When selected, the addMediaInfo() method is called and the video is added to the end of the queue.

Overwrite the control bar setup

When your media controller layout is set by emitting the MediaControllerConfig, the BrightcoveControlBar view will be created and the BrightcoveCastMediaManager.setupBrightcoveControlBar() method will be called. It’s here where you can get your UI components by Id and add the appropriate listeners.

@Override
protected void setupBrightcoveControlBar(BrightcoveControlBar controlBar) {
   Button playButton = controlBar.findViewById(R.id.cast_play);
   if (playButton != null) {
       playButton.setOnClickListener(new View.OnClickListener() {...});
   }
}

Known Issues

Android 9

When using Chromecast with Android 9 and above, you need to include a FOREGROUND_SERVICE permission. This allows the app to use notifications to control a casting session when the app is backgrounded and brought back to the foreground.

The uses-permission tag should be added to the app's AndroidManifest.xml file, as follows:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

Google Play services

The Casting connection may not be created if the sender’s Google Play Services version is not up to date. When the sender’s Google Play Services, in particular the Cast Services framework, are out of date, you may be unsuccessful in creating a Cast connection. This is remedied by updating the sender’s Google Play Services through the Google Play Store.