You need to enable JavaScript to run this app.
导航
Android 端屏幕共享
最近更新时间:2025.10.17 11:45:43首次发布时间:2022.07.14 17:30:18
复制全文
我的收藏
有用
有用
无用
无用

在实时通信中,如果你希望用户可以分享本端设备的屏幕和设备播放的音频,可以使用 RTC 内建的屏幕采集功能,也可以自行实现屏幕采集逻辑(自定义采集),并通过屏幕共享功能,与远端用户共享。

在使用屏幕共享功能时,仅可见的用户可以发布屏幕流。

适用场景

你可以在多种行业、多种场景下使用屏幕共享功能:

行业场景
在线教育老师共享屏幕给学生上课;美术老师共享屏幕给学生教画画。
游戏直播主播共享屏幕给观众,展现自己的游戏画面。
互动直播主播共享自己的屏幕和观众互动。
视频会议会议成员共享屏幕观看 PPT 或者文档。

功能实现

Android 端屏幕共享基于 Android 5 (API 级别 21) 中引入的媒体投影 API 和 RTC 提供的 API 共同实现。

前提条件

  • 你已经集成 Android SDK,实现了基本的音视频通话

  • Android 5.0 (API 级别 21) 及以上版本。推荐使用 Android 10 (API 级别 29) 及以上版本

说明:Android 5.0 ~ 10 版本仅支持屏幕视频采集,不支持屏幕音频采集。

接入流程

在 3.60 单路流版本中,存在如下变动:

  • createRTCVideo 接口已变更为 createRTCEngine
  • publishScreenunpublishScreen 接口已不存在。请使用 publishStreamVideopublishStreamAudio 控制是否发布音视频。如果您已经调用 startScreenCapture,那么设置 publishStreamVideo(true) 将会直接开始发布屏幕流。
  • 在屏幕准备好后,您会收到 onVideoDeviceStateChanged 回调。
  • onUserUnPublishScreen 回调已不存在。请使用 onUserPublishStreamVideo 回调替代。使用 streamInfo.isScreen 来判断是否为屏幕流。

API flow

工程配置

Android 10 (API 级别 29) 及以上进行屏幕采集需要用到前台服务,在应用的 Android 清单文件中添加如下前台服务声明。
录音权限声明已包含在 RTC SDK 中,App 清单文件无需添加。

<application>
    ...
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    ...
        
     <service
        android:name="com.ss.bytertc.base.media.screen.RXScreenCaptureService"
        android:enabled="true"
        android:exported="false"
        android:foregroundServiceType="mediaProjection" />
</application>

Android 14(API 级别 34)及以上还需声明如下权限,以免出现应用闪退等异常:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />

权限申请

向系统请求屏幕共享的权限。

public static final int REQUEST_CODE_OF_SCREEN_SHARING = 101;

// 向系统发起屏幕共享的权限请求
public void requestForScreenSharing() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        Log.e("ShareScreen","当前系统版本过低,无法支持屏幕共享");
        return;
    }
    MediaProjectionManager projectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    if (projectionManager != null) {
        startActivityForResult(projectionManager.createScreenCaptureIntent(), REQUEST_CODE_OF_SCREEN_SHARING);
    } else {
        Log.e("ShareScreen","当前系统版本过低,无法支持屏幕共享");
    }
}

在权限申请的响应中,开启屏幕共享。

public static final int REQUEST_CODE_OF_SCREEN_SHARING = 101;

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    if (requestCode == REQUEST_CODE_OF_SCREEN_SHARING && resultCode == Activity.RESULT_OK) {
        startScreenShare(data);
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

API 调用

  1. 本端开启屏幕采集并开始共享屏幕
private void startScreenShare(Intent data) {
    startRXScreenCaptureService(data);
    // 编码参数
    ScreenVideoEncoderConfig config = new ScreenVideoEncoderConfig();
    config.width = 720;
    config.height = 1280;
    config.frameRate = 15;
    config.maxBitrate = 1600;
    mRTCEngine.setScreenVideoEncoderConfig(config);

    // 开启屏幕视频数据采集
    // 如果在进入房间的时候设置了 isAutoSubscribeVideo = true,那么此步骤将直接开始屏幕共享。
    // 否则,您需要调用 publishStreamVideo(true) 来开始共享屏幕。
    mRTCEngine.startScreenCapture(ScreenMediaType.SCREEN_MEDIA_TYPE_VIDEO_AND_AUDIO, data);
}

private void startRXScreenCaptureService(@NonNull Intent data) {
    Context context = Utilities.getApplicationContext();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        Intent intent = new Intent();
        intent.putExtra(RXScreenCaptureService.KEY_LARGE_ICON, R.drawable.launcher_quick_start);
        intent.putExtra(RXScreenCaptureService.KEY_SMALL_ICON, R.drawable.launcher_quick_start);
        intent.putExtra(RXScreenCaptureService.KEY_LAUNCH_ACTIVITY, mHostActivity.getClass().getCanonicalName());
        intent.putExtra(RXScreenCaptureService.KEY_CONTENT_TEXT, "正在录制/投射您的屏幕");
        intent.putExtra(RXScreenCaptureService.KEY_RESULT_DATA, data);
        context.startForegroundService(RXScreenCaptureService.getServiceIntent(context, RXScreenCaptureService.COMMAND_LAUNCH, intent));
    }
}
  1. (可选)本端在 IRTCEngineEventHandler 实现类的回调方法 onVideoDeviceStateChanged 。此步骤不需要调用关于屏幕采集的 API,只需要更新相关 UI。
