1. Add the maven repository to your project level build.gradle
allprojects {
repositories {
...
maven { url "https://avvpl-staging.sportradar.com/dist/android/latest/" }
maven { url 'http://bitmovin.bintray.com/maven' }
...
}
}
KNOWN ISSUE: Bitmovin Analytics integration demands adding the bitmovin maven repository. Fixing this issue is in progress.
2. Add the avvpl dependency to your app module build.gradle
implementation("ag.sportradar.android:avvplayermarvin:X.X.X")
The newest Version can be found here
val licence = AVVLicence.Builder(this, "[your_licence_key]")
.domain("[www.yourlicencedomain.com]") //if required
.bundle("[com.your.appid]") //optional: it will pick the applictiationId per default
.listener(object : AVVLicenceCheckListener {
override fun onLicenceValidated(valid: Boolean) {
// player should not be built before licence is validated.
}
})
.build()
AVVSettings.instance.checkLicence(licence)
1. Instantiate the Player
val player = AVVPlayerBuilder(context) //your Activity's context
.setPlayerContainer(playerContainer) //the viewgroup that contains the player
.build()
2. Start the video passing a video configuration to the player
player.setup(AVVConfigUrl("[URL to the OTT videoconfig]"))
or
player.setup(AVVStreamUrl("https://cdn.theoplayer.com/video/elephants-dream/playlist.m3u8"))
or
player.setup(AVVConfig.Builder(0)
.streamUrl("https://cdn.theoplayer.com/video/elephants-dream/playlist.m3u8")
.build())
The AVVPlayer instance needs to react to certain lifecycle events of the application, such as onPause(), onResume() and onDestroy().
class MainActivity : AppCompatActivity() {
override fun onPause() {
player.onActivityPause()
super.onPause()
}
override fun onResume() {
player.onActivityResume()
super.onResume()
}
override fun onDestroy() {
player.onActivityDestroy()
super.onDestroy()
}
}
POSSIBLE ISSUE:
player.onActivityDestroy()
calls Exoplayers stop()
and release()
method.
As release()
is known for blocking the UI Thread until all resources are released,some
devices (especially lowend devices) might appear to freeze for 1-3 seconds. If you are experiencing these issues try calling
player.onActivityDestroy(killPlaybackThread = false)
instead.
In order for the AVVPlayer to continue playback through orientation changes you need to enable configChanges in the Activity that holds the player.
1. Add the following line to your activity in the AndroidManifest.xml
<activity
android:name=".YourPlayerActivity"
android:configChanges="orientation|screenSize|screenLayout">
...
</activity>
2. Override onConfigurationChanged in your Activity/Fragment that holds the player
override fun onConfigurationChanged(newConfig: Configuration) {
player.onConfigurationChanged(newConfig)
super.onConfigurationChanged(newConfig)
}
The player fetches its configuration from the URL that is passed in the setUp() call. The configuration determines the looks and the behavior of the player, however if you wish to change any of this behavior or need to add necessary information to the player configuration you can do so by implementing AVVConfigAdaptationCallback and adding it to the player.setup() call
player.setup(config, object : AVVConfigAdaptationCallback() {})
- Adding request headers to streamaccess
player.setUp(config, object : AVVConfigAdaptationCallback() {
override fun adaptConfig(config: AVVConfig) {
//...
config.streamUrlProviderInfo.requestData = AVVPostRequestData(mapOf(Pair("authorization", "your auth token")))
//...
}
})
- Changing autoplay behavior
player.setUp(config, object : AVVConfigAdaptationCallback() {
override fun adaptConfig(config: AVVConfig) {
//...
config.playbackOptions.autoPlay = true
//...
}
})
- Add heartbeat
player.setUp(config, object : AVVConfigAdaptationCallback() {
override fun adaptConfig(config: AVVConfig) {
//...
config.heartbeat = AVVHeartbeat.Builder()
.enabled(true)
.time(30) //seconds
.ticket("[your heartbeat ticket]")
.validationPath("https://yourvalidation.com/validation")
.build()
//...
}
})
class MyErrorOverlay : AVVErrorOverlayDelegate {
override fun onCreateErrorView(parent: ViewGroup, error: AVVError): View {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater.inflate(R.layout.my_error_layout, parent, false)
// display the error data contained in the AVVError as you like.
return view
}
}
val player = AVVPlayerBuilder(activity)
.setPlayerContainer(playerContainer)
.setCustomErrorOverlay(MyErrorOverlay())
.build()
class MyCustomControls : AVVControlOverlayDelegate {
override fun onCreateErrorView(parent: ViewGroup, error: AVVError): View {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater.inflate(R.layout.layout_videoplayer_controls, parent, false)
controlBinding.bindPlayPauseButton(
view.findViewById(R.id.playPauseButton),
R.drawable.ic_avv_play,
R.drawable.ic_avv_pause, config.skin
)
controlBinding.bindFullscreenButton(
view.findViewById(R.id.fullscreenButton),
R.drawable.ic_avv_fullscreen_enter,
R.drawable.ic_avv_fullscreen_exit
)
controlBinding.bindLiveIndicatorView(
view.findViewById(R.id.liveindicator),
R.drawable.ic_avv_live_indicator,
config.skin
)
// for more bindings check MyCustomControls.kt in DemoApplication
return view
}
}
val player = AVVPlayerBuilder(activity)
.setPlayerContainer(playerContainer)
.setControlOverlay(MyCustomControls())
.build()
class DemoCastOptionsProvider: AVVCastOptionsProvider() {
override fun getReceiverApplicationId(context: Context): String {
return "YOUR_CAST_RECEIVER_ID"
}
}
<application>
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="path.to.your.AVVCastOptionsProvider"
tools:replace="android:value" />
</application>
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
//...
CastContext.getSharedInstance(this)
//...
}
}
<fragment
android:id="@+id/miniControllerFragment"
class="ag.sportradar.avvplayer.player.chromecast.widgets.AVVDefaultCastMiniController"
android:layout_width="match_parent"
android:layout_height="wrap_content" />