即时通信 IM 屏幕分享(高级功能)

这篇档介绍如何在 LiveKit 中集成屏幕共享功能。通过该功能,用户可以在进入房间后将自己的屏幕画面实时分享给房间内的其他成员。

相关产品
即时通信 IM 产品页,含版本规格、价格与购买入口。

一、概述

注意:说明:

注意:视频流互斥限制: Live 屏幕分享时不支持打开摄像头。在 LiveKit 架构中,摄像头采集的视频流与屏幕分享视频流只能二选一,两者无法同时推流。

竖屏显示 横屏显示
 

二、环境配置

在正式调用接口开播前,请确保已按照相应平台的要求完成了屏幕分享的环境与参数配置。

三、Android

Android 10 及以上版本对用户隐私策略进行了调整,录屏与屏幕共享功能必须在 Foreground Service 中运行。LiveKit SDK 内部已自动完成了前台服务的封装,开发者在开播前无需再做额外的代码侵入。

四、iOS

iOS 系统要求跨应用的屏幕分享必须通过独立的 Broadcast Upload Extension 进程来配合主 App 进程进行推流。

五、步骤 1:配置 App Group

开发者在 Apple 开发者账号中配置 App Group 能力,打通主进程与 Extension 进程的跨进程通信渠道,注意完成后需要重新下载对应的 Provisioning Profile。具体操作如下:

1. 登录 Apple Developer,单击 Certificates, IDs & Profiles。

2. 在右侧的界面中单击加号。

3. 选择 App Groups,单击 Continue。

4. 在弹出的表单中填写 Description 和 Identifier,其中 Identifier 需要传入接口中对应的 App Group 参数。完成后单击 Continue。

5. 回到 Identifier 页面,左上边的菜单中选择 App IDs,然后单击你的 App ID(主 App 与 Extension 的 AppID 需要进行同样的配置)。

6. 选中 App Groups 并单击 Edit。

7. 在弹出的表单中选择你之前创建的 App Group,单击 Continue 返回编辑页,单击 Save 保存。

8. 重新下载 Provisioning Profile 并配置到 Xcode 中。

六、步骤 2:新建并配置 Extension 录屏扩展

1. 新建 Extension:在 Xcode 菜单依次单击 File > New > Target…,选择 Broadcast Upload Extension,不用勾选 Include UI Extension,单击 Finish 完成创建。

2. 配置 Extension 依赖:开发者在项目的 Podfile 中为新建的 Extension Target 配置依赖,引入专门定制的屏幕直播扩展库,配置完成后,在项目根目录下执行 pod install 进行更新。配置代码如下:

target 'YourXXReplayKit' do inherit! :search_paths pod 'TXLiteAVSDK_Professional/ReplayKitExt' // 与 Host APP 版本号保持一致。 end

3. 实现 SampleHandler:在 Extension 中重写系统录屏回调,将屏幕图像帧发送给主应用或服务器。

