Video Player
This guide will walk you through the steps to start a BlendVision One streaming playback using the Android player SDK.
Additionally, you can also check out the sample project for an even smoother start: https://github.com/BlendVision/Android-Player-SDK/tree/main
Prerequisites
- A valid BlendVision Player License Key
Step 1: Initialize the Player
Install Player SDK
- Download latest Player SDK 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 to libs/.
// 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-analytics
kks-analytics.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"
//Retrofit2
implementation "com.squareup.retrofit2:retrofit:$retrofit2_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit2_version"
// gson
implementation 'com.google.code.gson:gson:' + gsonVersion
// thumbnail
implementation "com.github.bumptech.glide:glide:$glideVersion"
kapt "com.github.bumptech.glide:compiler:$glideVersion"
//koin
implementation "io.insert-koin:koin-android:$koinVersion"
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:
- Android
- Android TV
// create the player instance
private var player: UniPlayer? = null
player = UniPlayer.Builder(
requireContext(),
PlayerConfig(
license = "YOUR_LICENSE_KEY"
)
).build()
// set player view type
binding.kksPlayerServiceView.setupControlPanel(
defaultContentType = ContentType.STANDALONE
)
// bind the new created player to player view
binding.playerView.setUnifiedPlayer(player)
// 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()
// 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 2: Obtain a Playback Token
Make a POST request to the API: POST /bv/cms/v1/tokens
Query Parameters | Required | Description |
---|---|---|
resource_id | Required | The unique ID of your content. You can retrieve this in two ways: 1. Using BlendVision One CMS - VOD > Publishing Tools - Live > Publishing Tools 2. Using BlendVision One API - VOD: GET /bv/cms/v1/vods - Live: GET /bv/cms/v1/lives |
resource_type | Required | The streaming type of your content. |
customer_id | Optional | Your custom user ID to identify who is watching the content. This parameter is required if the content's security privacy is set to SECURITY_PRIVACY_TYPE_TOKEN . |
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 3: Start a Playback Session
Start a playback session and obtain the stream data using the APIs:
Replace the sample URLs in Step 1 with the obtained manifest_url
(dashUrl
) for playback.
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 4: Manage the Lifecycle of a Playback Session
To keep the playback session alive, periodically invoke the heartbeat API every 10 seconds:
POST /bv/playback/v1/sessions/:device_id:heartbeat
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
)
}