Skip to main content

Chromecast

Chromecast on the Quickplay platform qualifies as a device on its own and can perform device registration, content authorization, and playback similar to any other conventional device. The client sender facilitates sharing required information for device registration and content authorization to the receiver, and the receiver processes the information for playback.

Overview

Casting media using the Quickplay platform requires you to perform the following steps in sequence:

  • Initialize Cast Manager
  • Send User Device Information message to Receiver
  • Load Media onto Receiver

You must perform the above sequence of steps for a new cast session, including both Connect & Play and Play & Connect scenarios.

info

We recommend initializing the cast manager and sharing user device information during application launch, and loading media on demand.

Cast Manager

CastManager uses the Google Cast Application Framework's (CAF) SessionManager instance to manage CastSession and coordinate all Cast interactions. CastManager provides access to CastPlayer that helps you initiate and control playback on the Web Receiver Application, and enforces policies for accessing Quickplay Platform resources.

Initialize CastOptions

Your Sender Application must implement the com.google.android.gms.cast.framework.OptionsProvider interface to supply CastOptions needed to initialize the CastContext singleton. The most important option is the Receiver Application ID, which filters Cast Device discovery results and launches the Receiver Application when a CastSession starts.


import android.content.Context
import com.google.android.gms.cast.framework.OptionsProvider

class CastOptionsProvider : OptionsProvider {

override fun getCastOptions(context: Context): CastOptions {
return CastOptions.Builder()
.setReceiverApplicationId(context.getString(R.string.app_receiver_id))
.build()
}

override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
return null
}
}

You must then declare the OptionsProvider within the "application" tag of your app's AndroidManifest.xml file:


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

Initialize CastManager

Initializing the CastManager is the first and foremost step in setting up Chromecast. We recommend performing initialization right at application launch. CastManager uses CAF's global singleton object, the CastContext, which coordinates all Cast interactions. When you create the CastManager instance, your Sender Application must also register CastManagerStateListener that provides notifications for the CastManager and CAF's CastSession events.


class CastSampleActivity : AppCompatActivity() {

private lateinit var castManager: CastManager
private var castManagerStateListener: CastManagerStateListener = object : CastManagerStateListener {
override fun onCastDeviceAvailable() {
//Handle as required
}
override fun onSessionStarted(castDeviceID: String) {
//Handle as required
}
override fun onSessionStartError(error: Error) {
//Handle as required
}
override fun onSessionSuspended() {
//Handle as required
}
override fun onSessionResumed(castDeviceID: String) {
//Handle as required
}
override fun onSessionEnded(error: Error) {
//Handle as required
}
override fun onDeviceRegistrationError(error: Error) {
//Handle as required
}
override fun onDeviceRegistered() {
//Handle as required
}
override fun onPlaybackAuthorisationError(error: Error) {
//Handle as required
}
override fun onPlaybackAuthorised() {
//Handle as required
}
override fun onCastManagerInitializationSuccess() {
//Handle as required
}
override fun onCastManagerInitializationFailure(error: Error) {
//Handle as required
}
override fun onCustomError(error: Error){
//Handle as required
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_cast_sample)
initViews()
castManager = CastManager(this) // pass context
castManager.registerCastManagerStateListener(castManagerStateListener)
}
}

note

In some cases, you can't initialize CastManager because of an incompatible device or Google Play Services version. The registered CastManagerStateListener will notify you of any failure or success in initialization of CastManager.

Cast Session Management

For the CAF, a CastSession combines the steps of connecting to a device, launching (or joining), connecting to the Receiver Application, and initializing a media control channel if appropriate. The media control channel is how the Cast framework sends and receives messages from the Receiver Media Player. The CastSession starts automatically when you click the Cast button and select a device from the list, and stops automatically when you disconnect. The Cast SDK also automatically handles reconnecting to a receiver session due to networking issues.

Device Registration

Your Sender Application requires a CastPlayer instance to initiate content playback, but before casting any media, you must register the Cast Device with the Quickplay Platform. The sender application receives the Device Registration status via the attached CastManagerStateListener.

