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

Android ExoPlayer合并不同时长HLS流并投屏Chromecast技术咨询

解决不同时长HLS音视频流合并并投送Chromecast的方案

一、替代MergingMediaSource的本地音视频同步方案

由于MergingMediaSource强制要求所有源流时长一致,你可以拆分音视频源的加载逻辑,用以下方式实现同步:

  • 分别创建视频流A和音频流B的HlsMediaSource实例
  • 通过ExoPlayer的TrackSelector手动指定播放视频A的视频轨和音频B的音频轨
  • 添加播放状态监听,自定义时长不一致时的同步逻辑:
    • 当较短的流播放结束时,可选择暂停较长流,或者循环较短流以匹配时长
    • 示例代码片段:
      val videoSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(videoUri)
      val audioSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(audioUri)
      
      // 创建复合媒体源,无需强制时长一致
      val compositeSource = CompositeMediaSource<Void>()
      compositeSource.addMediaSource(0, videoSource)
      compositeSource.addMediaSource(0, audioSource)
      
      player.setMediaSource(compositeSource)
      player.prepare()
      
      // 添加监听处理时长不一致逻辑
      player.addListener(object : Player.Listener {
          override fun onPositionDiscontinuity(oldPosition: PositionInfo, newPosition: PositionInfo, reason: Int) {
              val videoDuration = player.currentTimeline.getWindow(0, Timeline.Window()).durationMs
              val audioDuration = player.currentTimeline.getWindow(1, Timeline.Window()).durationMs
              if (videoDuration != audioDuration) {
                  // 自定义同步逻辑,比如循环较短的流
                  if (player.currentPosition >= min(videoDuration, audioDuration)) {
                      player.seekTo(0)
                      player.play()
                  }
              }
          }
      })
      

二、Chromecast投送的适配逻辑

Chromecast投送时,无需在本地合成流,直接将音视频双源流信息发送给Cast设备,让其加载并同步播放:

  • 通过MediaRouter获取Cast路由会话
  • 使用RemoteMediaClient设置包含音视频URI的媒体信息,部分设备支持通过自定义字段传递音频流地址:
    val remoteMediaClient = castSession.remoteMediaClient
    val mediaMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
    val mediaInfo = MediaInfo.Builder(videoUri.toString())
        .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
        .setContentType("application/x-mpegURL")
        .setMetadata(mediaMetadata)
        .setCustomData(mapOf("audioTrackUri" to audioUri.toString()))
        .build()
    remoteMediaClient.load(mediaInfo, true)
    
    注:如果使用通用Cast应用,可将音视频流打包为包含双轨道的HLS主播放列表后再投送,Cast设备会自动识别并加载对应轨道。

三、备选方案:动态适配流时长

如果必须使用合并类MediaSource,可通过裁剪或循环流来强制匹配时长:

  • 裁剪较长流至较短流的时长:
    val videoDuration = videoSource.duration
    val audioDuration = audioSource.duration
    val targetDuration = min(videoDuration, audioDuration)
    val clippedSource = if (videoDuration > targetDuration) {
        ClippingMediaSource(videoSource, 0, targetDuration)
    } else {
        ClippingMediaSource(audioSource, 0, targetDuration)
    }
    val mergedSource = MergingMediaSource(if (videoDuration > targetDuration) clippedSource else videoSource, if (audioDuration > targetDuration) clippedSource else audioSource)
    
  • 循环较短流以匹配较长流时长:
    val loopCount = ceil(max(videoDuration, audioDuration).toFloat() / min(videoDuration, audioDuration)).toInt()
    val loopedSource = if (videoDuration < audioDuration) {
        LoopingMediaSource(videoSource, loopCount)
    } else {
        LoopingMediaSource(audioSource, loopCount)
    }
    val mergedSource = MergingMediaSource(if (videoDuration < audioDuration) loopedSource else videoSource, if (audioDuration < videoDuration) loopedSource else audioSource)
    
    该方案适合能接受裁剪或循环的场景,若流时长动态变化,需实时重新计算并更新MediaSource。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 16:25:15