android - 如何在视频/音频通话中远程共享屏幕?
问题描述
我正在尝试构建一个具有屏幕共享功能的视频通话应用程序。用户可以在通话期间共享他们的屏幕。我正在使用WebRTC
SDK 来满足我的目的,但他们有一个在通话开始时屏幕共享的解决方案,但在通话进行时没有屏幕共享的解决方案。可以勾选屏幕共享选项,可以开始通话,但不能在通话过程中开始屏幕共享。
我在 CallActivity 屏幕上添加了一个按钮,单击该按钮会调用 Android 的 MediaProjection 类来投射屏幕,但投射的屏幕并未远程显示。
public void onScreenShare(boolean isScreenShared) {
screencaptureEnabled = isScreenShared;
if (screencaptureEnabled && videoWidth == 0 && videoHeight == 0) {
DisplayMetrics displayMetrics = getDisplayMetrics();
videoWidth = displayMetrics.widthPixels;
videoHeight = displayMetrics.heightPixels;
}
if (isPemitted()) {
startScreenCapture();
} else {
Log.i(TAG, "onScreenShare: not permitted");
}
/*if (peerConnectionClient != null) {
peerConnectionClient.stopVideoSource();
}*/
}
private void startScreenCapture() {
MediaProjectionManager mediaProjectionManager =
(MediaProjectionManager) getApplication().getSystemService(
Context.MEDIA_PROJECTION_SERVICE);
startActivityForResult(
mediaProjectionManager.createScreenCaptureIntent(),
CAPTURE_PERMISSION_REQUEST_CODE);
Log.d("tagged", ">>>>Method called :- ");
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
Log.d("tagged", ">>>>Method called :- " + requestCode);
if (requestCode != CAPTURE_PERMISSION_REQUEST_CODE)
return;
else {
mediaProjectionPermissionResultCode = resultCode;
mediaProjectionPermissionResultData = data;
if (peerConnectionParameters.videoCallEnabled) {
videoCapturer = createVideoCapturer();
}
peerConnectionClient.createPeerConnection(
localProxyVideoSink, remoteSinks, videoCapturer,
signalingParameters);
}
}
private @Nullable
VideoCapturer createScreenCapturer() {
Log.d("CheckMedia", ">>>Checking " +
mediaProjectionPermissionResultData);
if (mediaProjectionPermissionResultCode != Activity.RESULT_OK) {
reportError("User didn't give permission to capture the screen.");
return null;
}
return new ScreenCapturerAndroid(
mediaProjectionPermissionResultData, new
MediaProjection.Callback() {
@Override
public void onStop() {
reportError("User revoked permission to capture the screen.");
}
});
}
此代码在本地设备上开始投射,但不在远程设备上流式传输任何内容。
更新
private void switchCameraInternal() {
if (videoCapturer instanceof CameraVideoCapturer) {
if (!isVideoCallEnabled() || isError) {
Log.e(TAG,
"Failed to switch camera. Video: " +
isVideoCallEnabled() + ". Error : " + isError);
return; // No video is sent or only one camera is available or
error happened.
}
Log.d(TAG, "Switch camera");
CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer)
videoCapturer;
cameraVideoCapturer.switchCamera(null);
} else {
Log.d(TAG, "Will not switch camera, video caputurer is not a
camera");
}
}
public void switchCamera() {
executor.execute(this::switchCameraInternal);
}
private void startScreenSharing() {
if (videoCapturer instanceof ScreenCapturerAndroid) {
if (!isVideoCallEnabled() || isError) {
Log.e(TAG,
"Failed to share screen. Video: " + isVideoCallEnabled()
+ ". Error : " + isError);
return; // No video is sent or only one camera is available or
error happened.
}
ScreenCapturerAndroid screenCapturerAndroid =
(ScreenCapturerAndroid) videoCapturer;
screenCapturerAndroid.startCapture(500, 500, 30);
}
}
public void screenSharing() {
executor.execute(this::startScreenSharing);
}
我进行了更改,并使代码看起来类似于 switchCamera() 代码,但我得到了一个 Not On Camera Thread 异常。
解决方案
我不确定您是否可以同时从相机和屏幕流式传输。但是,您可以做的是:
- 用户单击屏幕共享按钮
- 您从使用中删除了您的相机视频轨道
PeerConnection
PeerConnection.removeTrack(RtpSender sender)
- 您使用(就像您已经做的那样)创建您的屏幕视频轨道
ScreenCapturerAndroid
- 您将曲目添加到您的
PeerConnection
如果您说屏幕共享无需调用即可工作,那么步骤 4 和 5 应该已经完成。
Camera
当你删除它的轨道时,不要忘记处置/释放与它相关的所有资源。
同样要停止屏幕共享并返回相机,只需对屏幕轨道执行上述步骤。
更新 :
这是 WebRTC 客户端的一部分,它允许我实现您的要求(您可以将其调整为您当前的代码库):
private fun stopCameraShare(){
videoCapturerAndroid?.stopCapture()
localRenderer.dispose()
localVideoView.release()
localVideoView.clearImage()
stream?.removeTrack(localVideoTrack)
localVideoTrack.dispose()
}
private fun shareScreen(){
stopCameraShare()
val mediaProjectionManager = activity!!.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), 29)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode != 29)
return
initVideos()
videoCapturerAndroid = ScreenCapturerAndroid(
data, object : MediaProjection.Callback() {
override fun onStop() {
Timber.e("User revoked permission to capture the screen.")
}
})
peerConnectionFactory.setVideoHwAccelerationOptions(rootEglBase.eglBaseContext, rootEglBase.eglBaseContext)
videoSource = peerConnectionFactory.createVideoSource(videoCapturerAndroid)
localVideoTrack = peerConnectionFactory.createVideoTrack("100", videoSource)
videoCapturerAndroid?.startCapture(300, 300, 30)
stream?.addTrack(localVideoTrack)
}
PS:这很重要peerConnectionFactory.setVideoHwAccelerationOptions(rootEglBase.eglBaseContext, rootEglBase.eglBaseContext)
希望对你有帮助 !
推荐阅读
- symfony - Symfony/Doctrine 关系良好实践
- php - openssl_encrypt,IV 为 0xFFFFFFFFFFFFFFFF,加密模式为 bf-cbc
- python - 将 python .py 文件转换为 exe 文件
- postgresql - 在 Docker Compose 中初始化 Postgres 数据库
- sql-server - SQL Server 中的拆分字符串函数
- ios - 无效的`Podfile`文件:没有这样的文件或目录@rb_sysopen-flutter_module/.ios/Flutter/podhelper.rb
- angular - 我无法以可观察的角度获得 s3 存储桶的响应
- django - 在字段旁边显示 django form.errors
- javafx - 为什么 DatePicker 在自定义 JavaFX 11 TableCell 中的 startEdit() 后变得无响应?
- android - Recyclerview - Notifydatachange() 不起作用