Send User Device Information

Once you've established a session successfully, you must share the User Device Information with the Receiver. This is a mandatory step so that the receiver can register the device with the Quickplay platform and perform authorization while loading media.

You should call CastManager.registerUserData to perform the user data registration, which includes the Cast Device ID. You should perform the registration just after CastManager notifies your Sender Application that the CastSession has started. After the user data registration, your Sender Application should proceed with loading media on the Receiver Application. If the user data registration fails, your Sender Application receives a notification with an error when you make the first attempt to load media on the Receiver Application.


private var castManagerStateListener: CastManagerStateListener = object : CastManagerStateListener {
private fun defaultClient(): PlatformClient {
return object : PlatformClient {
override val id: String = "client-id"
override val type: String = "androidmobile"
}
}

private suspend fun acquireUserAuthorizationData(): Result<UserAuthorizationData, Error> =
suspendCancellableCoroutine { continuation ->
acquireUserAuthorizationDataInner(
Callback { userAuthData, error ->
userAuthData?.let {
continuation.resume(Result.Success(it))
} ?: error?.let {
continuation.resume(Result.Failure(it))
}
}
)
}

private fun acquireUserAuthorizationDataInner(
callback: Callback<UserAuthorizationData, Error>
) =
platformAuthorizer.userAuthorizationDelegate.fetchUserAuthorizationToken(
Callback { authData, error ->
check(authData != null || error != null) {
"Either UserAuthorizationData instance or Error instance should be non-null"
}
authData?.let {
callback.complete(it, null)
} ?: error?.let {
callback.complete(null, it)
}
}
)

override fun onSessionStarted(castDeviceID: String) {
val coroutineScope = CoroutineScope(Dispatchers.IO)
coroutineScope.launch {
when (val result = acquireUserAuthorizationData()) {
is Result.Success -> {
authToken = result.value.accessToken
val platformClient = defaultClient()
withContext(Dispatchers.Main) {
castManager.registerUserData(
platformClient,
castDeviceID,
authToken
)
}
}
is Result.Failure -> {
val error = result.value
logger.warn { "Failed to acquire user access token: $error" }
}
}
}
}
}

Load and start playback

The CastPlayer is analogous to the remote media player of the Receiver Application. Your Sender Application can retrieve a CastPlayer instance from the CastManager once the CastSession is established. You should then create a PlatformAsset that describes the media that needs to be authorized with the Quickplay Platform and played upon successful authorization. You should also provide MediaMetadata and, optionally, a collection of MediaTracks while loading media.

CastPlatformAsset and CastAsset

CastPlatformAsset encapsulates all the information required by the receiver to authorize the content and start playback.

Property NameTypeMandatory?Description
mediaIDStringYesThe unique id of the content to cast.
consumptionTypeConsumptionTypeYesThe ConsumptionType of the content. It can be ConsumptionType.LIVE or ConsumptionType.VOD.
catalogTypeStringYesThe catalog type of the content.
mediaTypeMediaTypeYesThe MediaType of the content. It can be MediaType.DASH, MediaType.SMOOTH_STREAMING or MediaType.HLS.
drmSchemeDrmTypeNoThe DrmType of the content. Default value for the Android platform is DrmType.WIDEVINE.
playbackModePlaybackMode?NoOne of the PlaybackMode values i.e. PlaybackMode.LIVE, PlaybackMode.RESTART or PlaybackMode.CATCHUP. This is mandatory only for the live playback.
eventIdString?NoThe unique identifier of the live event/channel.
startTimeString?NoThe live event start time in ISO 8601 format(in milliseconds). This is mandatory when playing in restart and catchup modes, otherwise not required.
endTimeString?NoThe live event end time in ISO 8601 format(in milliseconds). This is mandatory when playing in catchup mode, otherwise not required..
initialPlaybackPositionMsLongNoThe playback position for cast player to start playback from (in milliseconds). By default, the playback will start from 0 for VOD and live edge for live playbacks.
val castPlatformAsset = CastPlatformAsset(
"myMediaID", // pass preferred value
ConsumptionType.LIVE, // pass preferred value
"myCatalogType", // pass preferred value
MediaType.DASH, // pass preferred value
DrmType.WIDEVINE, // pass preferred value
PlaybackMode.RESTART, // pass preferred value. mandatory for any LIVE playback.
"myEventID", // pass preferred value
"9999-12-30T12:59:59Z", // program start time in yyyy-MM-dd'T'HH:mm:ss'Z' format. mandatory for RESTART and CATCHUP playbacks.
"9999-12-31T12:59:59Z", // program end time in yyyy-MM-dd'T'HH:mm:ss'Z' format. mandatory for RESTART and CATCHUP playbacks.
)

