Android本地WiFi投屏应用优化及替代方案技术咨询
本地WiFi投屏应用优化咨询
我开发了一款基于本地WiFi的Android手机到Android TV的投屏应用,已完成两端客户端开发:TV端显示带IP和端口的二维码,手机端扫码后用MediaProjection+MediaCodec捕获屏幕画面,通过DatagramSocket以UDP发送到TV端,但投屏效果极差,画面模糊且卡顿。
用到的核心类:
- MediaCodec与MediaProjection(屏幕捕获)
- DatagramSocket(UDP数据发送)
我对UDP了解不多,想咨询两个问题:
- 如何提升投屏画面的清晰度与流畅度?
- 有没有其他实现方案或可用的开源库?
两端核心代码
手机端发送数据:
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") } }
问题解答
一、提升画面清晰度与流畅度的方案
优化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传输压力。
修复UDP传输缺陷
- 处理丢包:UDP不可靠,需给数据包加序号、帧类型标识(I/P帧),接收端检测丢包后请求重传关键帧,或添加前向纠错(FEC)冗余数据。
- 优化分片逻辑:单UDP包不要超过WiFi MTU(约1472字节,去掉IP/UDP头),避免分片丢包;拆分时给每个分片加序号和总片数,接收端重组完整帧后再解码——你当前直接把单包喂给MediaCodec,会导致解码混乱。
- 增大缓冲区:通过
datagramSocket.setSendBufferSize()和setReceiveBufferSize()增大缓冲区,减少因缓冲区不足导致的丢包。 - 控制发送速率:避免循环逐个发送分片造成的拥塞,可批量异步发送,同时根据网络状态调整速率。
优化解码逻辑
- 加入时间戳:当前
queueInputBuffer时间戳设为0,会导致MediaCodec无法控制帧时序,造成卡顿。必须传入捕获时的真实时间戳(从MediaProjection帧数据中获取),让解码端按正确顺序渲染。 - 调整解码超时:
dequeueOutputBuffer超时设为0会频繁空轮询占用CPU,建议设为1000毫秒左右。 - 先重组再解码:接收端必须把同一帧的所有分片重组完成后,再传给MediaCodec,否则解码不完整帧会导致画面模糊花屏。
- 加入时间戳:当前
二、其他实现方案与开源库
- 混合协议传输:用TCP传输关键帧(保证可靠性),UDP传输普通帧(保证实时性),平衡延迟和画质。
- 开源库推荐
- Scrcpy:成熟投屏工具,核心用MediaProjection+FFmpeg编码,可参考其编码、传输逻辑,适配WiFi直连场景。
- ScreenShare:专门的Android投屏开源项目,采用H.264+UDP传输,自带丢包重传机制,可直接复用或参考。
- LibVLC:跨平台多媒体库,支持流媒体编码与播放,能快速搭建稳定的投屏系统。
内容的提问来源于stack exchange,提问作者Summer-Yang
相关产品推荐
相关产品推荐

