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

基于Google Cast SDK实现Android全屏幕镜像至Chromecast设备的技术咨询

可以借助Google Cast SDK实现全屏屏幕镜像

Google Cast SDK支持自定义媒体流投屏,结合Android的MediaProjection捕获屏幕内容,就能实现类似Google Home的全屏镜像功能——Vizio SmartCast作为Chromecast兼容设备,完全支持这种方案。

实现核心思路

  1. 屏幕捕获:通过MediaProjectionManager获取屏幕捕获权限,将屏幕内容转为可编码的视频帧。
  2. 媒体编码:用MediaCodec将捕获的屏幕帧编码为Cast设备支持的H.264格式(音频需额外用AudioRecord捕获并编码为AAC)。
  3. Cast流推送:通过Cast SDK的RemoteMediaClient将编码后的直播流推送至目标设备,而非加载远程URL。

代码示例(核心逻辑)

1. 初始化核心组件

private lateinit var castContext: CastContext
private lateinit var mediaProjectionManager: MediaProjectionManager
private var mediaProjection: MediaProjection? = null
private val REQUEST_CODE_SCREEN_CAPTURE = 1001

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // 初始化Cast上下文
    castContext = CastContext.getSharedInstance(this)
    // 初始化屏幕捕获管理器
    mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
}

2. 请求屏幕捕获权限

// 触发权限申请
fun startScreenMirroring() {
    val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
    startActivityForResult(captureIntent, REQUEST_CODE_SCREEN_CAPTURE)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_CODE_SCREEN_CAPTURE && resultCode == RESULT_OK) {
        mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data!!)
        // 权限获取成功,启动投屏流程
        streamScreenToCastDevice()
    }
}

3. 编码并推送屏幕流

private fun streamScreenToCastDevice() {
    // 获取当前活跃的Cast会话
    val currentSession = castContext.sessionManager.currentCastSession ?: return
    val remoteMediaClient = currentSession.remoteMediaClient

    // 配置直播流媒体信息
    val mediaInfo = MediaInfo.Builder()
        .setContentType("video/avc") // 指定H.264编码格式
        .setStreamType(MediaInfo.STREAM_TYPE_LIVE) // 标记为直播流,优化延迟
        .build()

    // 构建媒体加载请求
    val loadRequest = MediaLoadRequestData.Builder()
        .setMediaInfo(mediaInfo)
        .build()

    // 初始化Cast端的媒体接收
    remoteMediaClient.load(loadRequest)

    // 启动屏幕捕获与编码线程
    Thread {
        val displayMetrics = resources.displayMetrics
        val screenWidth = displayMetrics.widthPixels
        val screenHeight = displayMetrics.heightPixels

        // 初始化MediaCodec编码器
        val codec = MediaCodec.createEncoderByType("video/avc")
        val videoFormat = MediaFormat.createVideoFormat(
            "video/avc",
            screenWidth,
            screenHeight
        ).apply {
            setInteger(MediaFormat.KEY_BIT_RATE, 6000000) // 6Mbps码率,可根据设备调整
            setInteger(MediaFormat.KEY_FRAME_RATE, 30)
            setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5) // 每5秒生成关键帧
            setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)
        }
        codec.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        val encoderSurface = codec.createInputSurface()

        // 创建虚拟显示,将屏幕内容输出到编码器Surface
        val virtualDisplay = mediaProjection?.createVirtualDisplay(
            "ScreenCast",
            screenWidth,
            screenHeight,
            displayMetrics.densityDpi,
            DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
            encoderSurface,
            null,
            null
        )

        codec.start()
        // 循环处理编码后的数据包,推送到Cast设备
        val bufferInfo = MediaCodec.BufferInfo()
        while (true) {
            val outputBufferId = codec.dequeueOutputBuffer(bufferInfo, 10000)
            if (outputBufferId >= 0) {
                val outputBuffer = codec.getOutputBuffer(outputBufferId)
                // 将编码后的H.264数据推送到Cast设备
                remoteMediaClient.sendMediaData(outputBuffer, bufferInfo.offset, bufferInfo.size)
                codec.releaseOutputBuffer(outputBufferId, false)
            }
        }

        // 注意:实际代码中需处理线程中断、资源释放逻辑
        // virtualDisplay?.release()
        // codec.stop()
        // codec.release()
    }.start()
}

关键注意事项

  • 音频同步:上述示例仅处理视频流,如需投屏音频,需额外捕获系统音频(Android 10+需RECORD_AUDIO权限及MediaProjection的音频捕获权限),编码为AAC后通过remoteMediaClient同步推送。
  • 延迟优化:降低码率、减少关键帧间隔可优化延迟,但需平衡画质。
  • 资源清理:退出投屏时需及时释放MediaProjection、VirtualDisplay、MediaCodec等资源,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 17:17:49