@Override
public void onVideoDeviceStateChanged(String deviceId, VideoDeviceType deviceType, int deviceState, int deviceError) {
    Log.i(TAG, "onVideoDeviceStateChanged, type: " + deviceType + " state:" + deviceState);
}
  1. 远端使用 onUserPublishStreamVideoonFirstRemoteVideoFrameDecoded 回调监视视频流和屏幕共享流。建议您在 onFirstRemoteVideoFrameDecoded 回调中设置视频渲染,确保视频渲染在视频解码完成后进行,以避免卡顿。
// 房间内远端屏幕共享音视频流移除的回调。

@Override
public void onFirstRemoteVideoFrameDecoded(String streamId, StreamInfo streamInfo, VideoFrameInfo frameInfo) {
    Log.i(TAG, "onUserPublishStreamVideo, streamId: " + streamId);
    runOnUiThread(() -> {
        // 设置远端视频渲染视图
        setRemoteRenderView(streamId);
    });
}

@Override
public void onUserPublishStreamVideo(String streamId, StreamInfo streamInfo, boolean isPublish) {
    if (!isPublish) {
		Log.i(TAG, "onUserUnpublishStreamVideo, uid: " + streamId);
        runOnUiThread(() -> {
            // 解除远端视频渲染视图绑定
            removeRemoteView(streamId);
        });
	}
}
  1. 远端渲染屏幕共享流
private void setRemoteRenderView(String streamId, String roomID, String userID, FrameLayout container) {
    TextureView renderView = new TextureView(this);
    FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT);
    container.removeAllViews();
    container.addView(renderView, params);

    VideoCanvas videoCanvas = new VideoCanvas();
    videoCanvas.renderView = renderView;
    videoCanvas.renderMode = VideoCanvas.RENDER_MODE_HIDDEN;
    
    // 设置远端用户视频渲染视图
	mRTCEngine.setRemoteVideoCanvas(streamId, videoCanvas);
}
  1. 本端停止屏幕共享
private void stopScreenShare() {
    // 停止屏幕数据采集和屏幕共享
	mRTCEngine.stopScreenCapture();
}

自定义采集屏幕视频流

RTC 强烈建议你使用内部采集。如果你仍然希望使用自定义采集,参看以下步骤。

  1. 实现屏幕音视频流采集逻辑。

  2. 指定为外部输入源。调用 setVideoSourceType 设置屏幕视频自定义采集。

  3. 调用 pushScreenAudioFrame 将采集得到的音频帧推送到 RTC SDK 用于编码传输。

常见问题

  1. 共享屏幕后,对端只能看到画面,听不到音频。

    • 当前的系统版本低于 Android 10,系统不支持屏幕音频采集。
    • 其他 App 独占了屏幕音频捕获。切换至其他 App,查看是否开启了独占设置。
    • 已向系统申请录制权限,并在使用中允许录制请求。
  2. 收到以下错误提示:
    Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
    原因和解决方法如下:

    • AndroidManifest.xml 中配置 android.permission.FOREGROUND_SERVICE 权限和 org.webrtc.RXScreenCaptureService 声明。
    • 检查启动 RXScreenCaptureService 时,RXScreenCaptureService.KEY_LAUNCH_ACTIVITY 是否传入了全类名。 由于启动流程中开启 notification 时,需要获取全类名,以通过反射获取相关联的 activity,非全类名将导致 notification 创建失败,并收到上述错误提示。
  3. 没有实现功能,且无其他错误提示。

    • 检查启动 RXScreenCaptureService 时,RXScreenCaptureService.KEY_RESULT_DATA 是否传入 。
    • 在Android 清单文件中增加前台服务声明
    • 在代码中启动前台服务
    • 请求录制权限的结果需要作为参数传递给前台服务
  4. 使用 Android 5.0 ~ 10 版本 设备进行屏幕共享后自动断开。
    上述系统版本的个别 Android 机型可能存在兼容性问题。建议用户升级系统版本。

功能变更日志

本文最近更新时的 SDK 版本为 3.50.1。如果你使用的 SDK 为之前版本,请查看以下变动,并进行相应适配。

  • 3.50.1 版本中
    • 设置共享流的编码参数类型名称变更为 ScreenVideoEncoderConfig
    • 自定义屏幕流相关的接口名称和行为有变更。
  • 3.43.1 版本中
    • publishScreen 和 unpublishScreen 的类名由 RTCEngine 变更为 RTCRoom。其他方法的类名由 RTCEngine 变更为 RTCVideo
    • 回调类名由 IRTCEngineEventHandler 变更为 IRTCVideoEventHandler
  • 3.36.1 版本中
    • 设置共享流的编码参数方法由 setVideoEncoderConfig 变更为 setScreenVideoEncoderConfig
    • publishScreen 等 API 的参数有变更。