import ReplayKitimport TXLiteAVSDK_ReplayKitExtlet APPGROUP = "group.com.yourcompany.yourapp.screenshare"class SampleHandler: RPBroadcastSampleHandler, TXReplayKitExtDelegate { let recordScreenKey = Notification.Name.init("TRTCRecordScreenKey") override func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?) { // User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional. TXReplayKitExt.sharedInstance().setup(withAppGroup: APPGROUP, delegate: self) } override func broadcastPaused() { // User has requested to pause the broadcast. Samples will stop being delivered. } override func broadcastResumed() { // User has requested to resume the broadcast. Samples delivery will resume. } override func broadcastFinished() { // User has requested to finish the broadcast. TXReplayKitExt.sharedInstance().broadcastFinished() } func broadcastFinished(_ broadcast: TXReplayKitExt, reason: TXReplayKitExtReason) { var tip = "" switch reason { case TXReplayKitExtReason.requestedByMain: tip = "屏幕共享已结束" break case TXReplayKitExtReason.disconnected: tip = "应用断开" break case TXReplayKitExtReason.versionMismatch: tip = "集成错误(SDK 版本号不相符合)" break default: break } let error = NSError(domain: NSStringFromClass(self.classForCoder), code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:tip]) finishBroadcastWithError(error) } override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) { TXReplayKitExt.sharedInstance().send(sampleBuffer, with: sampleBufferType) }}

七、Flutter

Flutter 端的集成需要兼顾 Android 和 iOS 两个原生平台的底层特性。

·Android:确保原生层插件已自动开启 ForegroundService 前台服务。

·iOS:参照上述 iOS 平台前置配置指南,在 Xcode 原生工程中完成 App Group 配置、新建 Extension 目标、并在 Podfile 中配置 ReplayKitExt 依赖及执行 pod install。

八、开始屏幕分享直播

完成屏幕分享配置后,开发者可以选择 含 UI 或 无 UI(基于 Core SDK) 的模式指导用户完成开播进房。

注意:说明:屏幕分享功能属于独占式媒体流,仅支持在成功完成开播并进入房间后由开发者开启。

十、步骤 1:组件接入

请参考 LiveKit 组件接入文档完成对应平台的 主播开播页面。

·Android:主播开播。

·iOS:主播开播。

·Flutter:主播开播。

十一、步骤 2:配置平台参数

iOS 平台屏幕分享需要手动在组件源码的指定位置修改 App Group 参数,请参考以下源码位置:

·iOS(原生):live/Sources/Features/AnchorView/View/Game/AnchorGameView.swift。

·iOS(Flutter):live/livekit/lib/live_stream/features/live_room_anchor_widget.dart。

十二、步骤 3:开始直播

在集成 LiveKit 组件后,选择 AnchorPrepareView 组件的顶部工具栏 手游直播,再点击下方的开始直播按钮进房。成功进房后,UI 组件内部会自动开始进行屏幕画面推送。

十四、步骤 1:AtomicXCore SDK 集成

请参考 Core SDK 接入文档集成对应平台的 主播开播页面:

·Android:主播开播。

·iOS:主播开播。

·Flutter:主播开播。

十五、步骤 2:配置屏幕分享开播

配置开播的房间参数 LiveInfo.seatTemplate 为 videoLandscape4Seats(即开启适合屏幕共享的横屏多座位模板),请在 LiveListStore.startLive 成功回调后,再调用 DeviceStore.shared().startScreenShare()开启屏幕分享。

Android(Kotlin)iOS(Swift)Flutter(Dart)import io.trtc.tuikit.atomicxcore.api.live.LiveListStoreimport io.trtc.tuikit.atomicxcore.api.live.LiveInfoimport io.trtc.tuikit.atomicxcore.api.live.SeatLayoutTemplateimport io.trtc.tuikit.atomicxcore.api.device.DeviceStoreprivate fun startLiveForScreenShare() { val liveInfo = LiveInfo().apply { // 1. 设置直播的房间 ID liveID = "test_live_id" // 2. 设置直播的房间名称 liveName = "屏幕分享直播" // 3. 核心步骤:配置适合屏幕分享的布局模板 seatTemplate = SeatLayoutTemplate.VideoLandscape4Seats } // 4. 调用 LiveListStore 开始直播并进房 LiveListStore.shared().startLive(liveInfo, object : LiveListStore.StartLiveCallback { override fun onSuccess(roomInfo: Any) { // 5. 调用接口开启屏幕分享,系统会自动触发前台服务与系统弹窗 DeviceStore.shared().startScreenShare() } override fun onError(code: Int, message: String) { // 处理开播失败错误 } })}import AtomicXCoreprivate func startLiveForScreenShare() { var liveInfo = LiveInfo() // 1. 设置直播的房间 ID liveInfo.liveID = "test_live_id" // 2. 设置直播的房间名称 liveInfo.liveName = "屏幕分享直播" // 3. 核心步骤:配置适合屏幕分享的布局模板(横屏动态 1 视频 + 3 音频) liveInfo.seatTemplate = .videoLandscape4Seats // 4. 调用 LiveListStore 单例开始直播并进房 LiveListStore.shared.startLive(liveInfo: liveInfo) { [weak self] roomInfo in guard let self = self else { return } // 5. 调用 startScreenShare 开启屏幕分享 DeviceStore.shared.startScreenShare(appGroup: "group.com.yourcompany.yourapp.screenshare") } onError: { code, message in // 处理开播失败错误 }}import 'package:atomic_x_core/api/live/live_list_store.dart';import 'package:atomic_x_core/api/device/device_store.dart';Future<void> startLiveForScreenShare() async { // 1. 构建开播房间参数 LiveInfo liveInfo = LiveInfo(); liveInfo.liveID = "test_live_id"; liveInfo.liveName = "屏幕分享直播"; // 2. 核心步骤:配置适合屏幕分享的布局模板(横屏动态 1 视频 + 3 音频) // 注:具体枚举名请对齐你当前 Flutter SDK 定义的横屏 4 座位模板字段 liveInfo.seatTemplate = SeatLayoutTemplate.VideoLandscape4Seats; try { // 3. 调用 LiveListStore 开始直播并进房 await LiveListStore.shared.startLive(liveInfo: liveInfo); // 4. 调用 startScreenShare 开启屏幕分享 await DeviceStore.shared.startScreenShare(iOSAppGroup: 'group.com.yourcompany.yourapp.screenshare'); } catch (e) { // 处理开播或进房失败错误 }}

十六、结束屏幕分享直播

当需要停止屏幕画面分享时,请根据以下业务场景进行选择:

十七、场景一:仅结束屏幕分享

如果主播只想退出投屏状态,继续留在直播间进行直播,可以主动调用各平台的 stopScreenShare() 接口。

Android(Kotlin)iOS(Swift)Flutter(Dart)import io.trtc.tuikit.atomicxcore.api.device.DeviceStore/** * 停止 Android 屏幕共享 */fun stopScreenShare() { // 停止屏幕共享,底层会自动销毁前台保活服务 DeviceStore.shared().stopScreenShare() // 推荐:重新打开本地摄像头恢复露脸直播 DeviceStore.shared().openLocalCamera(isFront: true)}import AtomicXCore/** * 停止 iOS 屏幕共享 */func stopScreenCapture() { // 停止屏幕共享,底层会自动断开与 TUIKitReplay 扩展进程的通信 DeviceStore.shared.stopScreenShare() // 推荐:恢复本地摄像头画面 DeviceStore.shared.openLocalCamera(isFront: true)}import 'package:atomic_x_core/api/device/device_store.dart';/** * 停止 Flutter 屏幕共享 */Future<void> stopScreenShare() async { // 停止 Flutter 跨平台屏幕共享 await DeviceStore.shared.stopScreenShare(); // 推荐:恢复本地摄像头画面 await DeviceStore.shared.openLocalCamera(true);}

十八、场景二:直接结束直播

如果在投屏结束后直接选择下播,开发者只需要调用结束直播的 LiveListStore.endLive,SDK 底层会自动执行关闭屏幕分享的全部逻辑。

Android(Kotlin)iOS(Swift)Flutter(Dart)import android.util.Logimport com.tencent.cloud.tuikit.engine.extension.TUILiveListManagerimport io.trtc.tuikit.atomicxcore.api.live.LiveListStoreimport io.trtc.tuikit.atomicxcore.api.live.StopLiveCompletionHandler/** * 直接结束直播(下播):底层会自动级联关闭并释放当前的屏幕分享流 */fun stopLive() { LiveListStore.shared().endLive(object : StopLiveCompletionHandler { override fun onSuccess(statisticsData: TUILiveListManager.LiveStatisticsData) { Log.d("Live", "endLive success, duration: ${statisticsData.liveDuration}") } override fun onFailure(code: Int, desc: String) { Log.e("Live", "endLive error: $desc") } })}import AtomicXCore/** * 直接结束直播(下播):底层会自动级联关闭并释放当前的屏幕分享流 */func stopLive() { LiveListStore.shared.endLive { result in switch result { case .success(let data): debugPrint("endLive success") case .failure(let error): debugPrint("endLive error: \\(error.message)") } }}import 'package:flutter/material.dart';import 'package:atomic_x_core/api/live/live_list_store.dart';/** * 直接结束直播(下播):底层会自动级联关闭并释放当前的屏幕分享流 */Future<void> stopLive() async { // 调用接口结束直播,SDK 内部会自动停止推流、销毁房间并清理媒体资源 final result = await LiveListStore.shared.endLive(); if (result.isSuccess) { debugPrint("endLive success"); } else { debugPrint("endLive error: ${result.errorMessage}"); }}

20、开启共享失败,点击屏幕共享按钮没有反应。

在调用开启屏幕共享 API 前,需确保摄像头流已关闭,否则会与屏幕共享的视频流产生互斥。

21、在 iOS 平台上,App Group 是否必须配置?

在 iOS 平台上,如果不配置 App Group(即接口传 null 或未调用),屏幕分享虽然在部分系统版本下依然可以运行,但整体推流的稳定性会大幅度打折扣。为了保障线上产品的体验流畅度,强烈建议完成 App Group 的正确配置。

赞 (0)
上一篇 4天前
下一篇 4天前