Android如何获取摄像头录制时的实时视频数据以实现实时传输?
安卓摄像头实时视频传输:获取数据并发送的实现方案
要实现类似视频通话的实时效果,核心是高效获取摄像头帧数据→编码压缩→低延迟网络传输→接收端解码渲染,以下是具体步骤:
一、获取摄像头预览帧数据(转为byte[])
推荐用Google的CameraX库(兼容性更好),通过ImageAnalysis组件获取实时预览帧:
- 添加CameraX依赖(在build.gradle中配置):
dependencies { def camerax_version = "1.3.0" implementation "androidx.camera:camera-core:$camerax_version" implementation "androidx.camera:camera-camera2:$camerax_version" implementation "androidx.camera:camera-lifecycle:$camerax_version" implementation "androidx.camera:camera-view:$camerax_version" }
- 实现帧数据获取逻辑:
CameraX默认输出YUV_420_888格式的Image,可将其转为byte[]:
val imageAnalyzer = ImageAnalysis.Builder() .setTargetResolution(Size(640, 480)) // 降低分辨率减少带宽压力 .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) // 只保留最新帧,保证实时性 .build() imageAnalyzer.setAnalyzer(Executors.newSingleThreadExecutor()) { image -> // 提取YUV各平面数据并合并为byte[] val yBuffer = image.planes[0].buffer val uBuffer = image.planes[1].buffer val vBuffer = image.planes[2].buffer val ySize = yBuffer.remaining() val uSize = uBuffer.remaining() val vSize = vBuffer.remaining() val byteArray = ByteArray(ySize + uSize + vSize) yBuffer.get(byteArray, 0, ySize) uBuffer.get(byteArray, ySize, uSize) vBuffer.get(byteArray, ySize + uSize, vSize) // 拿到原始YUV数据后,编码再发送 encodeAndSendFrame(byteArray, image.width, image.height) image.close() // 必须关闭Image,否则会阻塞后续帧采集 } // 将Analyzer绑定到页面生命周期 CameraX.bindToLifecycle(this, cameraProvider, preview, imageAnalyzer)
二、视频编码(必做:原始数据带宽无法支撑实时传输)
原始YUV数据体积极大(640x480单帧约900KB),必须用硬编码压缩为H.264格式,用MediaCodec实现:
private lateinit var mediaCodec: MediaCodec private var codecConfigured = false fun initEncoder(width: Int, height: Int) { val format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height) format.setInteger(MediaFormat.KEY_BIT_RATE, 1000000) // 1Mbps码率,可根据网络调整 format.setInteger(MediaFormat.KEY_FRAME_RATE, 25) // 25帧/秒,平衡流畅度与带宽 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5) // 每5帧生成一个关键帧,优化丢包恢复 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible) mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC) mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE) mediaCodec.start() codecConfigured = true } fun encodeAndSendFrame(yuvData: ByteArray, width: Int, height: Int) { if (!codecConfigured) initEncoder(width, height) // 喂数据到编码器 val inputBufferIndex = mediaCodec.dequeueInputBuffer(1000) if (inputBufferIndex >= 0) { val inputBuffer = mediaCodec.getInputBuffer(inputBufferIndex) inputBuffer?.clear() inputBuffer?.put(yuvData) mediaCodec.queueInputBuffer(inputBufferIndex, 0, yuvData.size, System.nanoTime() / 1000, 0) } // 获取编码后的数据并发送 val bufferInfo = MediaCodec.BufferInfo() var outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 1000) while (outputBufferIndex >= 0) { val outputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex) val encodedData = ByteArray(bufferInfo.size) outputBuffer?.get(encodedData) sendEncodedData(encodedData) mediaCodec.releaseOutputBuffer(outputBufferIndex, false) outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0) } }
三、网络传输(低延迟优先选UDP)
实时视频通话优先用UDP协议(延迟低,少量丢包不影响观感),用DatagramSocket实现发送:
private lateinit var datagramSocket: DatagramSocket private val targetAddress = InetAddress.getByName("192.168.1.100") // 接收端IP private val targetPort = 8888 // 接收端监听端口 fun initSocket() { datagramSocket = DatagramSocket() datagramSocket.soTimeout = 1000 } fun sendEncodedData(data: ByteArray) { val packet = DatagramPacket(data, data.size, targetAddress, targetPort) try { datagramSocket.send(packet) } catch (e: IOException) { e.printStackTrace() } }
四、接收端核心逻辑
接收端收到H.264格式的byte[]后,需完成:
- 初始化
MediaCodec解码器,配置H.264格式 - 接收UDP数据包,将数据喂给解码器
- 获取解码后的Surface帧,渲染到
TextureView或SurfaceView
关键实时性优化点
- 降低摄像头分辨率(如640x480)和帧率(20-25帧/秒)
- 动态调整编码码率:网络差时降低码率,保证流畅度
- 用UDP+RTP封装视频流:规范传输格式,方便处理丢包与时序
- 所有帧处理、编码、发送逻辑放在子线程,避免阻塞主线程
内容的提问来源于stack exchange,提问作者Shrikant Devarmani
相关产品推荐
相关产品推荐

