屏幕共享.md 7.9 KB

屏幕共享

在视频通话或互动直播中进行屏幕共享,可以将说话人或主播的屏幕内容,以视频的方式分享给其他说话人或观众观看,以提高沟通效率。

屏幕共享在如下场景中应用广泛:

  • 视频会议场景中,屏幕共享可以将讲话者本地的文件、数据、网页、PPT 等画面分享给其他与会人;
  • 在线课堂场景中,屏幕共享可以将老师的课件、笔记、讲课内容等画面展示给学生观看。

自4.0.0 起,Agora 提供 C# API 进行屏幕共享。本文介绍如何使用 4.0.0 版和之后的 Unity SDK 在 Android 和 iOS 平台实现屏幕共享。

前提条件

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

Android 平台

在 Android 平台实现屏幕共享时,只需要调用 startScreenCapture 开启屏幕共享。你可以参考 agora-unity-example 中的 ScreenShare.cs 实现屏幕共享。

iOS 平台

  • 受系统限制,屏幕共享只支持 iOS 12.0 或之后的系统。
  • 该功能对设备性能要求较高,Agora 推荐你在 iPhone X 及之后机型上使用。

技术原理

iOS 端的屏幕共享是通过在 Extension 中使用 iOS 原生的 ReplayKit 框架实现录制屏幕,然后将屏幕共享流作为一个用户加入频道实现的。由于 Apple 不支持在主 app 进程采集屏幕,因此你需要为屏幕共享流单独创建一个 Extension。

img

实现步骤

  1. 使用Unity Editor build iOS, export Xcode 工程。

  2. 前往你的项目文件夹,用 Xcode 打开 unity-iphone/.xcodeproj 文件夹。

  3. 创建一个 Broadcast Upload Extension 用于开启屏幕共享的进程:

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

img

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

c. 在 Target 下选中刚创建的 Extension,点 General,在 Deployment Info 下将 iOS 的版本设置为 12.0 或之后。请确保app 和 extension有相同的 TARGETS/Deployment/iOS 版本。

img

d. 修改 SampleHandler.h 文件,以修改实现屏幕共享的代码逻辑:

  • 如果你只需使用 Agora 提供的 AgoraReplayKitExtension.framework 中的功能,修改方式为:选中 Target 为刚刚创建的 Extension,在 Info 中将 NSExtension > NSExtensionPrincipalClass 所对应的 ValueSampleHandler 改为 AgoraReplayKitHandler

    img

  • 如果你还需要自定义一些业务逻辑,修改方式为:将如下代码替换到 SampleHandler.h 文件中:

       // Objective-C
       #import "SampleHandler.h"
       #import "AgoraReplayKitExt.h"
       #import <sys/time.h>
         
       @interface SampleHandler ()<AgoraReplayKitExtDelegate>
         
       @end
         
       @implementation SampleHandler
         
       - (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {
           // User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.
           [[AgoraReplayKitExt shareInstance] start:self];
         
       }
         
       - (void)broadcastPaused {
           // User has requested to pause the broadcast. Samples will stop being delivered.
           NSLog(@"broadcastPaused");
           [[AgoraReplayKitExt shareInstance] pause];
       }
         
       - (void)broadcastResumed {
           // User has requested to resume the broadcast. Samples delivery will resume.
           NSLog(@"broadcastResumed");
           [[AgoraReplayKitExt shareInstance] resume];
         
       }
         
       - (void)broadcastFinished {
           // User has requested to finish the broadcast.
           NSLog(@"broadcastFinished");
           [[AgoraReplayKitExt shareInstance] stop];
         
       }
         
       - (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
           [[AgoraReplayKitExt shareInstance] pushSampleBuffer:sampleBuffer withType:sampleBufferType];
       }
         
       #pragma mark - AgoraReplayKitExtDelegate
         
       - (void)broadcastFinished:(AgoraReplayKitExt *_Nonnull)broadcast reason:(AgoraReplayKitExtReason)reason {
           switch (reason) {
               case AgoraReplayKitExtReasonInitiativeStop:
                   {
       //                NSDictionary *userInfo = @{NSLocalizedDescriptionKey : @"Host app stop srceen capture"};
       //                NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:userInfo];
       //                [self finishBroadcastWithError:error];
                       NSLog(@"AgoraReplayKitExtReasonInitiativeStop");
                   }
                   break;
               case AgoraReplayKitExtReasonConnectFail:
                   {
       //                NSDictionary *userInfo = @{NSLocalizedDescriptionKey : @"Connect host app fail need startScreenCapture in host app"};
       //                NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:userInfo];
       //                [self finishBroadcastWithError:error];
                       NSLog(@"AgoraReplayKitExReasonConnectFail");
                   }
                   break;
         
               case AgoraReplayKitExtReasonDisconnect:
                   {
       //                NSDictionary *userInfo = @{NSLocalizedDescriptionKey : @"disconnect with host app"};
       //                NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:userInfo];
       //               [self finishBroadcastWithError:error];
                       NSLog(@"AgoraReplayKitExReasonDisconnect");
                   }
                   break;
               default:
                   break;
           }
       }
         
       @end
    
  1. TARGETS 中选中你创建的Extension, 在 General/Frameworks and Libraries 中添加 Frameworks/Agora-RTC-Plugin/Agora-Unity-RTC-SDK/Plugins/iOS/ 路径下所有framework。

  2. 调用 startScreenCapture,并结合用户的手动操作,使 app 开启屏幕共享。

    • 方式一:提示用户在 iOS 系统的控制中心长按屏幕录制按钮,并选择用你创建的 Extension 开启录制。

示例项目

Agora 在 agora-unity-example 中提供屏幕共享的示例,你可以参考其中的如下文件实现屏幕共享:

  • ScreenShare.cs

开发注意事项

  • 请确保app 和 extension有相同的 TARGETS/Deployment/iOS 版本。

  • Broadcast Upload Extension 的内存使用限制为 50 MB,请确保屏幕共享的 Extension 内存使用不超过 50 MB。

  • 屏幕共享的进程中,需要调用 muteAllRemoteVideoStreamsmuteAllRemoteAudioStreams 方法取消接收远端用户的流,避免重复订阅。

API 参考

屏幕共享功能目前存在一些使用限制和注意事项,同时会产生费用,Agora 推荐你在调用 API 前先阅读如下 API 参考:

  • [startScreenCapture]()
  • [stopScreenCapture]()
  • [updateScreenCaptureParameters]()