概述
上一节,我们成功实现了嘟宝与嘟妈之间的WebRTC音视频通信,整体流程顺畅且逻辑清晰。嘟妈通过扫描二维码完成嘟宝的绑定,点击呼叫后,两端实时建立连接。嘟宝端摄像头与麦克风采集的多媒体视频流直接推送至嘟妈,画面与声音同步传输,体验流畅稳定。
- 嘟妈扫描二维码绑定嘟宝
- 嘟妈点击呼叫,与嘟宝建立WebRTC实时通信
- 嘟宝将摄像头、麦克风多媒体视频流发送至嘟妈

切换前后摄像头
接下来介绍一个非常实用的功能:远程切换摄像头。嘟妈端通过信令下发切换指令,嘟宝端摄像头可前置与后置之间无缝切换,视频流全程保持连接,画面跳转流畅,用户体验顺滑自然。
实现原理核心在于两点:获取相机采集工具,以及获取视频流。
private VideoCapturer createCameraCapturer(boolean isFront) {
Camera2Enumerator enumerator = new Camera2Enumerator(context.getApplicationContext());
final String[] deviceNames = enumerator.getDeviceNames();
for (String deviceName : deviceNames) {
if (isFront ? enumerator.isFrontFacing(deviceName) : enumerator.isBackFacing(deviceName)) {
VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
mCamCapture = (CameraVideoCapturer) videoCapturer;
if (videoCapturer != null) {
return videoCapturer;
}
}
}
return null;
}
该函数逻辑十分明确:枚举设备上的所有摄像头,根据传入的 isFront 参数,返回前置或后置摄像头的采集实例。代码清晰直接,无冗余。
private VideoTrack getVideoTrack(){
surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBase.getEglBaseContext());
VideoCapturer videoCapturer = createCameraCapturer(true);
VideoSource videoSource = factory.createVideoSource(videoCapturer.isScreencast());
videoCapturer.initialize(surfaceTextureHelper, context.getApplicationContext(), videoSource.getCapturerObserver());
videoCapturer.startCapture(480, 640, 30);
VideoTrack videoTrack= factory.createVideoTrack("100", videoSource);
videoTrack.enabled();
return videoTrack;
}
接着,getVideoTrack 方法调用上述函数,默认选用前置摄像头,启动采集后将视频流封装为 VideoTrack 并返回。注意在 createCameraCapturer 中有一行 mCamCapture = (CameraVideoCapturer) videoCapturer;,通过强制转换得到了 CameraVideoCapturer 引用,后续摄像头切换正依赖于此。
public void changeCam(){
if (mCamCapture != null) {
mCamCapture.switchCamera(null);
} }
只需一行 switchCamera 调用,即可完成摄像头切换,稳定可靠。
屏幕共享
另一个实用场景是屏幕共享。嘟妈可通过信令远程查看嘟宝的屏幕,例如帮助孩子调试设备或演示操作流程,极为便捷。不过获取桌面屏幕需要用户授权——嘟宝启动时必须弹窗请求同意,否则无法采集屏幕数据。
实现步骤涉及三个环节:在后台服务中声明允许屏幕采集、在 MainActivity 中发起申请、用户同意后获取 Intent 变量以捕获桌面视频流。首先看清单文件中的配置:
其中 mediaProjection 类型的 foregroundServiceType 是必需的,它赋予服务在后台执行屏幕录制的能力。
public void startprojectionManager(){
@SuppressLint({"NewApi", "LocalSuppress"}) MediaProjectionManager projectionManager = (MediaProjectionManager)getSystemService(MEDIA_PROJECTION_SERVICE);
@SuppressLint({"NewApi", "LocalSuppress"}) Intent intent = projectionManager.createScreenCaptureIntent();
ActivityResultLauncher screenCaptureLauncher;
screenCaptureLauncher=registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == RESULT_OK) {
Intent data = result.getData();
MyWebRtc.i=data;
}
else {
}
});
screenCaptureLauncher.launch(intent);
}
这段代码通过 MediaProjectionManager 创建屏幕捕获意图,并启动 ActivityResultLauncher 等待用户授权。若用户点击允许,result.getData() 返回的 Intent 将用于后续构建屏幕采集器,此处直接赋值给 MyWebRtc.i。
public void getScreenVideo() {
surfaceTextureHelperscreen = SurfaceTextureHelper.create("CaptureThread1", eglBase.getEglBaseContext()); VideoCapturer screenCapturer=new ScreenCapturerAndroid(i, new MediaProjection.Callback() {
@Override
public void onStop() {
super.onStop();
}
}); // eglBaseContext 是你之前初始化的 EGL 上下文
VideoSource screenVideoSource = factory.createVideoSource(screenCapturer.isScreencast());
VideoTrack screenVideoTrack = factory.createVideoTrack("102", screenVideoSource); // 2. 初始化并启动 ScreenCapturer
// 注意:需要提前准备好 SurfaceTextureHelper
screenCapturer.initialize(surfaceTextureHelperscreen, context.getApplicationContext(), screenVideoSource.getCapturerObserver());
screenCapturer.startCapture(/* width */ 640, /* height */ 480, /* fps */ 25); // 3. 执行替换的关键操作
// localVideoSender.track().setEnabled(false);
localVideoSender.setTrack(screenVideoTrack, /* takeOwnership= */ true);
}
获得授权 Intent 后,使用 ScreenCapturerAndroid 实例化屏幕采集器,初始化并启动采集,最终得到屏幕的 VideoTrack。最后调用 localVideoSender.setTrack 将原摄像头视频流替换为屏幕视频流,即完成从摄像头画面到屏幕画面的平滑切换。
