This guide will walk you through the steps to start a BlendVision One streaming playback on Android TV using the Android player SDK.
Before We Start
Before proceeding, ensure that you have prepared your BlendVision One streaming content. You can prepare your content in two ways:
- Using BlendVision One Console
-
Using BlendVision One API
Step 1: Obtain a Playback Token
- Obtain a valid API token by following the authentication steps
-
Retrieve the
resource_id
andresource_type
of the streaming content you would like to playback. There are two ways to obtain this information:
-
Retrieve using the API
- VOD: /bv/cms/v1/vods
- Livestream: /bv/cms/v1/lives
- Copy from your BlendVision One console
-
Retrieve using the API
-
Obtain the
playback token
using the API: /bv/cms/v1/tokens, with theresource_id
andresource_type
as body parameters-
You can also include
customer_id
in the body parameters if you want to track user playback with your own defined user ID
-
const val API_DOMAIN = "https://api.one.blendvision.com" interface ApiService { @POST("bv/cms/v1/tokens") suspend fun getPlaybackToken( @Header("Authorization") cmsToken: String, // 'Bearer ${CMS token}' @Body request: PlaybackTokenRequest() }: PlaybackTokenResponse } data class PlaybackTokenRequest( @SerializedName("resource_id") val resourceId: String, @SerializedName("resource_type") val resourceType: String, @SerializedName("customer_id") val customerId: String ) data class PlaybackTokenResponse( @SerializedName("token") val playbackToken: String )
Step 2: Start a Playback Session
-
Start a playback session by making a POST request to bv/playback/v1/sessions/<device-id>:start
- Return: drm_server_endpoint
-
Obtain stream data from bv/playback/v1/sessions/<device-id>
- Return: protocol, manifest url, resolutions, thumbnail_seeking_url
const val API_DOMAIN = "https://api.one.blendvision.com" interface ApiService { @POST("bv/playback/v1/sessions/{deviceId}:start") suspend fun startPlaybackSession( @Header("Authorization") playbackToken: String, @Path("deviceId") deviceId: String ): StartSessionResponse @POST("bv/playback/v1/sessions/${deviceId}") suspend fun getStreamInfo( @Header("Authorization") playbackToken: String, @Path("deviceId") deviceId: String ): GetStreamInfoResponse } // data for start a session data class StartSessionResponse( @SerializedName("drm_server_endpoint") val endPoint: String ) // data for get stream information data class GetStreamInfoResponse( @SerializedName("sources") val sources: List ) data class Source( @SerializedName("manifests") val manifests: List, @SerializedName("thumbnail_seeking_url") val thumbnailSeekingUrl: String ) data class Manifest( @SerializedName("protocol") val protocal: String, @SerializedName("url") val url: String, @SerializedName("resolutions") val resolutions: List ) data class Resolution( @SerializedName("height") val height: String, @SerializedName("width") val width: String )
Step 3: Initialize the Player
Obtain Player License Key
Obtain a valid player license key from your BlendVision One console
Install Player SDK
- Download from https://github.com/BlendVision/Android-Player-SDK/releases
-
Create /libs folder in the project root path
-
Add below description in the gradle
implementation fileTree(dir: 'libs', include: ['*.aar'])
-
Add following
.aar
files tolibs/
.// UniPlayer kks-playcraft-daas.aar kks-playcraft-paas.aar // KKSPlayer kksplayer.aar kksplayer-kkdrm.aar kksplayer-library-core-release.aar kksplayer-library-common-release.aar kksplayer-library-extractor-release.aar kksplayer-library-dash-release.aar kksplayer-library-ui-release.aar // KKS-network kks-network.aar
- others (can be imported from public maven)
plugins { ... id 'kotlin-kapt' } api ('com.google.guava:guava:' + guava_version) { // Exclude dependencies that are only used by Guava at compile time // (but declared as runtime deps) [internal b/168188131]. exclude group: 'com.google.code.findbugs', module: 'jsr305' exclude group: 'org.checkerframework', module: 'checker-compat-qual' exclude group: 'com.google.errorprone', module: 'error_prone_annotations' exclude group: 'com.google.j2objc', module: 'j2objc-annotations' exclude group: 'org.codehaus.mojo', module: 'animal-sniffer-annotations' } //Google IMA SDK implementation "com.google.ads.interactivemedia.v3:interactivemedia:$google_ima_version" //Chromecast implementation "com.google.android.gms:play-services-cast-framework:$cast_version" //Coroutines implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" //Okhttp implementation 'com.squareup.okhttp3:okhttp:' + okhttp_version implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version" // gson implementation 'com.google.code.gson:gson:' + gsonVersion // thumbnail implementation "com.github.bumptech.glide:glide:$glideVersion" kapt "com.github.bumptech.glide:compiler:$glideVersion"
-
Set gradle.properties
android.enableDexingArtifactTransform.desugaring=false android.enableJetifier=true
Create a Player
To play the streaming content, create a player instance with the license key and stream information in the initialization configuration:
// create the player instance private var player: UniPlayer? = null player = UniPlayer.Builder( requireContext(), PlayerConfig( license = "YOUR_LICENSE_KEY" ) ).build() val uniTvFragment: UniTvFragment = supportFragmentManager.findFragmentById(R.id.uniTvFragment) as UniTvFragment player?.let { uniTvFragment.setup(it) } // To pass events sent by the TV remote control, // bypass the onKeyUp/onKeyDown events through the following interface. override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { return if (isFragmentHandleOnKeyUpEvent(keyCode, event)) { // If isFragmentHandleOnKeyUpEvent return true, it means we handle the onKeyUp event. // So we return true to tell parent we has handled it. true } else { super.onKeyUp(keyCode, event) } } override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { return if (isFragmentHandleOnKeyDownEvent(keyCode, event)) { // If isFragmentHandleOnKeyUpEvent return true, it means we handle the onKeyUp event. // So we return true to tell parent we has handled it. true } else { super.onKeyDown(keyCode, event) } } private fun isFragmentHandleOnKeyUpEvent(keyCode: Int, event: KeyEvent?): Boolean { return (uniTvFragment as? CustomUniTVFragmentInterface)?.onKeyUp(keyCode, event, null) ?: false } private fun isFragmentHandleOnKeyDownEvent(keyCode: Int, event: KeyEvent?): Boolean { return (uniTvFragment as? CustomUniTVFragmentInterface)?.onKeyDown(keyCode, event, null) ?: false } // load stream information player?.load( MediaConfig( source = listOf( MediaConfig.Source( url = streamInfoResponse.sources[0].manifests.filter { it.protocol == MediaConfig.Protocol.DASH }[0].url, protocal = MediaConfig.Protocol.DASH, drm = MediaConfig.DrmInfo.Widevine( licenseUrl = startSessionResponse.endPoint, headers = mapOf("" to "") ) ) ), title = "", imageUrl = "", thumbnailSeekingUrl = "" ) ) // start playback player?.start() // pause/play during playback player?.pause() player?.play() // release the player player?.release()
Step 4: Manage the Lifecycle of a Playback Session
- To keep the playback session alive, periodically invoke the heartbeat API every 10 seconds
const val API_DOMAIN = "https://api.one.blendvision.com" interface ApiService { // post this API every 10 seconds @POST("bv/playback/v1/sessions/${deviceId}:heartbeat") suspend fun Heartbeat( @Header("Authorization") playbackToken: String, @Path("deviceId") deviceId: String ) @POST("bv/playback/v1/sessions/${deviceId}:end") suspend fun EndPlaybackSession( @Header("Authorization") playbackToken: String, @Path("deviceId") deviceId: String ) }
What's More
- To control the playback, refer to the API Reference documentation.
- To track the playback, refer to the Playback Event documentation.
- To ensure a smooth playback experience, check the compatible operating systems.
- To enable advanced features, refer to the following guides: