屏幕共享

  • iOS

更新时间不可用

了解实时互动 RTC 在不同平台上的屏幕共享方式,包括接入流程、关键配置和平台差异。

在互动直播或视频通话中,主播或视频通话用户可以通过声网 SDK 中的屏幕共享特性,将自己的屏幕内容以视频的方式分享给其他用户或观众观看。

屏幕共享可以应用在以下场景中:

场景描述
在线教育老师将自己的屏幕、教学课件、绘图软件或编程软件共享给学生,用于课堂示范
游戏直播主播共享自己的游戏画面给观众
互动直播主播共享自己的屏幕和观众互动
视频会议会议成员共享屏幕观看 PPT 或者文档
远程控制被控端向主控端展示自己的桌面
表格内容超出当前宽度,可横向滚动查看更多列。

技术原理

由于 Apple 不支持在 App 主进程采集屏幕,因此你需要为屏幕共享流单独创建一个 Extension,并在 Extension 中使用 iOS 原生的 ReplayKit 框架实现录制屏幕,然后将屏幕共享流发送给主进程、实现屏幕共享。

根据实际业务场景的不同,你可以选择以下任一方式调用 API 实现屏幕共享:

  • 在加入频道前调用 startScreenCapture,然后调用 joinChannelByToken [2/4] 加入频道并设置 publishScreenCaptureVideo 为 true,即可开始屏幕共享。
  • 在加入频道后调用 startScreenCapture,然后调用 updateChannelWithMediaOptions 更新频道媒体选项并设置 publishScreenCaptureVideo 为 true,即可开始屏幕共享。

注意事项

  • 在开启屏幕共享后,声网以屏幕共享视频流的分辨率作为计费标准。默认分辨率为 1280 × 720,你也可以根据你的业务需求进行调整。详见如何确定屏幕共享视频流的分辨率?
  • 受系统限制,屏幕共享只支持 iOS 12.0 及以上版本。
  • 该功能对设备性能要求较高,声网推荐你使用 iPhone X 及以上设备。
  • 用户在 iOS 设备上开启屏幕共享后,因系统限制,音频路由会自动切换为听筒。
    • 如果当前使用通话音量,则你可以手动切换音频路由,例如,你可以根据实际需求将音频路由切换成扬声器。
    • 如果当前使用媒体音量,由于系统限制,你在这种场景下无法手动切换音频路由。如果你不了解使用的是通话还是媒体音量,请参考如何区分媒体音量和通话音量。

前提条件

在实现屏幕共享前,请确保已在你的项目中实现基本的实时音视频功能。详见实现音视频互动。

项目设置

按照以下步骤修改你的项目文件。

  1. 前往你的项目文件夹,用 Xcode 打开 .xcodeproj 文件。

  2. 点击 File > New > Target..., 在弹出的窗口中选择 Broadcast Upload Extension, 点击 Next。

  3. 在弹出的窗口中填写 Product Name 等信息,取消勾选 Include UI Extension,点击 Finish。Xcode 会自动创建该 Extension 的文件夹,其中包含 SampleHandler.swift 文件。

  4. 在 Target 下选中刚创建的 Extension,点击 General,在 Deployment Info 下将 iOS 的版本设置为 12.0 或之后。

  1. 修改项目设置以实现屏幕共享的代码逻辑,根据实际业务需求选择以下三种方式其中之一即可:

    • 如果你只需使用声网提供的 AgoraReplayKitExtension.xcframework 动态库中的功能,则:选中 Target 为刚刚创建的 Extension,在 Info 中将 NSExtension > NSExtensionPrincipalClass 所对应的 Value 从 SampleHandler 改为 AgoraReplayKitHandler。

    • 如果你需要另外实现一些业务逻辑,可以继承 AgoraReplayKitHandler 类。参考如下代码修改 SampleHandler.swift 文件:

      Swift
      import ReplayKit
      import AgoraReplayKitExtension
      
      class SampleHandler: AgoraReplayKitHandler {
          override func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?) {
              // 用户请求开始广播
              super.broadcastStarted(withSetupInfo: setupInfo)
          }
      }
    • 如果你希望使用 iOS 原生的 SampleHandler、不继承 AgoraReplayKitHandler 类,参考如下代码修改 SampleHandler.swift 文件:

      Swift
      import ReplayKit
      import AgoraReplayKitExtension
      
      class SampleHandler: RPBroadcastSampleHandler, AgoraReplayKitExtDelegate {
          override func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?) {
              // 用户请求开始广播
              AgoraReplayKitExt.shareInstance().start(self)
          }
          override func broadcastPaused() {
              // 用户请求暂停广播,屏幕共享暂停
              AgoraReplayKitExt.shareInstance().pause()
          }
      
          override func broadcastResumed() {
              // 用户请求恢复广播,屏幕共享恢复
              AgoraReplayKitExt.shareInstance().resume()
          }
      
          override func broadcastFinished() {
              // 用户请求停止广播
              AgoraReplayKitExt.shareInstance().stop()
          }
      
          override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
              AgoraReplayKitExt.shareInstance().push(sampleBuffer, with: sampleBufferType)
          }
      
          func broadcastFinished(_ broadcast: AgoraReplayKitExt, reason: AgoraReplayKitExtReason) {
              var tip = ""
              switch reason {
              case AgoraReplayKitExtReasonInitiativeStop:
                  tip = "AgoraReplayKitExtReasonInitiativeStop"
      
              case AgoraReplayKitExtReasonConnectFail:
                  tip = "AgoraReplayKitExReasonConnectFail"
      
              case AgoraReplayKitExtReasonDisconnect:
                  tip = "AgoraReplayKitExReasonDisconnect"
      
              default: break
              }
      
              let error = NSError(domain: NSCocoaErrorDomain,
                                  code: 0,
                                  userInfo: [NSLocalizedFailureReasonErrorKey: tip])
              self .finishBroadcastWithError(error as Error)
              }
          }
      

自动集成动态库

通过 CocoaPods 集成 SDK 时,你需要在 Podfile 文件中添加如下内容,指定集成屏幕共享动态库 AgoraReplayKitExtension.xcframework,示例代码如下:

Ruby
platform :ios, '9.0'
target 'Your App' do
    # 只集成基础库和屏幕共享动态库
    # 请使用具体的 SDK 版本号替换下面代码中的 x.y.z,可通过发版说明获取最新版本号
    pod 'AgoraRtcEngine_iOS', 'x.y.z', :subspecs => ['RtcBasic', 'ReplayKit']
end

信息

屏幕共享动态库封装了如下功能:

  • 使用 Apple ReplayKit 进行屏幕录制。
  • 使用 SDK 采集功能获取系统录屏数据,并发送给频道中其他用户。

实现屏幕共享

本节介绍如何在你的项目中实现屏幕共享,API 调用时序如下图所示。

设置音频场景

调用 setAudioScenario 方法,并设置音频场景为 AgoraAudioScenarioGameStreaming(高音质场景),以提高屏幕共享时采集系统音频的成功率。