CastAsset binds additional information along with the CastPlatformAsset. You must pass this object to the loadMedia API to start playback.

Property NameTypeMandatory?Description
userAuthTokenStringYesThe user authentication token.
platformAssetCastPlatformAssetYesThe CastPlatformAsset object associated with the playback.
headersMap<String, String>?NoThe Key-Value pair sent as HTTP Request Header.
flAnalyticsDataMap<String, Any>?NoAny additional analytics attributes that has to be shared with chromecast receiver.
videoAnalyticsDataMap<String, Any>?NoAny additional video analytics metadata that has to be shared with chromecast receiver.
customAdMetadataMap<String, Any>?NoAny additional ad metadata that has to be shared with chromecast receiver.
// any additional custom metadata for analytics. below is the expected schema for New Relic:
val flAnalyticsData = mapOf(
"appBuild" to "testId",
"channelID" to "myChannelID",
"channelName" to "myChannelName",
"contentID" to "myContentID",
"contentName" to "myContentName",
"pageID" to "myContent",
"seriesName" to "mySeriesName",
"serviceID" to "myServiceID",
"silentLogin" to "true",
"tabName" to "myTabName"
)

// any additional custom metadata for video analytics. below is the expected schema for Conviva:
val videoAnalyticsData = mapOf(
"viewerId" to "userID / mobileNumber / emailId", // mandatory
"assetName" to "myAssetName", // mandatory
"tags" to mapOf("customTagKey" to "customTagValue") // optional key value pairs to provide additional information about the content.
)

// any additional custom metadata for ads. below is the expected schema:
val customAdMetadata = mapOf(
"rdId" to "myAndroidAdID", // mandatory - resettable device ID.
"idType" to "adid", // mandatory - the type of device identifier (always adid for Android).
"isLat" to "0" // mandatory - boolean flag to limit ad tracking (pass preferred value - 0 or 1)
)

val castAsset = CastAsset(
authToken, // user authentication token from UMS
castPlatformAsset, // corresponding castPlatformAsset object
headers, // any optional HTTP headers to be passed
flAnalyticsData, // optional custom metadata for analytics with New Relic
videoAnalyticsData, // optional custom metadata for video analytics with Conviva
customAdMetadata // optional custom metadata for ad supported content
)

After building the CastAsset and MediaMetadata objects along with a successful CastSession, your sender application can start loading using the CastPlayer.loadMedia API.

val mediaMetadata = MediaMetadata(
MediaMetadata.MEDIA_TYPE_MOVIE // use appropriate media type. Refer com.google.android.gms.cast.MediaMetadata.
)
castManager.castPlayer?.let {
it.loadMedia(castAsset, mediaMetadata)
}

The Receiver Application receives this information and attempts to authorize the asset with the Quickplay Platform and plays the content.

Cast Player UI

The CastPlayer extends the Player interface and provides the benefit of having the same APIs as the conventional Player from the FL Player library. You have complete control over UI rendering and can integrate the Google CAF SDK directly and use the UI widgets from the CAF SDK. Most integrators prefer using their own UI for the player, which can function as both a remote Cast player and a local player. This makes the player integration straightforward because both players have the same interface.