首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >用media3播放背景音乐

用media3播放背景音乐
EN

Stack Overflow用户
提问于 2022-07-20 13:08:53
回答 1查看 488关注 0票数 1

我一直试图在后台使用系外播放器media3库播放音频。因此,到目前为止,我能够播放音频,它也是在后台播放,但我一直保持url是一个测试url是静态的。现在,我想把url传递给我的服务类,但我无法理解该如何做。

在这里附加我的代码文件..。

代码语言:javascript
运行
复制
class PlaybackService : MediaSessionService() {
    // Create your Player and MediaSession in the onCreate lifecycle event
    lateinit var player: Player
    private var mediaSession: MediaSession? = null
    override fun onCreate() {
        super.onCreate()
        player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).build()
        player.prepare()
        player.playWhenReady = true
        player.seekTo(0, 0)
        val mediaItem =
            MediaItem.fromUri("https://www.learningcontainer.com/wp-content/uploads/2020/05/sample-mp4-file.mp4")
        player.setMediaItem(mediaItem)
    }

    // Return a MediaSession to link with the MediaController that is making
    // this request.
    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
        mediaSession

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

这是我的碎片

代码语言:javascript
运行
复制
class MusicPlayerFragment : BaseFragment() {


private lateinit var controllerFuture: ListenableFuture<MediaController>
private val controller: MediaController?
    get() = if (controllerFuture.isDone) controllerFuture.get() else null

private val currentWindow = 0
var playerStopped = true
private val playbackPosition: Long = 0


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

private fun playMedia() {
    val sessionToken = SessionToken(
        requireContext(),
        ComponentName(requireContext(), PlaybackService::class.java)
    )
    controllerFuture =
        MediaController.Builder(requireContext(), sessionToken)
            .buildAsync()
    controllerFuture.addListener({
        fragmentMusicPlayerBinding.playerView.player = controllerFuture.get()
        fragmentMusicPlayerBinding.controls.player = controllerFuture.get()
    }, MoreExecutors.directExecutor())
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
   
    fragmentMusicPlayerBinding.imgPlay.setOnClickListener {
        if (playerStopped) {
            playerStopped = false
            playMedia()
        }
    }
    fragmentMusicPlayerBinding.imgStop.setOnClickListener {
        stopMediaPlayer()
    }

    fragmentMusicPlayerBinding.imgPause.setOnClickListener {
        fragmentMusicPlayerBinding.playerView.player?.pause()
    }

    fragmentMusicPlayerBinding.imgRewind.setOnClickListener {
        fragmentMusicPlayerBinding.playerView.player?.seekTo(0)
    }
    
}

private fun stopMediaPlayer() {
    controllerFuture.get().stop()
    controllerFuture.get().playWhenReady = false
    MediaController.releaseFuture(controllerFuture)
    playerStopped = true
    fragmentMusicPlayerBinding.playerView.player = null
}

}

我所面临的问题

  1. 我想动态地设置媒体uri,这将从api中获得。那么,怎么做呢?
  2. 使用这段代码,我在我的通知中心得到一个中介者通知,当我从那里播放或暂停时,音乐会停止或播放,但同时当我停止通知和播放片段音乐时,就不会播放。
  3. 每当我从片段中停止音乐,那么音乐也会继续在背景中播放,并且通知仍然存在。
  4. 当我从通知中心清除音乐通知时,音乐也会继续播放。

如果有人遇到过问题,请帮助我解决问题。提亚

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-23 14:59:47

经过多次尝试和大量研究,我找到了我自己问题的答案。发布答案,因为它可以帮助社区中的人找到同样的答案。没有什么不好的感觉,但三天后,我发布了我自己的问题的答案,但这里没有人来帮助我。

注意:我的代码中使用的Exoplayer(Mediaplayer)是media3库,它处于beta模式。代码在android 11和android 12中进行了测试,运行良好。所以,继续使用它吧。

这个youtube视频给了我很大的帮助。

我按照视频中的说明稍微修改了我的代码,这是我的最后代码。

代码语言:javascript
运行
复制
    class MusicPlayerActivity : BaseActivity() {

    lateinit var musicPlayerBinding: ActivityMusicPlayerBinding
    var player: Player? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        musicPlayerBinding = ActivityMusicPlayerBinding.inflate(layoutInflater)
        setContentView(musicPlayerBinding.root)
        bindMusicService()
    }

    override fun onBackPressed() {
        unbindService(playerServiceConnection)
        if (player?.isPlaying!!) {
            player?.stop()
        }
        player?.release()
        player = null
        super.onBackPressed()
    }
    
    private fun bindMusicService() {
        val intent = Intent(this, PlayerService::class.java)
        bindService(intent, playerServiceConnection, Context.BIND_AUTO_CREATE)
    }

    private val playerServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            val binder = service as PlayerService.ServiceBinder
            player = binder.getPlayerService().player!!
            player?.prepare()
            player?.playWhenReady = false
            player?.seekTo(0, 0)
            val mediaMetaData = MediaMetadata.Builder()
                .setAlbumTitle(MusicPlayerData.audioTitle)
                .build()
            val mediaItem =
                MediaItem.Builder().setMediaMetadata(mediaMetaData)
                    .setUri(MusicPlayerData.audioUri)
                    .build()
            player?.setMediaItem(mediaItem)
            setPlayerControls()
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            TODO("Not yet implemented")
        }
    }

    private fun setPlayerControls() {
        musicPlayerBinding.playerView.player = player
        musicPlayerBinding.controls.player = player
        player?.addListener(object : Player.Listener {
            override fun onPlaybackStateChanged(playbackState: Int) {
                super.onPlaybackStateChanged(playbackState)
                when (playbackState) {
                    Player.STATE_BUFFERING -> show(musicPlayerBinding.progressCircular)
                    Player.STATE_READY -> {
                        player?.playWhenReady = true
                        hide(musicPlayerBinding.progressCircular)
                    }
                }
            }
        })
    }
}