开启屏幕采集

  1. 调用 startScreenCapture 开启屏幕采集,并根据你的应用场景进行参数设置:

    • captureVideo:是否在屏幕共享时采集系统视频。
    • captureAudio:是否在屏幕共享时采集系统音频。
    • captureSignalVolume:采集的系统音量。
    • dimensions:视频编码的分辨率。
    • frameRate:视频编码帧率 (fps)。
    • bitrate:视频编码码率 (Kbps)。
    • contentHint:屏幕共享视频的内容类型。
    Swift
    // 设置屏幕采集的参数
    private lazy var screenParams: AgoraScreenCaptureParameters2 = {
        let params = AgoraScreenCaptureParameters2()
        params.captureVideo = true
        params.captureAudio = true
        let audioParams = AgoraScreenAudioParameters()
        // 设置采集的系统音量
        audioParams.captureSignalVolume = 50
        params.audioParams = audioParams
        let videoParams = AgoraScreenVideoParameters()
        // 设置共享屏幕的分辨率
        videoParams.dimensions = screenShareVideoDimension()
        // 设置视频编码帧率
        videoParams.frameRate = .fps15
        // 设置视频编码码率
        videoParams.bitrate = AgoraVideoBitrateStandard
        params.videoParams = videoParams
        return params
    }()
    
    // 开启屏幕采集
    agoraKit.startScreenCapture(screenParams)
  2. 结合用户的手动操作,使 App 开启屏幕共享。有两种方式供你参考:

    • 方式一:提示用户在 iOS 系统的控制中心长按屏幕录制按钮,并选择用你创建的 Extension 开启录制。
    • 方式二:使用 Apple 在 iOS 12.0 中新增的 RPSystemBroadcastPickerView,使 App 界面弹出 “开启屏幕共享” 的按钮,提示用户通过点击该按钮开启录制,示例代码如下:

    信息

    RPSystemBroadcastPickerView 存在一些使用限制并可能在后续版本的 iOS 系统中失效。因此,请酌情使用方式二。

    Swift
    let frame = CGRect(x: 0, y:0, width: 60, height: 60)
    // 创建 RPSystemBroadcastPickerView 对象
    systemBroadcastPicker = RPSystemBroadcastPickerView(frame: frame)
    systemBroadcastPicker?.showsMicrophoneButton = false
    systemBroadcastPicker?.autoresizingMask = [.flexibleTopMargin, .flexibleRightMargin]
    // 获取主应用程序的 Bundle Identifier
    let bundleId = Bundle.main.bundleIdentifier ?? ""
    // 设置 Extension 名称
    systemBroadcastPicker?.preferredExtension = "\(bundleId).Agora-ScreenShare-Extension"

在频道中发布屏幕共享视频流

加入频道后,调用 updateChannelWithMediaOptions,发布采集的屏幕共享视频流。示例代码如下:

Swift
// 在进行屏幕视频采集(state 为 capturing,souceType 为 screen)时,定义 rtcEngine 的行为
func rtcEngine(_ engine: AgoraRtcEngineKit, localVideoStateChangedOf state: AgoraVideoLocalState, error: AgoraLocalVideoStreamError, sourceType: AgoraVideoSourceType) {
    switch (state, sourceType) {
    case (.capturing, .screen):
        // 在频道中发布屏幕采集的视频
        option.publishScreenCaptureVideo = true
        // 在频道中发布屏幕采集的音频
        option.publishScreenCaptureAudio = true
        // 不发布摄像头采集的视频
        option.publishCameraTrack = false
        // 使用以上 option 中的设置更新频道设置
        agoraKit.updateChannel(with: option)

    default: break
    }
}

(可选)设置屏幕共享场景

调用 setScreenCaptureScenario 方法设置屏幕共享场景,按照实际使用场景,将 scenarioType 设置为以下任意一种:

  • AgoraScreenScenarioDocument(1):文档场景
  • AgoraScreenScenarioGaming(2):游戏场景
  • AgoraScreenScenarioVideo(3):视频场景
  • AgoraScreenScenarioRDC(4):远程控制场景

示例代码如下:

Swift
agoraKit.setScreenCaptureScenario(.video)

(可选)更新屏幕共享

如果你要更新屏幕共享的参数,调用 updateScreenCapture,修改屏幕共享的参数。例如:视频编码分辨率、帧率、码率。

