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
相关产品推荐
相关产品推荐