这是我的服务班

代码语言:javascript
运行
复制
class PlayerService : Service() {

private val iBinder = ServiceBinder()
var player: Player? = null
private var mediaSession: MediaSession? = null
lateinit var notificationManager: PlayerNotificationManager

inner class ServiceBinder : Binder() {
    fun getPlayerService(): PlayerService = this@PlayerService
}


override fun onBind(intent: Intent?): IBinder {
    return iBinder
}

override fun onCreate() {
    super.onCreate()
    player = ExoPlayer.Builder(this).build()
    mediaSession =
        MediaSession.Builder(this, player!!).setSessionActivity(pendingIntent()!!).setId(Random(5).toString())
            .build()
    notificationManager = PlayerNotificationManager.Builder(this, 111, "Music Channel")
        .setChannelImportance(IMPORTANCE_HIGH)
        .setSmallIconResourceId(R.drawable.music)
        .setChannelDescriptionResourceId(R.string.app_name)
        .setChannelNameResourceId(R.string.app_name)
        .setMediaDescriptionAdapter(audioDescriptor)
        .setNotificationListener(notificationListener)
        .build()


    notificationManager.setPlayer(player)
    notificationManager.setPriority(PRIORITY_MAX)
    notificationManager.setUseRewindAction(true)
    notificationManager.setUseFastForwardAction(false)
    notificationManager.setUsePreviousAction(false)
    notificationManager.setUsePlayPauseActions(true)
}


override fun onDestroy() {
    if (player?.isPlaying!!) {
        player?.stop()
    }
    notificationManager.setPlayer(null)
    player?.release()
    player = null
    stopForeground(true)
    stopSelf()
    super.onDestroy()
}

private val notificationListener = object : PlayerNotificationManager.NotificationListener {
    override fun onNotificationCancelled(notificationId: Int, dismissedByUser: Boolean) {
        super.onNotificationCancelled(notificationId, dismissedByUser)
        stopForeground(true)
        if (player?.isPlaying!!) {
            player?.stop()
            player?.release()
        }
    }

    override fun onNotificationPosted(
        notificationId: Int,
        notification: Notification,
        ongoing: Boolean
    ) {
        super.onNotificationPosted(notificationId, notification, ongoing)
        startForeground(notificationId, notification)
    }
}

private val audioDescriptor = object : PlayerNotificationManager.MediaDescriptionAdapter {
    override fun getCurrentContentTitle(player: Player): CharSequence {
        return player.currentMediaItem?.mediaMetadata?.albumTitle!!
    }

    override fun createCurrentContentIntent(player: Player): PendingIntent? {
        return pendingIntent()
    }

    override fun getCurrentContentText(player: Player): CharSequence? {
        return ""
    }

    override fun getCurrentLargeIcon(
        player: Player,
        callback: PlayerNotificationManager.BitmapCallback
    ): Bitmap? {
        val bitmapDrawable: BitmapDrawable =
            ContextCompat.getDrawable(
                applicationContext,
                R.drawable.cma_logo_render
            ) as BitmapDrawable
        return bitmapDrawable.bitmap
    }
}

private fun pendingIntent(): PendingIntent? {
    val intent = Intent(applicationContext, MusicPlayerActivity::class.java)
    return PendingIntent.getActivity(
        applicationContext,
        0,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )
}

我所面临的问题

  1. 我想动态地设置媒体uri,这将从api中获得。那么,如何做到这一点呢?->不需要在服务类中设置媒体uri,我们可以在活动中从服务类获取player的实例,并且只在中执行活动类中的所有功能。
  2. 使用这段代码,我在我的通知中心得到一个中介者通知,当我从那里播放或暂停时,音乐会停止或播放,但同时当我停止通知和播放片段音乐时,就不会播放。
  3. 每当我停止播放片段音乐时,音乐也会继续在后台播放,并且通知仍然存在
  4. 当我从通知中心清除音乐通知时,音乐也会继续播放。 ->为此,我已经将会话活动设置为打开当前播放状态中的活动。

在实现了上面这样的事情之后,

  1. 活动返回到当前状态
  2. 从通知和活动中播放音乐是同步的
  3. 音乐也在背景中播放
  4. 在活动回到后台后恢复状态后,play暂停可以从UI或通知中心完美地工作。
  5. 一个人可以播放,暂停,停止和回放。
  6. 如果需要,可以在音频描述符中设置音乐标题和图标,否则可以空着。
  7. 当歌曲播放时,通知不会清除
  8. 如果应用程序被杀死,那么歌曲就停止播放,通知也消失了。
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73052245

复制
相关文章

相似问题

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