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

Android本地WiFi投屏应用优化及替代方案技术咨询

本地WiFi投屏应用优化咨询

我开发了一款基于本地WiFi的Android手机到Android TV的投屏应用,已完成两端客户端开发:TV端显示带IP和端口的二维码,手机端扫码后用MediaProjection+MediaCodec捕获屏幕画面,通过DatagramSocket以UDP发送到TV端,但投屏效果极差,画面模糊且卡顿。

用到的核心类:

  • MediaCodec与MediaProjection(屏幕捕获)
  • DatagramSocket(UDP数据发送)

我对UDP了解不多,想咨询两个问题:

  1. 如何提升投屏画面的清晰度与流畅度?
  2. 有没有其他实现方案或可用的开源库?

两端核心代码

手机端发送数据:

fun sendCaptureData(data: ByteArray) {
    if (tvInfo == null) {
        Logger.e("TV info is null")
        return
    }
    job.launch {
        try {
            val chunks = splitCaptureData(data)
            for (chunk in chunks) {
                val datagramPacket = datagramPacketPool.borrow()
                datagramPacket.data = chunk
                datagramPacket.length = chunk.size
                datagramPacket.address = InetAddress.getByName(tvInfo!!.ip)
                datagramPacket.port = tvInfo!!.port
                datagramSocket.send(datagramPacket)
                datagramPacketPool.release(datagramPacket)
            }
        } catch (e: Exception) {
            Logger.e(e, "Error sending data")
        }
    }
}

TV端接收数据:

fun startReceiving(dataDecoder: DataDecoder) {
    isReceiving = true
    // 启动接收协程
    job.launch {
        while (isReceiving) {
            try {
                datagramSocket.receive(packet)
                val receivedData = ByteArray(packet.length)
                System.arraycopy(packet.data, packet.offset, receivedData, 0, packet.length)
                dataDecoder.decode(receivedData)
            } catch (e: Exception) {
                Logger.e(e, "Error receiving data")
            }
        }
    }
}

TV端解码数据:

fun decode(receivedData: ByteArray) {
    if (mediaCodec == null) return
    val index = mediaCodec!!.dequeueInputBuffer(10000)
    if (index >= 0) {
        val inputBuffer = mediaCodec!!.getInputBuffer(index)
        inputBuffer?.clear()
        inputBuffer?.put(receivedData, 0, receivedData.size)
        mediaCodec!!.queueInputBuffer(index, 0, receivedData.size, 0, 0)
        val bufferInfo = MediaCodec.BufferInfo()
        var outputBufferIndex = mediaCodec!!.dequeueOutputBuffer(bufferInfo, 0)
        while (outputBufferIndex >= 0) {
            mediaCodec!!.releaseOutputBuffer(outputBufferIndex, true)
            outputBufferIndex = mediaCodec!!.dequeueOutputBuffer(bufferInfo, 0)
        }
        Logger.i("MediaCodec decoded")
    }
}

问题解答

一、提升画面清晰度与流畅度的方案

  1. 优化MediaCodec编码参数

    • 匹配分辨率:确保捕获的屏幕分辨率与TV端显示分辨率一致,避免拉伸压缩导致模糊。比如手机是1080P,就用1080P编码,不要用低分辨率编码后放大。
    • 提高码率:默认码率过低,1080P建议设置8-12Mbps,720P建议4-6Mbps,通过MediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate)配置。
    • 调整帧率与I帧间隔:设置与屏幕刷新一致的帧率(30/60fps),用MediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);同时设置I帧间隔为2-3秒(KEY_I_FRAME_INTERVAL),平衡画质和传输量。
    • 选用高效编码格式:优先用H.265(HEVC)替代H.264,相同画质下码率可降低50%,减轻UDP传输压力。
  2. 修复UDP传输缺陷

    • 处理丢包:UDP不可靠,需给数据包加序号、帧类型标识(I/P帧),接收端检测丢包后请求重传关键帧,或添加前向纠错(FEC)冗余数据。
    • 优化分片逻辑:单UDP包不要超过WiFi MTU(约1472字节,去掉IP/UDP头),避免分片丢包;拆分时给每个分片加序号和总片数,接收端重组完整帧后再解码——你当前直接把单包喂给MediaCodec,会导致解码混乱。
    • 增大缓冲区:通过datagramSocket.setSendBufferSize()和setReceiveBufferSize()增大缓冲区,减少因缓冲区不足导致的丢包。
    • 控制发送速率:避免循环逐个发送分片造成的拥塞,可批量异步发送,同时根据网络状态调整速率。
  3. 优化解码逻辑

    • 加入时间戳:当前queueInputBuffer时间戳设为0,会导致MediaCodec无法控制帧时序,造成卡顿。必须传入捕获时的真实时间戳(从MediaProjection帧数据中获取),让解码端按正确顺序渲染。
    • 调整解码超时:dequeueOutputBuffer超时设为0会频繁空轮询占用CPU,建议设为1000毫秒左右。
    • 先重组再解码:接收端必须把同一帧的所有分片重组完成后,再传给MediaCodec,否则解码不完整帧会导致画面模糊花屏。

二、其他实现方案与开源库

  1. 混合协议传输:用TCP传输关键帧(保证可靠性),UDP传输普通帧(保证实时性),平衡延迟和画质。
  2. 开源库推荐
    • Scrcpy:成熟投屏工具,核心用MediaProjection+FFmpeg编码,可参考其编码、传输逻辑,适配WiFi直连场景。
    • ScreenShare:专门的Android投屏开源项目,采用H.264+UDP传输,自带丢包重传机制,可直接复用或参考。
    • LibVLC:跨平台多媒体库,支持流媒体编码与播放,能快速搭建稳定的投屏系统。

内容的提问来源于stack exchange,提问作者Summer-Yang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 01:23:18