Android Media3 MediaSessionService如何指定HLS流源并传URL与Token
Android Media3 MediaSessionService 传递HLS参数与切换源方案
问题描述
在Android Media3的MediaSessionService中,只能在服务内为ExoPlayer配置HLS媒体源,无法直接在Activity/Composable的MediaController中完成设置。需解决两个核心问题:
- 如何向PlaybackService传递HLS流URL和Authorization Token,以正确初始化HLSMediaSource?
- 后续如何通过MediaController切换播放不同的HLS源?
一、向服务传递URL和Token的实现方案
1. 启动Intent传递(首次初始化场景)
在Composable/Activity中启动服务时,将URL和Token放入Intent的Extra字段,服务在onStartCommand中读取并初始化播放源。
修改后的Composable代码:
val context = LocalContext.current // 启动服务并传递HLS参数 val startHlsPlayback = { url: String, token: String -> val intent = Intent(context, PlaybackService::class.java).apply { putExtra("HLS_STREAM_URL", url) putExtra("AUTHORIZATION_TOKEN", token) } ContextCompat.startForegroundService(context, intent) }
修改后的PlaybackService核心代码:
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null private lateinit var player: ExoPlayer override fun onCreate() { super.onCreate() // 先初始化基础Player实例 player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player).build() // 注意:需调用startForeground()并设置通知,避免服务被系统回收 } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { intent?.let { val hlsUrl = it.getStringExtra("HLS_STREAM_URL") val authToken = it.getStringExtra("AUTHORIZATION_TOKEN") if (!hlsUrl.isNullOrEmpty() && !authToken.isNullOrEmpty()) { setupHlsPlayback(hlsUrl, authToken) player.playWhenReady = true } } return START_STICKY } // 封装HLS源初始化逻辑 private fun setupHlsPlayback(url: String, token: String) { // 创建带Token的Http数据源工厂 val authDataSourceFactory = DataSource.Factory { DefaultHttpDataSource.Factory().createDataSource().apply { setRequestProperty("Authorization", token) } } // 创建HLS媒体源 val hlsMediaSource = HlsMediaSource.Factory(authDataSourceFactory) .createMediaSource(MediaItem.fromUri(url)) // 替换当前播放源并准备播放 player.setMediaSource(hlsMediaSource) player.prepare() } // 原有生命周期方法... override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? { return mediaSession } }
2. 自定义命令传递(动态切换场景)
如果需要在服务运行中动态更新播放源,可通过MediaController发送自定义命令,服务端接收后处理参数并切换源。
Composable中发送自定义命令:
val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java)) val controllerFuture = MediaController.Builder(context, sessionToken).buildAsync() val mediaController = remember { mutableStateOf<MediaController?>(null) } LaunchedEffect(controllerFuture) { controllerFuture.addListener({ mediaController.value = controllerFuture.get() }, ContextCompat.getMainExecutor(context)) } // 切换HLS流的方法 val switchHlsStream = { newUrl: String, newToken: String -> mediaController.value?.sendCustomCommand( SessionCommand( "com.your.app.COMMAND_SWITCH_HLS", Bundle().apply { putString("NEW_HLS_URL", newUrl) putString("NEW_AUTH_TOKEN", newToken) } ), null, ContextCompat.getMainExecutor(context) ) }
PlaybackService中注册命令回调:
override fun onCreate() { super.onCreate() player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player) .setCustomCommandReceiver { _, command, executor, callback -> when (command.customAction) { "com.your.app.COMMAND_SWITCH_HLS" -> { val newUrl = command.extras.getString("NEW_HLS_URL") val newToken = command.extras.getString("NEW_AUTH_TOKEN") if (!newUrl.isNullOrEmpty() && !newToken.isNullOrEmpty()) { executor.execute { setupHlsPlayback(newUrl, newToken) callback.onResult(SessionResult(SessionResult.RESULT_SUCCESS)) } } else { callback.onResult(SessionResult(SessionResult.RESULT_ERROR_BAD_VALUE)) } } } } .build() }
二、切换不同HLS源的核心逻辑
不管是首次初始化还是后续切换,核心都是调用player.setMediaSource()替换当前媒体源,再调用player.prepare()完成准备工作:
- 首次启动:通过Intent传递参数,触发
setupHlsPlayback初始化播放 - 动态切换:通过自定义命令传递新参数,重新调用
setupHlsPlayback替换源
注意事项
- 必须将服务设置为前台服务(调用
startForeground()并配置通知),否则系统可能在后台回收服务 - 需添加空值判断,避免URL或Token为空导致播放异常
- 若需播放多段流,可使用
player.addMediaItem()或player.setMediaItems()批量添加媒体项
内容的提问来源于stack exchange,提问作者rysv
相关产品推荐
相关产品推荐