Swift
// 设置新的屏幕采集参数
private lazy var screenParams: AgoraScreenCaptureParameters2 = {
    let params = AgoraScreenCaptureParameters2()
    params.captureVideo = true
    params.captureAudio = true
    let audioParams = AgoraScreenAudioParameters()
    // 设置采集的系统音量
    audioParams.captureSignalVolume = 70 // 更新音量
    params.audioParams = audioParams
    let videoParams = AgoraScreenVideoParameters()
    // 更新共享屏幕的分辨率
    videoParams.dimensions = CGSizeMake(960, 540) // 更新为 960 x 540
    // 更新视频编码帧率
    videoParams.frameRate = .fps30 // 更新为 30 fps
    // 更新视频编码码率
    videoParams.bitrate = 1500 // 更新为 1500 Kbps
    params.videoParams = videoParams
    return params
}()

// 更新屏幕共享参数
agoraKit.updateScreenCapture(screenParams)

停止屏幕共享

调用 stopScreenCapture,在频道内停止屏幕共享。示例代码如下:

Swift
// 停止屏幕共享
agoraKit.stopScreenCapture()
// 停止在频道中发布屏幕采集的视频
option.publishScreenCaptureVideo = false
// 停止在频道中发布屏幕采集的音频
option.publishScreenCaptureAudio = false
// 在频道中发布摄像头采集的视频
option.publishCameraTrack = true
agoraKit.updateChannel(with: option)

参考信息

功能介绍

声网 SDK 的屏幕共享特性可提供以下功能,适用于各种场景下的屏幕共享。

信息

屏幕共享功能在各平台的适用情况如下:

  • 桌面端(Windows 和 macOS):支持上述所有屏幕共享功能。
  • 移动端(Android 和 iOS):仅支持共享整个屏幕。

共享整个屏幕

将你的整个屏幕进行共享,包括屏幕中的所有信息,该功能支持同时采集两块屏幕的信息并共享。

共享 App 窗口

如果你不希望将整个屏幕内容分享给其他用户,可以只共享一个 App 窗口中的画面。

共享指定区域

如果你只希望共享屏幕或 App 窗口中的一部分,可以在开启屏幕共享时设置一个共享区域,只有该区域内的画面会被分享。

高级功能

除上述功能外,你还可以在开启屏幕共享时通过调整参数,实现以下高级共享功能:

  • 屏蔽指定 App 窗口:在共享屏幕时,如果你不希望暴露某个 App 窗口,可以选择将该 App 窗口屏蔽,则该窗口将不会出现在共享的屏幕中。
  • 描边:如果你需要勾勒出正在共享的范围,可以对指定的 App 窗口或屏幕进行描边,并自定义描边的宽度、颜色和透明度。
  • 前置指定 App 窗口:如果你同时共享了多个 App 窗口,可能会出现窗口之间互相遮挡的情况,你可以指定一个 App 窗口并将其前置,即可避免它被其他窗口遮挡。
  • 设置共享场景:SDK 会根据你设置的场景自动调节 QOE (Quality of Experience) 策略。
    • 在共享文档、幻灯片、表格,或者远程控制时,你可以将共享场景设置为文档场景或远程控制场景,SDK 会优先保障画质,并降低接收端看共享视频的延时。
    • 在共享游戏或电影、视频直播时,你可以将共享场景设置为游戏场景或视频场景,SDK 会优先保障流畅性。

优势介绍

声网提供的屏幕共享特性具备以下优势:

超高清画质体验

支持分辨率为 4K、帧率为 60 fps 的超高清视频。

多 App 支持

适配 WPS Office、Microsoft PowerPoint、Visual Studio Code、Adobe Photoshop、Windows Media Player 和 Scratch 等多款主流 App,方便用户在屏幕共享时直接共享指定 App,提升功能易用性。

多设备支持

支持多设备同时共享,兼容 Windows 8 系统、无独立显卡的设备、双显卡设备、外接屏幕设备等,满足各种场景下的设备使用需求。

多平台适配

支持 iOS、Android、macOS、Windows、Web、Unity、Flutter、React Native、Unreal Engine 等平台。

高安全保障

支持仅共享 App 和屏幕中的部分区域内容,并支持屏蔽指定 App 窗口,有效保障用户信息安全。

示例项目

声网提供了开源的示例项目供你参考,你可以前往下载或查看其中的源代码。

API 参考