在 Android 上使用 WebRTC,可分为「官方 SDK」和「自己编译源码」两条路线。
多数业务场景直接依赖官方发布的 AAR 即可,下面给出一套从 0 到 1 的落地步骤(基于官方 AAR)。
一、准备工作
- Android Studio ≥ 4.0
- minSdkVersion ≥ 21(建议 23+)
- 真机或模拟器(模拟器需支持 Camera2)
二、在工程里引入 WebRTC
app/build.gradle
java
implementation 'org.webrtc:google-webrtc:1.0.32006' // 官方最新稳定版
implementation 'org.java-websocket:Java-WebSocket:1.5.3' // 仅示例,用 WebSocket 做信令
三、Manifest 权限与特性
xml
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-feature android:name="android.hardware.camera" android:required="true"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
动态权限申请代码略(可用 EasyPermissions / RxPermission)。
四、初始化 WebRTC(一次性在 Application 或 Activity 中做)
java
PeerConnectionFactory.InitializationOptions init =
PeerConnectionFactory.InitializationOptions.builder(context)
.setEnableInternalTracer(true)
.setFieldTrials("WebRTC-H264HighProfile/Enabled/")
.createInitializationOptions();
PeerConnectionFactory.initialize(init);
五、创建 PeerConnectionFactory
java
// EGL 上下文,用于硬件加速编解码 & 渲染
EglBase rootEgl = EglBase.create();
// 视频编解码工厂
VideoEncoderFactory encoderFactory =
new DefaultVideoEncoderFactory(rootEgl.getEglBaseContext(), true, true);
VideoDecoderFactory decoderFactory =
new DefaultVideoDecoderFactory(rootEgl.getEglBaseContext());
// 音频模块
AudioDeviceModule adm = JavaAudioDeviceModule.builder(context).createAudioDeviceModule();
PeerConnectionFactory factory = PeerConnectionFactory.builder()
.setAudioDeviceModule(adm)
.setVideoEncoderFactory(encoderFactory)
.setVideoDecoderFactory(decoderFactory)
.setOptions(new PeerConnectionFactory.Options()) // 可关闭加密/网络监控
.createPeerConnectionFactory();
六、采集本地音视频
- 视频源
java
SurfaceTextureHelper surfaceHelper =
SurfaceTextureHelper.create("CaptureThread", rootEgl.getEglBaseContext());
CameraEnumerator enumerator = new Camera1Enumerator(true); // Camera2Enumerator 亦可
String camName = enumerator.getDeviceNames()[0]; // 默认后置
VideoCapturer capturer = enumerator.createCapturer(camName, null);
VideoSource videoSource = factory.createVideoSource(capturer.isScreencast());
capturer.initialize(surfaceHelper, context, videoSource.getCapturerObserver());
capturer.startCapture(1280, 720, 30);
VideoTrack localVideoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
- 音频源
java
AudioSource audioSource = factory.createAudioSource(new MediaConstraints());
AudioTrack localAudioTrack = factory.createAudioTrack("ARDAMSa0", audioSource);
七、本地渲染(SurfaceViewRenderer)
xml
<org.webrtc.SurfaceViewRenderer
android:id="@+id/localRenderer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
java
SurfaceViewRenderer localRenderer = findViewById(R.id.localRenderer);
localRenderer.init(rootEgl.getEglBaseContext(), null);
localVideoTrack.addSink(localRenderer);
八、创建 PeerConnection & 信令交换
- 配置 ICE 服务器
java
List<PeerConnection.IceServer> iceServers = new ArrayList<>();
iceServers.add(PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer());
// 有 TURN 账号也可加
- 创建 PeerConnection
java
PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
PeerConnection pc = factory.createPeerConnection(rtcConfig, new PeerConnectionObserver() {
@Override public void onIceCandidate(IceCandidate iceCandidate) {
// 通过信令通道发送给对方
sendIceCandidateToRemote(iceCandidate);
}
@Override public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
VideoTrack remoteVideoTrack = (VideoTrack) mediaStreams[0].videoTracks.get(0);
remoteVideoTrack.addSink(remoteRenderer); // 远端渲染
}
});
// 把本地轨道加入
pc.addTrack(localVideoTrack, Collections.singletonList("ARDAMS"));
pc.addTrack(localAudioTrack, Collections.singletonList("ARDAMS"));
- 信令流程(伪代码)
- 收到 room 中新成员通知 → createOffer → setLocalDescription → 发送 Offer
- 收到 Answer → setRemoteDescription
- 收到 ICE candidate → addIceCandidate
信令通道可用 WebSocket、Socket.IO、FCM 任意实现。
九、远端渲染
与本地渲染完全一样,再准备一个 SurfaceViewRenderer 即可。
十、释放资源
java
capturer.stopCapture();
capturer.dispose();
pc.close();
factory.dispose();
rootEgl.release();
十一、常见坑排查
现象 | 可能原因 |
---|---|
黑屏 | 未调用 startCapture /addSink ,或 EGL 线程冲突 |
听不到声音 | 未动态申请 RECORD_AUDIO ,或 AudioTrack 未 addTrack |
连不上 | 防火墙拦截 UDP,需要 TURN |
崩溃 so not found | 64 位 so 未打入,检查 Gradle abiFilters |
十二、进阶方向
- 多人会议:SFU(Janus/mediasoup)
- 数据通道:DataChannel 传文件 / 白板
- 编译源码:定制编解码器、裁剪包大小
- 性能调优:硬件编解码、带宽估计、抖动缓冲
至此,即可在 Android 上完成一次完整的 WebRTC 点对点通话。