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.
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)
}
}
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 Name | Type | Mandatory? | Description |
|---|---|---|---|
| mediaID | String | Yes | The unique id of the content to cast. |
| consumptionType | ConsumptionType | Yes | The ConsumptionType of the content. It can be ConsumptionType.LIVE or ConsumptionType.VOD. |
| catalogType | String | Yes | The catalog type of the content. |
| mediaType | MediaType | Yes | The MediaType of the content. It can be MediaType.DASH, MediaType.SMOOTH_STREAMING or MediaType.HLS. |
| drmScheme | DrmType | No | The DrmType of the content. Default value for the Android platform is DrmType.WIDEVINE. |
| playbackMode | PlaybackMode? | No | One of the PlaybackMode values i.e. PlaybackMode.LIVE, PlaybackMode.RESTART or PlaybackMode.CATCHUP. This is mandatory only for the live playback. |
| eventId | String? | No | The unique identifier of the live event/channel. |
| startTime | String? | No | The live event start time in ISO 8601 format(in milliseconds). This is mandatory when playing in restart and catchup modes, otherwise not required. |
| endTime | String? | No | The live event end time in ISO 8601 format(in milliseconds). This is mandatory when playing in catchup mode, otherwise not required.. |
| initialPlaybackPositionMs | Long | No | The 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 Name | Type | Mandatory? | Description |
|---|---|---|---|
| userAuthToken | String | Yes | The user authentication token. |
| platformAsset | CastPlatformAsset | Yes | The CastPlatformAsset object associated with the playback. |
| headers | Map<String, String>? | No | The Key-Value pair sent as HTTP Request Header. |
| flAnalyticsData | Map<String, Any>? | No | Any additional analytics attributes that has to be shared with chromecast receiver. |
| videoAnalyticsData | Map<String, Any>? | No | Any additional video analytics metadata that has to be shared with chromecast receiver. |
| customAdMetadata | Map<String, Any>? | No | Any 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.