You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Media3 Session&Controller:合并媒体源实现音视频同步播放问题

解决Media3 Session音视频同时播放+后台播放的方案

针对你遇到的Media3 Session无法在Controller设置多媒体项、实现音视频同时播放的问题,可通过自定义命令+合并媒体源的方式实现,具体步骤如下:

核心思路

Media3的MergingMediaSource支持将多个独立的媒体源(音频、视频)合并为一个播放源,我们只需要在Controller端将音视频URI传递给Session端,由Session端完成媒体源合并并启动播放,同时配置Session确保后台播放和通知正常工作。

1. Controller端:发送音视频播放请求

不要直接调用setMediaItem,而是通过sendCustomCommand将音视频URI传递给Session:

// 构造自定义命令,携带音视频URI
val playAudioVideoCommand = SessionCommand(
    "com.your.package.ACTION_PLAY_AUDIO_VIDEO",
    Bundle().apply {
        putString("KEY_AUDIO_URI", audioUri.toString())
        putString("KEY_VIDEO_URI", videoUri.toString())
    }
)

// 发送命令到Session
mediaController.sendCustomCommand(playAudioVideoCommand, null)
    .addListener(
        object : ListenableFutureCallback<SessionResult> {
            override fun onSuccess(result: SessionResult?) {
                // 命令发送成功后的处理
            }

            override fun onFailure(t: Throwable) {
                // 命令发送失败的处理
            }
        },
        Executors.mainThreadExecutor()
    )

2. Session端:处理自定义命令并合并媒体源

在MediaSessionService的onCreate或MediaSession初始化时,设置自定义命令处理器,收到请求后合并音视频源并启动播放:

// 初始化ExoPlayer(需使用ExoPlayer支持MergingMediaSource)
val player = ExoPlayer.Builder(this).build()

// 初始化MediaSession
val mediaSession = MediaSession.Builder(this, player)
    .setSessionActivity(PendingIntent.getActivity(this, 0, Intent(this, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE))
    .setMediaButtonReceiver(PendingIntent.getBroadcast(this, 0, Intent(Intent.ACTION_MEDIA_BUTTON), PendingIntent.FLAG_IMMUTABLE))
    .build()

// 设置自定义命令处理器
mediaSession.setCustomCommandHandler { session, command, args ->
    when (command.customAction) {
        "com.your.package.ACTION_PLAY_AUDIO_VIDEO" -> {
            val audioUri = args.getString("KEY_AUDIO_URI")?.let { Uri.parse(it) }
            val videoUri = args.getString("KEY_VIDEO_URI")?.let { Uri.parse(it) }
            
            if (audioUri != null && videoUri != null) {
                // 创建数据源工厂
                val dataSourceFactory = DefaultHttpDataSource.Factory()
                
                // 创建音频、视频媒体源
                val audioSource = ProgressiveMediaSource.Factory(dataSourceFactory)
                    .createMediaSource(MediaItem.fromUri(audioUri))
                val videoSource = ProgressiveMediaSource.Factory(dataSourceFactory)
                    .createMediaSource(MediaItem.fromUri(videoUri))
                
                // 合并媒体源(注意顺序:视频在前,音频在后)
                val mergedSource = MergingMediaSource(videoSource, audioSource)
                
                // 设置到Player并启动播放
                session.player.setMediaSource(mergedSource)
                session.player.prepare()
                session.player.play()
            }
            SessionResult(SessionResult.RESULT_SUCCESS)
        }
        else -> SessionResult(SessionResult.RESULT_ERROR_BAD_REQUEST)
    }
}

3. 配置后台播放与通知

确保MediaSessionService在Manifest中正确声明,且配置前台服务类型:

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

同时使用PlayerNotificationManager创建播放通知,确保后台播放时通知正常显示:

val notificationManager = PlayerNotificationManager.Builder(this, NOTIFICATION_ID, CHANNEL_ID)
    .setMediaDescriptionAdapter(object : PlayerNotificationManager.MediaDescriptionAdapter {
        override fun createCurrentContentIntent(player: Player): PendingIntent? {
            return mediaSession.sessionActivity
        }

        override fun getCurrentContentTitle(player: Player): CharSequence {
            return "音视频播放"
        }

        override fun getCurrentContentText(player: Player): CharSequence? {
            return "正在播放音视频内容"
        }

        override fun getCurrentLargeIcon(player: Player, callback: PlayerNotificationManager.BitmapCallback): Bitmap? {
            // 返回通知大图标
            return null
        }
    })
    .build()

// 将Player与通知管理器绑定
notificationManager.setPlayer(player)

关键注意事项

  • 必须使用ExoPlayer作为MediaSession的Player,因为MergingMediaSource是ExoPlayer的专属类,Media3的默认Player不支持该功能。
  • Android 13及以上版本需申请POST_NOTIFICATIONS权限,否则通知无法显示。
  • 合并媒体源时,顺序建议为视频在前、音频在后,确保播放同步。
  • 若需处理播放控制(暂停、进度条),MediaSession会自动同步Player状态到Controller,无需额外处理。

内容的提问来源于stack exchange,提问作者zeet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 03:27:39