I'm trying to implement the Android Media3 MediaSessionService and MediaController but for some reason the playback doesn't start. What am I doing wrong? I think I did everything exactly as described in Play media in the background.
PlaybackService.kt
class PlaybackService : MediaSessionService() {
private var mediaSession: MediaSession? = null
override fun onCreate() {
super.onCreate()
val player = ExoPlayer.Builder(this).build()
mediaSession = MediaSession.Builder(this, player).build()
}
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
mediaSession
override fun onDestroy() {
mediaSession?.run {
player.release()
release()
mediaSession = null
}
super.onDestroy()
}
}
MainActivity.kt
class MainActivity : ComponentActivity() {
private lateinit var controllerFuture: ListenableFuture<MediaController>
private lateinit var controller: MediaController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
log("onCreate MainActivity")
setContent {
TestMediaTheme {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
Button(onClick = {
//val url = "android.resource://$packageName/${R.raw.test}"
val url = "https://download.samplelib.com/mp3/sample-15s.mp3"
play(url)
}) {
Text(text = "Play")
}
}
}
}
}
override fun onStart() {
super.onStart()
val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
controllerFuture.addListener(
{
controller = controllerFuture.get()
initController()
},
MoreExecutors.directExecutor()
)
}
override fun onStop() {
MediaController.releaseFuture(controllerFuture)
super.onStop()
}
private fun initController() {
//controller.playWhenReady = true
controller.addListener(object : Player.Listener {
override fun onMediaMetadataChanged(mediaMetadata: MediaMetadata) {
super.onMediaMetadataChanged(mediaMetadata)
log("onMediaMetadataChanged=$mediaMetadata")
}
override fun onIsPlayingChanged(isPlaying: Boolean) {
super.onIsPlayingChanged(isPlaying)
log("onIsPlayingChanged=$isPlaying")
}
override fun onPlaybackStateChanged(playbackState: Int) {
super.onPlaybackStateChanged(playbackState)
log("onPlaybackStateChanged=${getStateName(playbackState)}")
}
override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
log("onPlayerError=${error.stackTraceToString()}")
}
override fun onPlayerErrorChanged(error: PlaybackException?) {
super.onPlayerErrorChanged(error)
log("onPlayerErrorChanged=${error?.stackTraceToString()}")
}
})
log("start=${getStateName(controller.playbackState)}")
log("COMMAND_PREPARE=${controller.isCommandAvailable(COMMAND_PREPARE)}")
log("COMMAND_SET_MEDIA_ITEM=${controller.isCommandAvailable(COMMAND_SET_MEDIA_ITEM)}")
log("COMMAND_PLAY_PAUSE=${controller.isCommandAvailable(COMMAND_PLAY_PAUSE)}")
}
private fun play(url: String) {
log("play($url)")
log("before=${getStateName(controller.playbackState)}")
controller.setMediaItem(MediaItem.fromUri(url))
controller.prepare()
controller.play()
log("after=${getStateName(controller.playbackState)}")
}
private fun getStateName(i: Int): String? {
return when (i) {
1 -> "STATE_IDLE"
2 -> "STATE_BUFFERING"
3 -> "STATE_READY"
4 -> "STATE_ENDED"
else -> null
}
}
private fun log(message: String) {
Log.e("=====[TestMedia]=====", message)
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.TestMedia"
tools:targetApi="33">
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.TestMedia">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<service
android:name=".PlaybackService"
android:exported="true"
android:foregroundServiceType="mediaPlayback">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService" />
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
</application>
</manifest>
And here's the debug log:
01:51:22.004 E onCreate MainActivity
01:51:22.544 E start=STATE_IDLE
01:51:22.544 E COMMAND_PREPARE=true
01:51:22.544 E COMMAND_SET_MEDIA_ITEM=true
01:51:22.544 E COMMAND_PLAY_PAUSE=true
//click 1
01:51:24.027 E play(https://download.samplelib.com/mp3/sample-15s.mp3)
01:51:24.027 E before=STATE_IDLE
01:51:24.029 E onPlaybackStateChanged=STATE_BUFFERING
01:51:24.029 E after=STATE_BUFFERING
01:51:24.053 E onPlaybackStateChanged=STATE_ENDED
//click 2
01:51:25.715 E play(https://download.samplelib.com/mp3/sample-15s.mp3)
01:51:25.715 E before=STATE_ENDED
01:51:25.716 E onPlaybackStateChanged=STATE_BUFFERING
01:51:25.716 E after=STATE_BUFFERING
//click 3
01:51:26.749 E play(https://download.samplelib.com/mp3/sample-15s.mp3)
01:51:26.749 E before=STATE_BUFFERING
01:51:26.750 E after=STATE_BUFFERING
//click 4
01:51:30.172 E play(https://download.samplelib.com/mp3/sample-15s.mp3)
01:51:30.172 E before=STATE_BUFFERING
01:51:30.173 E after=STATE_BUFFERING
So it looks as if after the first click the player buffers and then immediately ends, and after the second click it just buffers indefinitely. Anyone got an idea what might be the problem?
setCallback
inonCreate
– Busily