Media Session Integration

This guide shows how to set up the media session integration with the Bitmovin Player Android SDK. Media sessions provide a universal way of interacting with an audio or video player. Integrating with the media session allows an app to advertise media playback externally, e.g. on the lock screen or in the notification area.

Prerequisites

  • Basic understanding of Android development with Kotlin
  • Bitmovin Player Android SDK added to your project (see Getting Started guide)

Add Required Permissions in the AndroidManifest.xml

Add the necessary permissions FOREGROUND_SERVICE and FOREGROUND_SERVICE_MEDIA_PLAYBACK to the manifest. The INTERNET permission should be already present, after following the Getting Started guide.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
    ...
</manifest>

Create a MediaSessionPlaybackService

The MediaSessionPlaybackService is responsible for managing the player instance and creating a MediaSession instance that is populated to the Android system. Through the MediaSession, the system can interact with the Bitmovin Player instance managed by this service. The service also automatically takes care of showing a playback notification in the notification area and on the lock screen.

class MediaSessionPlaybackService : MediaSessionService() {
    inner class ServiceBinder : Binder() {
        val player get() = [email protected]
        fun connectSession() = addSession(mediaSession)
        fun disconnectSession() = removeSession(mediaSession)
    }

    private val binder = ServiceBinder()
    private lateinit var player: Player
    private lateinit var mediaSession: MediaSession

    override fun onGetSession(controllerInfo: ControllerInfo) = mediaSession

    override fun onCreate() {
        super.onCreate()
        player = Player(
            this, PlayerConfig(
                playbackConfig = PlaybackConfig(
                    handleAudioFocus = true
                )
            )
        )
        mediaSession = MediaSession(
            this,
            mainLooper,
            player,
        )
    }

    override fun onDestroy() {
        mediaSession.release()
        player.destroy()

        super.onDestroy()
    }

    override fun onBind(intent: Intent?): IBinder {
        super.onBind(intent)
        return binder
    }
}

Register MediaSessionPlaybackService in AndroidManifest.xml

Declare the MediaSessionPlaybackService in the manifest, inside the <application> tag:

<service
    android:name=".MediaSessionPlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

Use the Service in Your Apps Playback Activity

The final step is to bind to the service in your playback activity. You can find a complete implementation of this in our MediaSessionKotlin sample application. The important parts are:

1. Bind to the MediaSessionPlaybackService

The intent actions to start the service needs to be Intent.ACTION_MEDIA_BUTTON. Once a connection to the service is established, the player instance that is managed by the service can be accessed through the MediaSessionPlaybackService.ServiceBinder.

override fun onStart() {
  super.onStart()
  bindService()
}

private fun bindService() {
  val intent = Intent(this, MediaSessionPlaybackService::class.java)
  intent.setAction(Intent.ACTION_MEDIA_BUTTON)
  bindService(intent, connection, Context.BIND_AUTO_CREATE)
  startService(intent)
}

private val connection = object : ServiceConnection {
  override fun onServiceConnected(className: ComponentName, service: IBinder) {
    // We've bound to the Service, cast the IBinder and get the Player instance
    val binder = service as MediaSessionPlaybackService.ServiceBinder
    val player = binder.player ?: throw IllegalStateException("Player is null")

    // Add the player to the player view
    playerView.player = player

    if (player.source == null) {
      // Load a source into the player if there is the need
    }
  }
}

2. Detach the Player From the PlayerView When Going Into Background

In order to prevent the player from being paused when the app is minimized, the player instance needs to be detached from the player view when this happens. The player itself is managed by the service, so playback continues and can be controlled through the media controls on the lock screen or in the notification area.

override fun onPause() {
  // Detach the Player to decouple it from the PlayerView lifecycle
  playerView.player = null
  playerView.onPause()
  super.onPause()
}

override fun onResume() {
  super.onResume()

  // Attach the Player to allow the PlayerView to control the player.
  playerView.player = player
  playerView.onResume()
}

Test Your Media Session Integration

Your app should now be correctly set up to populate a MediaSession. When a source is loaded and played back in the player, a notification should showing up that allows to control playback through the MediaSession. Also the lock screen should be presented with media controls.

Keep in mind that this guide only outlines key parts of the implementation. A full implementation can be found in our MediaSessionKotlin sample application.