This article details how to integrate CDN Orchestrator into your mobile app using ExoPlayer on Android.
Not into tutorials? |
Project Setup
Dependencies
The easiest way to get the Streamroot SDK is to add it as a Gradle dependency. We assume you are using Android Studio with the latest tools updates as recommended by Google. If not, drop us a line.
repositories {
maven {
url 'https://sdk.streamroot.io/android'
}
}
Next, add the Streamroot dependency to the build.gradle file of your app module.
implementation 'io.streamroot.lumen.delivery.client:orchestrator-sdk:1.1.4'
Integrate Streamroot SDK
1. Set deliveryClientKey.
Before doing anything, you must set the deliveryClientKey
as a meta-data in the application manifest.
<meta-data
android:name="io.streamroot.lumen.delivery.client.DeliveryClientKey"
android:value="DCKey" />
deliveryClientKey
: Refers to your Lumen unique identifier that you will find in the Account section of the Streamroot Dashboard. If you don't have a deliveryClientKey, you can ask for a free trial on our website.
It is also possible to pass your deliveryClientKey at Lumen Delivery Client instantiation.
2. SDK Initialization
SDK initialization is done preferably in an application context subclass.
- Create subclass of Application or
MultiDexApplication
if your code base is big and you need Kitkat / minSdkVersion 19 support. - Initialize the SDK
class SRApplication: MultiDexApplication() {
override fun onCreate() {
super.onCreate()
LumenDeliveryClient.initializeApp(this)
}
}
- Point to your custom application subclass in the
AndroidManifest.xml
android:name=".SRApplication"
3. Create a new Lumen Delivery Client instance
Now that you have set the deliveryClientKey, you are able to create the Lumen Delivery Client instances.
private fun initDeliveryClient(newPlayer: SimpleExoPlayer) =
LumenDeliveryClient.orchestratorBuilder(applicationContext)
.qosInterface(ExoPlayerQosModule(newPlayer))
.options {
contentID(<string>)
orchestratorProperty(<string>)
deliveryClientKey(<string>)
logLevel(<string>)
}
.build(mStreamUrl)
Builder parameters:
context
: An instance of Context.-
deliveryClientKey
: A method to pass your deliveryClientKey. It will override the one set in the application manifest. qosInterface
: An object that implementsqosInterface
. See section 8 for more details.contentID
: (optional) A parameter to identify the content and make our Dashboard more user friendly. The default value is the stream URL.orchestratorProperty
: (optional) The property to be used for this content. If none is set, we will use "default".logLevel
: (optional) Set the log level: TRACE | CRITICAL | ERROR | WARNING | INFO | DEBUG | OFF. Defaults to OFF.mStreamUrl
: the URL of your stream. The playback will start from this CDN (if available) in order to reduce the start-up time.
4. Start the SDK instance
Calling the start()
method on the DC will start the SDK.
deliveryClient.start()
5. Give your player the new URL
Once you have a running instance of the SDK, you must retrieve the final URL and input it to your player instead of your original one.
val finalUrl = deliveryClient.localUrl()
To maximize compatibility with the SDK we strongly encourage you to allow HTTP <-> HTTPS cross protocol redirects in your ExoPlayer media sources. Check section 9 for more details.
We recommend creating your ExoPlayer media source using the following way :
@SuppressLint("SwitchIntDef")
private fun buildMediaSource(uri: Uri): MediaSource {
val defaultDataSourceFactory =
DefaultHttpDataSourceFactory(
Util.getUserAgent(applicationContext, "StreamrootQA"),
DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS,
true
)
return when (Util.inferContentType(uri)) {
C.TYPE_HLS -> HlsMediaSource.Factory(defaultDataSourceFactory)
//.setDrmSessionManager()
.createMediaSource(uri)
C.TYPE_DASH -> DashMediaSource.Factory(
DefaultDashChunkSource.Factory(
defaultDataSourceFactory
), defaultDataSourceFactory
)
//.setDrmSessionManager()
.createMediaSource(uri)
else -> {
throw IllegalStateException("Unsupported type for url: $uri")
}
}
}
6. Stop the Lumen Delivery Client
Once the video is done playing, you have to stop the SDK you created earlier. Calling the following method will finish the ongoing tasks and release the resources.
deliveryClient.terminate()
7. Integrate the QoS module
This QoS module will link ExoPlayer events to our SDK in order to get accurate metrics regarding the client session.
Here is an example of the class to add to the app in order to do so.
import com.google.android.exoplayer2.ExoPlaybackException
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.source.TrackGroupArray
import com.google.android.exoplayer2.trackselection.TrackSelectionArray
import io.streamroot.lumen.delivery.client.core.LumenQosInterfaceBase
import io.streamroot.lumen.delivery.client.core.LumenVideoPlaybackState
class ExoPlayerQosModule(exoPlayer: ExoPlayer) : LumenQosInterfaceBase(), Player.EventListener {
init { exoPlayer.addListener(this) }
override fun onSeekProcessed() {
super.playerStateChange(LumenVideoPlaybackState.SEEKING)
}
override fun onTracksChanged(trackGroups: TrackGroupArray, trackSelections: TrackSelectionArray) {
super.playerTrackSwitch()
}
override fun onPlayerError(error: ExoPlaybackException) {
super.playerError()
}
override fun onLoadingChanged(isLoading: Boolean) {}
override fun onPositionDiscontinuity(reason: Int) {}
override fun onRepeatModeChanged(repeatMode: Int) {}
override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
when (playbackState) {
Player.STATE_IDLE -> {
super.playerStateChange(LumenVideoPlaybackState.IDLE)
}
Player.STATE_BUFFERING -> {
super.playerStateChange(LumenVideoPlaybackState.REBUFFERING)
}
Player.STATE_READY -> {
super.playerStateChange(if (playWhenReady) LumenVideoPlaybackState.PLAYING else LumenVideoPlaybackState.PAUSED)
}
Player.STATE_ENDED -> {
super.playerStateChange(LumenVideoPlaybackState.ENDED)
}
}
}
}
8. Network security configuration
Starting with Android 9 (API level 28), cleartext support is disabled by default.
As Streamroot SDK works as a local HTTP proxy, it is required to do one of the following
- Activate back cleartext to reactivate all HTTP calls in the AndroidManifest.xml (Not recommended)
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
<application
...
android:usesCleartextTraffic="true">
...
</application>
</manifest>
- Add 'localhost' in the allowed domains in a new network_security_config.xml file
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">localhost</domain>
</domain-config>
</network-security-config>
And add this file in the AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
<application
...
android:networkSecurityConfig="@xml/network_security_config">
...
</application>
</manifest>
More info can be found in the Android documentation.