首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Android Media3会话和控制器-播放未启动

Android Media3会话和控制器-播放未启动
EN

Stack Overflow用户
提问于 2022-10-11 23:59:06
回答 1查看 189关注 0票数 4

我正在尝试实现Android、Media3、MediaSessionService和MediaController,但由于某种原因,播放没有启动。我做错了什么?我想我所做的一切都和在后台播放媒体中描述的完全一样。

PlaybackService.kt

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
<?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>

下面是调试日志:

代码语言:javascript
运行
复制
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

因此,看起来好像在第一次单击播放器缓冲区之后,然后立即结束,在第二次单击之后,它只是无限期地缓冲。有谁知道有什么问题吗?

EN

回答 1

Stack Overflow用户

发布于 2022-10-12 08:35:34

最后,多亏了本期这个问题,我找到了解决方案。似乎Media3指南缺少了一个非常关键的部分。

来自onAddMediaItems文档:Note that the requested media items don't have a MediaItem.LocalConfiguration (for example, a URI) and need to be updated to make them playable by the underlying Player.

最后,我通过重写MediaSession.Callback.onAddMediaItems来解决这个问题

代码语言:javascript
运行
复制
class PlaybackService : MediaSessionService(), MediaSession.Callback {

    private var mediaSession: MediaSession? = null

    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).setCallback(this).build()
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
        mediaSession

    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
        super.onDestroy()
    }

    override fun onAddMediaItems(
        mediaSession: MediaSession,
        controller: MediaSession.ControllerInfo,
        mediaItems: MutableList<MediaItem>
    ): ListenableFuture<MutableList<MediaItem>> {
        val updatedMediaItems = mediaItems.map { it.buildUpon().setUri(it.mediaId).build() }.toMutableList()
        return Futures.immediateFuture(updatedMediaItems)
    }
}

然后替换

代码语言:javascript
运行
复制
controller.setMediaItem(MediaItem.fromUri(url))

通过

代码语言:javascript
运行
复制
val media = MediaItem.Builder().setMediaId(url).build()
controller.setMediaItem(media)
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74035158

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档