业务流程
本节汇总了电商直播场景中一些常见的业务流程,帮助您更好地理解整个场景的实现流程。
下图展示了主播(房主)本地预览、创建房间、进房开播、退房关播的流程。


下图展示了主播 A 邀请主播 B 进行跨房 PK 连麦的流程。跨房 PK 过程中,两个房间内的观众都可以看到两个房主 PK 连麦直播的画面。


下图展示了 RTC 实时互动直播间观众进入房间、申请连麦、结束连麦、退出房间的流程。


下图展示了 RTC 旁路直播间 CDN 观众进入房间、申请连麦、结束连麦、退出房间的流程。


下图展示了直播带货场景中主播编辑、上架商品,观众浏览、购买商品的流程。


接入准备
步骤1:开通服务
电商直播场景通常需要依赖腾讯云 实时音视频 TRTC、腾讯美颜特效、播放器 SDK 等付费 PaaS 服务构建。其中,TRTC 负责提供实时音视频互动能力,腾讯特效负责提供美颜特效能力,播放器负责提供直播和点播播放能力。您可根据实际业务需求自由选择开通上述服务。
1. 首先,您需要登录 实时音视频 TRTC 控制台 > 应用管理 创建应用,您可根据需要选择升级 TRTC 应用版本,例如旗舰版可解锁更多增值功能服务。

说明:
建议创建两个应用分别用于测试环境和生产环境,首次开通 TRTC 服务可前往 试用中心 免费领取 10000 分钟试用时长包。
TRTC 包月套餐(入门版、基础版、尊享版、旗舰版)可以解锁不同的增值功能服务,详情可见 包月套餐说明。
2. 应用创建完毕之后,您可以在 应用管理 的应用概览栏目看到该应用的基本信息,其中需要您保管好 SDKAppID、SDK密钥便于后续使用,同时应避免密钥泄露造成流量盗刷。

1. 您需要登录 腾讯云视立方控制台 > License 管理 > 移动端 License,单击新建测试 License,填写基本信息。
测试版 License 免费测试有效期为14天,可续期一次共28天。根据实际需求填写
App Name
、Package Name
和 Bundle ID
,选择腾讯特效,选择所需测试的能力:高级套餐 S1-07、原子能力 X1-01、原子能力 X1-02、原子能力 X1-03,并补充资质,准确填写公司名称、所属行业类型,上传公司营业执照,单击确定提交审核申请,等待人工审核流程。

2. 测试版 License 成功创建后,页面会显示生成的 License 信息。此时 Key 和 LicenseURL 两个参数暂未生效,等待提交审核通过后方可生效使用。在 SDK 初始化配置时需要传入 License URL 和 License Key 两个参数,请妥善保存以下信息。


1. 登录 腾讯云视立方控制台 > License 管理 > 移动端 License,单击新建测试 License。
若您想在已创建的测试应用中申请测试播放器功能,则需选择您想测试的应用,单击测试新功能。

2. 根据实际需求填写
App Name
、Package Name
和 Bundle ID
,选择播放器(高级版),单击确定。

3. 测试版 License 成功创建后,页面会显示已生成的 License 信息。在 SDK 初始化配置时需要传入 License Key 和 License URL 两个参数,请妥善保存以下信息。


注意:
同一应用的 License URL 和 Key 唯一,测试版 License 升级为正式版后 License URL 和 Key 不变。
测试版 License 有效期内可单击右侧的编辑,进入修改 Bundle ID 和 Package Name 信息,单击确定即可保存。
步骤2:SDK 集成
该步骤介绍如何完成 Flutter RTC Engine 的接入。
环境准备
Flutter 2.0及以上版本。
Android 端开发:
Android Studio 3.5及以上版本。
App 要求 Android 4.1及以上版本设备。
请确保您的项目支持 CMake 3.13及以上版本
iOS 端开发:
Xcode 11.0及以上版本。
osx 系统版本要求10.11及以上版本。
请确保您的项目已设置有效的开发者签名。
Flutter 端集成
方式一:
dependencies:tencent_rtc_sdk: ^12.5.4live_flutter_plugin: 12.5.0super_player:git:url: https://github.com/LiteAVSDK/Player_Flutterpath: Flutterref: release_pro_v12.0.0tencent_effect_flutter:git:url: https://github.com/Tencent-RTC/TencentEffect_Flutter
方式二:
本地依赖:从 tencent_effect_flutter 下载最新版本的 tencent_effect_flutter,然后把文件夹 android、ios、lib 和文件 pubspec.yaml、tencent_effect_flutter.iml 添加到工程目录下,然后在工程的 pubspec.yaml 文件中添加如下引用:(可参考 demo)。
dependencies:tencent_rtc_sdk: ^12.5.4live_flutter_plugin: 12.5.0super_player:git:url: https://github.com/LiteAVSDK/Player_Flutterpath: Flutterref: release_pro_v12.0.0tencent_effect_flutter:path: ../
执行如下命令:
flutter pub get
注意:
tencent_effect_flutter 只是提供一个桥接,美颜功能依赖各个平台提供的美颜SDK,所以如果需要更新美颜 SDK,您可以通过以下步骤进行 SDK 升级。
Android 在您的工程下的 app 模块下找到 build.gradle 文件,将 implementation 'com.tencent.mediacloud:TencentEffect_S1-04:latest.release' 中的
latest.release
字段修改为最新的版本号。如果您需要更换套餐,那么需要将
TencentEffect_S1-04
字段修改为您的套餐字段 。需要更换套餐类型和 SDK 版本:
1. 美颜 Flutter SDK 的依赖方式需要修改为本地依赖。
2. 在美颜 Flutter SDK 下找到
ios/tencent_effect_flutter.podspec
文件,打开找到 TencentEffect_All 修改为您需要的套餐,后边的版本号也可以修改为您需要的版本号。3. 在您工程的 ios 目录下执行
pod update
命令即可。原生端集成
1. 在 app 模块下找到 build.gradle 文件,添加您对应套餐的 maven 引用地址,例如您选择的是S1-04套餐,则添加如下:
dependencies {implementation 'com.tencent.mediacloud:TencentEffect_S1-04:latest.release'}
2. 在您工程下的
android/app
模块下找到 src/main/assets
文件夹,将 demo 工程中 demo/android/app/src/main/assets
中的 lut 和 MotionRes 复制到您工程的 android/app/ src/main/assets
中,如果您的工程没有 assets 文件夹可以手动创建一个。3. 如果您使用的 Android 版美颜 SDK 小于3.9版本,您需要在 app 模块下找到 AndroidManifest.xml 文件,在 application 表单内添加如下标签:
<uses-native-libraryandroid:name="libOpenCL.so"android:required="false" />//true 表示libOpenCL是当前app必需的。如果没有此库,系统将不允许app安装//false 表示libOpenCL不是当前app必需的。无论有没有此库,都可以正常安装app。如果设备有此库,美颜特效SDK里的GAN类型特效能正常生效(例如童话脸、国漫脸)。如果设备没有此库,GAN类型不会生效,但也不影响SDK内其他功能的使用。//关于uses-native-library的说明,请参考Android 官网介绍:https://developer.android.com/guide/topics/manifest/uses-native-library-element
添加后如下图:


步骤3:工程配置
1. 权限配置。
在 AndroidManifest.xml 中配置 App 权限,电商直播场景下 LiteAVSDK 及腾讯特效 SDK 需要以下权限。
<uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.RECORD_AUDIO" /><uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /><uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.CAMERA" /><uses-feature android:name="android.hardware.camera.autofocus" />
注意:
请勿设置
android:hardwareAccelerated="false"
,关闭硬件加速之后,会导致对方的视频流无法渲染。LiteAVSDK 没有内置权限申请逻辑,需要您自行声明相应的权限,部分权限(如存储、录音、相机等)还需要在运行时动态申请。
若 Android 项目
targetSdkVersion
为 31 或者目标设备涉及到 Android 12 及更高系统版本,官方要求需要在代码中动态申请 android.permission.BLUETOOTH_CONNECT
权限,以正常使用蓝牙功能,具体信息请参见 Android 官方说明。2. 混淆配置。
由于我们在 SDK 内部使用了 Java 的反射特性,需要您在 proguard-rules.pro 文件中将 SDK 相关类加入不混淆名单。
如果您在打 release 包时,启用了编译优化(把 minifyEnabled 设置为 true),会裁掉一些未在 Java 层调用的代码,而这些代码有可能会被 native 层调用,从而引起 no xxx method 的异常。
如果您启用了这样的编译优化,那就要添加这些 keep 规则,防止 xmagic 的代码被裁掉:
-keep class com.tencent.** { *; }-keep class org.light.** { *;}-keep class org.libpag.** { *;}-keep class org.extra.** { *;}-keep class com.gyailib.**{ *;}-keep class androidx.exifinterface.** { *;}
1. 权限配置。
电商直播场景下 LiteAVSDK 及腾讯特效 SDK 需要以下权限。在 App 的 Info.plist 中添加以下两项,分别对应麦克风和摄像头在系统弹出授权对话框时的提示信息。
Privacy - Microphone Usage Description,并填入麦克风使用目的提示语。
Privacy - Camera Usage Description,并填入摄像头使用目的提示语。


2. 如需 App 进入后台仍然运行相关功能,可在 XCode 中选中当前工程项目,并在 Capabilities 下将设置项 Background Modes 设定为 ON,并勾选 Audio, AirPlay, and Picture in Picture,如下图所示:


步骤4:鉴权与许可
UserSig 是腾讯云设计的一种安全保护签名,目的是为了阻止恶意攻击者盗用您的云服务使用权,TRTC 在进房时校验该鉴权凭证。
正式运行阶段:推荐安全等级更高的服务端计算 UserSig 方案,防止客户端被逆向破解泄露密钥。
具体实现流程如下:
1. 您的 App 在调用 SDK 的初始化函数之前,首先要向您的服务器请求 UserSig。
2. 您的服务器根据 SDKAppID 和 UserID 计算 UserSig。
3. 服务器将计算好的 UserSig 返回给您的 App。
4. 您的 App 将获得的 UserSig 通过特定 API 传递给 SDK。
5. SDK 将 SDKAppID + UserID + UserSig 提交给腾讯云服务器进行校验。
6. 腾讯云校验 UserSig,确认合法性。
7. 校验通过后,会向 TRTC SDK 提供实时音视频服务。


注意:
调试跑通阶段的本地 UserSig 计算方式不推荐应用到线上环境,容易被逆向破解导致密钥泄露。
我们提供了多个语言版本(Java/GO/PHP/Nodejs/Python/C#/C++)的 UserSig 服务端计算源代码,详见 UserSig 计算源码。
使用腾讯美颜特效之前,需要向腾讯云校验许可凭证。设置 License 需要用到 License Key 和 License Url,示例代码如下。
TencentEffectApi.getApi()?.setLicense(licenseKey, licenseUrl, (errorCode, msg) {TXLog.printlog("打印鉴权结果 errorCode = $errorCode msg = $msg");if (errorCode == 0) {//鉴权成功}});
注意:
建议在相关业务模块的初始化代码中触发鉴权许可,避免在使用前才临时去下载 License,同时鉴权时应具有网络权限。
实际应用的
Bundle ID/Package Name
必须和创建 License 时绑定的 Bundle ID/Package Name
完全匹配,否则会导致 License 校验失败,鉴权错误码。步骤5:与直播关联
使用TRTC场景,或者直播场景。需要把使用的美颜功能和直播关联起来,需要添加处理原生工程的关联逻辑。
在应用的 application 类的 oncreate 方法(或 FlutterActivity 的 onCreate 方法)中添加如下代码:
TXLivePluginManager.register(new XmagicProcesserFactory());TRTCCloudPlugin.register(new XmagicProcesserFactory());
在应用的 AppDelegate 类中的 didFinishLaunchingWithOptions 方法里面添加如下代码:
XmagicProcesserFactory *instance = [[XmagicProcesserFactory alloc] init];[TXLivePluginManager registerWithCustomBeautyProcesserFactory:instance];[TencentRTCCloud setBeautyProcesserFactoryWithFactory:instance];
步骤6:初始化 SDK
// 声明成员变量import 'package:tencent_rtc_sdk/trtc_cloud.dart';import 'package:tencent_rtc_sdk/trtc_cloud_def.dart';import 'package:tencent_rtc_sdk/trtc_cloud_listener.dart';late TRTCCloud trtcCloud;// 创建 TRTC 实例trtcCloud = (await TRTCCloud.sharedInstance())!;// 创建 TRTCCloudListener 实例TRTCCloudListener listener = TRTCCloudListener(// 根据需要实现对应的回调onError: (errCode, errMsg) {// TODO});// 注册监听器,将 listener 绑定到 trtcCloud 实例trtcCloud.registerListener(listener);
说明:
void _initSettings(InitXmagicCallBack callBack) async {String resourceDir = await ResPathManager.getResManager().getResPath();TXLog.printlog('$TAG method is _initResource ,xmagic resource dir is $resourceDir');TencentEffectApi.getApi()?.setResourcePath(resourceDir);/// 复制资源只需要复制一次,在当前版本中如果成功复制了一次,以后就不需要再复制资源。/// Copying the resource only needs to be done once. Once it has been successfully copied in the current version, there is no need to copy it again in future versions.if (await isCopiedRes()) {callBack.call(true);return;} else {_copyRes(callBack);}}void _copyRes(InitXmagicCallBack callBack) {_showDialog(context);TencentEffectApi.getApi()?.initXmagic((result) {if (result) {saveResCopied();}_dismissDialog(context);callBack.call(result);if (!result) {Fluttertoast.showToast(msg: "initialization failed");}});}
说明:
// 创建 controllerTXVodPlayerController _controller = TXVodPlayerController();// 设置监听事件// 监听视频宽高变化,设置合适的宽高比例,也可自行设置宽高比例,视频纹理也会根据比例进行相应拉伸playEventSubscription = _controller.onPlayerEventBroadcast.listen((event) async {// Subscribe to event distributionfinal int code = event["event"];if (code == TXVodPlayEvent.PLAY_EVT_CHANGE_RESOLUTION) {int? videoWidth = event[TXVodPlayEvent.EVT_PARAM1];int? videoHeight = event[TXVodPlayEvent.EVT_PARAM2];}});// 添加布局child: TXPlayerVideo(androidRenderType: _renderType, onRenderViewCreatedListener: (viewId) {_controller.setPlayerView(viewId);},)// 初始化播放器,分配共享纹理await _controller.initialize();// 播放视频资源String _url ="http://1400329073.vod2.myqcloud.com/d62d88a7vodtranscq1400329073/59c68fe75285890800381567412/adp.10.m3u8";await _controller.startVodPlay(_url);
// 创建 PlayerTXLivePlayerController controller = TXLivePlayerController();// 添加布局child: TXPlayerVideo(onRenderViewCreatedListener: (viewId) {_controller.setPlayerView(viewId);},),// 3. 启动播放String flvUrl = "http://liteavapp.qcloud.com/live/liteavdemoplayerstreamid_demo1080p.flv";await _controller.startLivePlay(flvUrl);// 如果您使用的是 12.0 的以下版本,需要传入 playType,示例如下:await _controller.startLivePlay(flvUrl, playType: TXPlayType.LIVE_FLV);
接入过程
API 时序图

步骤1:主播进房推流
1. 主播进房前开启本地视频预览及音频采集。
startPushStream() async {trtcCloud = (await TRTCCloud.sharedInstance())!;// 设置视频编码参数,决定远端用户看到的画面质量trtcCloud.enterRoom(params, TRTCAppScene.videoCall);TRTCVideoEncParam encParams = new TRTCVideoEncParam();encParams.videoResolution = TRTCVideoResolution.res_640_360;encParams.videoBitrate = 550;encParams.videoFps = 15;trtcCloud.setVideoEncoderParam(encParams);// isFrontCamera 可指定使用前置/后置摄像头进行视频采集trtcCloud.startLocalPreview(isFrontCamera, viewId);// 这里可指定声音音质,从低到高分别为 SPEECH/DEFAULT/MUSICtrtcCloud.startLocalAudio(TRTCAudioQuality.speech);// 设置事件回调trtcCloud.registerListener(listener);}
注意:
您可根据业务需求自行设置视频编码参数
TRTCVideoEncParam
,各档位最佳分辨率、码率搭配详见 TRTCVideoResolution
。在
enterRoom
之前调用以上接口,SDK 只会开启摄像头预览和音频采集,并一直等到您调用 enterRoom
之后才开始推流。在
enterRoom
之后调用以上接口,SDK 会开启摄像头预览和音频采集,并自动开始推流。2. 主播设置本地画面的渲染参数,以及编码器输出画面模式(可选项)。
setupRenderParams() {TRTCRenderParams renderParams = new TRTCRenderParams();// 画面镜像模式renderParams.mirrorType = TRTCVideoMirrorType.auto;// 画面填充模式renderParams.fillMode = TRTCVideoFillMode.fill;// 画面旋转角度renderParams.rotation = TRTCVideoRotation.rotation0;// 设置本地画面的渲染参数trtcCloud.setLocalRenderParams(renderParams);// 设置视频编码器输出的画面方向TRTCVideoEncParam params = new TRTCVideoEncParam();params.videoResolutionMode = TRTCVideoResolutionMode.portrait;trtcCloud.setVideoEncoderParam(params);}
注意:
设置本地画面渲染参数仅影响本地画面的渲染效果。
设置编码器输出模式会影响房间中其他用户所观看到(以及云端录制文件)的画面效果。
3. 主播正式开始直播,进房推流。
enterRoomByAnchorWithUserId(String userId, String roomId) async {trtcCloud = (await TRTCCloud.sharedInstance())!;TRTCParams params = new TRTCParams();// 指定主播角色params.role = TRTCRoleType.anchor;params.sdkAppId = GenerateTestUserSig.sdkAppId;params.roomId = roomId;params.userId = userId;params.userSig = "userSig";// 以互动直播场景进房trtcCloud.enterRoom(params, TRTCAppScene.live);}// 进房结果事件回调TRTCCloudListener getListener() {return TRTCCloudListener(onEnterRoom:(int result) {if (result > 0) {// result 代表加入房间所消耗的时间(毫秒)debugPrint("Enter room sucess! cost time = ${result}");} else {// result 代表进房失败的错误码debugPrint("Enter room failed! error code = ${result}");}});}
注意:
TRTC 房间号分为整型
roomId
和字符串类型 strRoomId
,两种类型的房间不互通,建议统一房间号类型。TRTC 用户角色分为主播和听众,只有主播才有推流权限,进房时需指定用户角色,如未指定则默认为主播角色。
电商直播场景下,进房模式建议选用
TRTCAppSceneLIVE
。步骤2:观众进房拉流
1. 观众进入 TRTC 房间。
enterRoomByAudienceWithUserId(String userId, String roomId) async {trtcCloud = (await TRTCCloud.sharedInstance())!;TRTCParams params = new TRTCParams();// 指定观众角色params.role = TRTCRoleType.audience;params.sdkAppId = GenerateTestUserSig.sdkAppId;params.roomId = roomId;params.userId = userId;params.userSig = "userSig";// 以互动直播场景进房trtcCloud.enterRoom(params, TRTCAppScene.live);}// 进房结果事件回调TRTCCloudListener getListener() {return TRTCCloudListener(onEnterRoom:(int result) {if (result > 0) {// result 代表加入房间所消耗的时间(毫秒)debugPrint("Enter room sucess! cost time = ${result}");} else {// result 代表进房失败的错误码debugPrint("Enter room failed! error code = ${result}");}});}
2. 观众订阅主播音视频流。
child: TRTCCloudVideoView(key: ValueKey('RemoteView_$userId'),onViewCreated: (viewId) {trtcCloud.startRemoteView(userId,TRTCVideoStreamType.small, viewId);},),TRTCCloudListener getListener() {return TRTCCloudListener(onRemoteUserLeaveRoom(String userId, int reason) {setState(() {if (remoteUidSet.containsKey(userId)) {remoteUidSet.remove(userId);}});},onUserAudioAvailable: (String userId, bool available) {// 某远端用户发布/取消了自己的音频// 在自动订阅模式下,您无需做任何操作,SDK 会自动播放远端用户音频},onUserVideoAvailable: (String userId, bool available) {// 某远端用户发布/取消了主路视频画面if (available) {// 订阅远端用户的视频流,并绑定视频渲染控件setState(() {remoteUidSet[userId] = userId;});}if (!available && remoteUidSet.containsKey(userId)) {// 停止订阅远端用户的视频流,并释放渲染控件setState(() {remoteUidSet.remove(userId);});}});}
3. 观众设置远端画面的渲染模式(可选项)。
setupRemoteRenderParams() {TRTCRenderParams renderParams = new TRTCRenderParams();// 画面镜像模式renderParams.mirrorType = TRTCVideoMirrorType.auto;// 画面填充模式renderParams.fillMode = TRTCVideoFillMode.fill;// 画面旋转角度renderParams.rotation = TRTCVideoRotation.rotation0;// 设置远端画面的渲染模式trtcCloud.setRemoteRenderParams(userId, TRTCVideoStreamType.big, renderParams);}
步骤3:观众连麦互动
1. 观众切换为主播角色。
switchToAnchor() {// 切换为主播角色trtcCloud.switchRole(TRTCRoleType.anchor);}TRTCCloudListener getListener() {return TRTCCloudListener(// 切换角色事件回调onSwitchRole: (errCode, errMsg) => {if (errCode == 0) {// 切换角色成功}},);}
2. 观众开始本地音视频采集和推流。
setupTRTC() {// 设置视频编码参数,决定远端用户看到的画面质量TRTCVideoEncParam encParams = new TRTCVideoEncParam();encParams.videoResolution = TRTCVideoResolution.res_640_360;encParams.videoBitrate = 550;encParams.videoFps = 15;encParams.videoResolutionMode = TRTCVideoResolutionMode.portrait;trtcCloud.setVideoEncoderParam(encParams);// 这里可指定声音音质,从低到高分别为 SPEECH/DEFAULT/MUSICtrtcCloud.startLocalAudio(TRTCAudioQuality.speech);// isFrontCamera 可指定使用前置/后置摄像头进行视频采集trtcCloud.startLocalPreview(isFrontCamera, viewId);}
注意:
3. 观众下麦停止推流。
switchToAudience() {// 切换为观众角色trtcCloud.switchRole(TRTCRoleType.audience);}// 切换角色事件回调TRTCCloudListener getListener() {return TRTCCloudListener(// 切换角色事件回调onSwitchRole: (errCode, errMsg) => {if (errCode == 0) {// 停止摄像头采集推流trtcCloud.stopLocalPreview();// 停止麦克风采集推流trtcCloud.stopLocalAudio();}},);}
步骤4:退出与解散房间
1. 退出房间。
exitRoom() {// 停止摄像头采集推流trtcCloud.stopLocalPreview();// 停止麦克风采集推流trtcCloud.stopLocalAudio();trtcCloud.exitRoom();}// 离开房间事件回调TRTCCloudListener getListener() {return TRTCCloudListener(onExitRoom: (int reason) {if (reason == 0) {//主动调用 exitRoom 退出房间} else if (reason == 1) {//被服务器踢出当前房间} else if (reason == 2) {//当前房间整个被解散}},);}
注意:
待 SDK 占用的所有资源释放完毕后,SDK 会抛出
onExitRoom
回调通知到您。如果您要再次调用
enterRoom
或者切换到其他音视频 SDK,请等待 onExitRoom
回调之后再执行相关操作。否则可能会遇到例如摄像头、麦克风设备被强占等各种异常问题。2. 解散房间。
服务端解散房间
TRTC 提供了服务端解散数字类型房间 API
DismissRoom
,以及解散字符串类型房间 API DismissRoomByStrRoomId
,您可以通过调用服务端解散房间接口把房间内所有用户从房间移出,并解散房间。客户端解散房间
客户端没有直接解散房间的 API,需要各个客户端调用
exitRoom
退出房间,当房间内的所有主播和观众完成退房后,根据 TRTC 房间生命周期规则,房间将会自动解散,详情请参见 TRTC 退出房间。注意:
当一次直播结束之后,建议您调用服务端解散房间 API 确保房间被解散,防止因部分用户未如期退房导致房间存续,从而产生非期望的费用。
备选方案
API 时序图

步骤1:主播旁路推流
1. 前置条件:打开 TRTC 控制台,在应用管理页选择目标应用,点击配置进入基础功能页面,点击开启旁路转推,同时建议选择指定流旁路。旁路转推域名可在 云直播控制台 添加,也可使用系统默认的推流域名。


2. 主播进房前开启本地视频预览及音频采集。
// 获取用于展示主播本地画面预览的视频渲染控件setupTRTC() {// 设置视频编码参数,决定远端用户看到的画面质量TRTCVideoEncParam encParams = new TRTCVideoEncParam();encParams.videoResolution = TRTCVideoResolution.res_640_360;encParams.videoBitrate = 550;encParams.videoFps = 15;encParams.videoResolutionMode = TRTCVideoResolutionMode.portrait;trtcCloud.setVideoEncoderParam(encParams);// isFrontCamera 可指定使用前置/后置摄像头进行视频采集trtcCloud.startLocalPreview(isFrontCamera, viewId);// 这里可指定声音音质,从低到高分别为 SPEECH/DEFAULT/MUSICtrtcCloud.startLocalAudio(TRTCAudioQuality.defaultMode);}
注意:
您可根据业务需求自行设置视频编码参数
TRTCVideoEncParam
,各档位最佳分辨率、码率搭配详见 TRTCVideoResolution
。在
enterRoom
之前调用以上接口,SDK 只会开启摄像头预览和音频采集,并一直等到您调用 enterRoom
之后才开始推流。在
enterRoom
之后调用以上接口,SDK 会开启摄像头预览和音频采集,并自动开始推流。3. 主播设置本地画面的渲染参数,以及编码器输出画面模式。
setupRenderParams() {TRTCRenderParams renderParams = new TRTCRenderParams();// 画面镜像模式renderParams.mirrorType = TRTCVideoMirrorType.auto;// 画面填充模式renderParams.fillMode = selectedRemoteFillMode;// 画面旋转角度renderParams.rotation = selectedRemoteRotation;// 设置本地画面的渲染参数trtcCloud.setLocalRenderParams(renderParams);// 设置视频编码器输出的画面方向TRTCVideoEncParam params = new TRTCVideoEncParam();params.videoResolutionMode = TRTCVideoResolutionMode.portrait;trtcCloud.setVideoEncoderParam(params);}
注意:
设置本地画面渲染参数仅影响本地画面的渲染效果。
设置编码器输出模式会影响房间中其他用户所观看到(以及云端录制文件)的画面效果。
4. 主播正式开始直播,进房推流。
enterRoomByAnchorWithUserId(String userId, String roomId) async {trtcCloud = (await TRTCCloud.sharedInstance())!;TRTCParams params = new TRTCParams();// 指定主播角色params.role = TRTCRoleType.anchor;params.sdkAppId = GenerateTestUserSig.sdkAppId;params.roomId = roomId;params.userId = userId;params.userSig = "userSig";// 以互动直播场景进房trtcCloud.enterRoom(params, TRTCAppScene.live);}// 进房结果事件回调TRTCCloudListener getListener() {return TRTCCloudListener(onEnterRoom:(int result) {if (result > 0) {// result 代表加入房间所消耗的时间(毫秒)debugPrint("Enter room sucess! cost time = ${result}");} else {// result 代表进房失败的错误码debugPrint("Enter room failed! error code = ${result}");}});}
注意:
TRTC 房间号分为整型
roomId
和字符串类型 strRoomId
,两种类型的房间不互通,建议统一房间号类型。TRTC 用户角色分为主播和听众,只有主播才有推流权限,进房时需指定用户角色,如未指定则默认为主播角色。
电商直播场景下,进房模式建议选用
TRTCAppSceneLIVE
。5. 主播将音视频流转推到直播 CDN。
startPublishMediaToCDN(String streamName) {// 设定推流地址过期时间DateTime now = DateTime.now();DateTime expiration = now.add(Duration(days: 1));int time = expiration.millisecondsSinceEpoch ~/ 1000; // 转换为秒// 生成鉴权信息,getSafeUrl 方法可在云直播控制台-域名管理-推流配置-推流地址示例代码获取String secretParam = getSafeUrl(LIVE_URL_KEY, streamName, time);// 媒体流发布的目标地址TRTCPublishTarget target = TRTCPublishTarget();// 目标地址设定为旁路转推到 CDNtarget.mode = TRTCPublishMode.bigStreamToCdn;TRTCPublishCdnUrl cdnUrl = new TRTCPublishCdnUrl();// 拼接发布到直播服务商的推流地址(RTMP 格式)cdnUrl.rtmpUrl = "rtmp://$PUSH_DOMAIN
/live/$streamName?$secretParam";// 腾讯云直播推流地址为 true,第三方为 falsecdnUrl.isInternalLine = true;NSMutableArray* cdnUrlList = [NSMutableArray array];// 可以添加多个 CDN 推流地址target.cdnUrlList?.add(cdnUrl);// 设置媒体流编码输出参数(可根据业务需求自定义)TRTCStreamEncoderParam encoderParam = TRTCStreamEncoderParam();encoderParam.audioEncodedSampleRate = 48000;encoderParam.audioEncodedChannelNum = 1;encoderParam.audioEncodedKbps = 50;encoderParam.audioEncodedCodecType = 0;encoderParam.videoEncodedWidth = 540;encoderParam.videoEncodedHeight = 960;encoderParam.videoEncodedFPS = 15;encoderParam.videoEncodedGOP = 2;encoderParam.videoEncodedKbps = 1300;// 设置布局配置TRTCStreamMixingConfig config = TRTCStreamMixingConfig();TRTCUser selfUser = TRTCUser();selfUser.userId = localUserId;selfUser.intRoomId = localRoomId;TRTCVideoLayout selfVideoLayout = TRTCVideoLayout();selfVideoLayout.fixedVideoStreamType = TRTCVideoStreamType.big;selfVideoLayout.rect = TRTCRect(left: 0, top: 0, right: 1080, bottom: 1920);selfVideoLayout.zOrder = 0;selfVideoLayout.fixedVideoUser = selfUser;selfVideoLayout.fillMode = TRTCVideoFillMode.fit;config.videoLayoutList.add(selfVideoLayout);TRTCUser remoteUser = TRTCUser();remoteUser.userId = remoteUserId;remoteUser.intRoomId = remoteRoomId;TRTCVideoLayout remoteVideoLayout = TRTCVideoLayout();remoteVideoLayout.fixedVideoStreamType = TRTCVideoStreamType.big;remoteVideoLayout.rect = TRTCRect(left: 100, top: 50, right: 316, bottom: 384);remoteVideoLayout.zOrder = 1;remoteVideoLayout.fixedVideoUser = remoteUser;remoteVideoLayout.fillMode = TRTCVideoFillMode.fit;config.videoLayoutList.add(remoteVideoLayout);// 开始发布媒体流trtcCloud.startPublishMediaStream(target, encoderParam, config);}
注意:
单主播直播时仅需发起旁路转推任务,待有观众连麦或主播 PK 时再将此任务更新为混流转码任务。
推流鉴权 KEY
LIVE_URL_KEY
及推流域名 PUSH_DOMAIN
等信息需在 云直播控制台-域名管理 获取。国际站用户、旧版架构用户如调用
startPublishMediaStream
报错,请 联系我们 协助发布配置。TRTCCloudListener getListener() {return TRTCCloudListener(onStartPublishMediaStream: (taskId, errCode, errMsg, extraInfo) {// taskId: 当请求成功时,TRTC 后台会在回调中提供给您这项任务的 taskId,后续您可以通过该 taskId 结合 updatePublishMediaStream 和 stopPublishMediaStream 进行更新和停止// code: 回调结果,0 表示成功,其余值表示失败debugPrint("TRTCCloudListener.onStartPublishMediaStream: taskId:$taskId, code:$errCode, msg:$errMsg");publishMediaStreamTaskId = taskId;});}
步骤2:观众拉流播放
CDN 观众无需进入 TRTC 房间,可直接拉取主播旁路 CDN 流播放。
// 初始化播放器var livePlayer = new V2TXLivePlayer();// 设置播放器回调监听livePlayer.addListener(onPlayerObserver);// 设置播放器的视频渲染控件var code = await livePlayer?.setRenderViewID(_localViewId!);if (code != V2TXLIVE_OK) {debugPrint("StartPlay error: please check remoteView load");}// 设置延迟调节模式(可选项)livePlayer.setCacheParams(1.0, 5.0);// 自动模式livePlayer.setCacheParams(1.0, 1.0);// 极速模式livePlayer.setCacheParams(5.0, 5.0);// 流畅模式// 拼接拉流播放地址String flvUrl = "http://$PLAY_DOMAIN/live/$streamName.flv";String hlsUrl = "http://$PLAY_DOMAIN/live/$streamName.m3u8";String rtmpUrl = "rtmp://$PLAY_DOMAIN/live/$streamName";String webrtcUrl = "webrtc://$PLAY_DOMAIN/live/$streamName";// 启动播放livePlayer.startLivePlay(flvUrl);// 自定义设置填充模式(可选项)livePlayer.setRenderFillMode(V2TXLiveFillMode.v2TXLiveFillModeFit);// 自定义设置画面渲染方向(可选项)livePlayer.setRenderRotation(V2TXLiveRotation.v2TXLiveRotation0);
注意:
播放域名
PLAY_DOMAIN
需要您在 云直播控制台 添加自有已备案域名进行直播播放,并需 配置域名 CNAME。使用直播播放功能需要提前配置播放器 License 授权,否则将播放失败(黑屏),详见 鉴权与许可。
步骤3:观众连麦互动
1. 连麦观众需要进入 TRTC 房间与主播实时互动。
// 进入 TRTC 房间并开始推流enterRoomWithUserId(String userId, String roomId) async {trtcCloud = (await TRTCCloud.sharedInstance())!;TRTCParams params = new TRTCParams();// 指定主播角色params.role = TRTCRoleType.anchor;params.sdkAppId = GenerateTestUserSig.sdkAppId;params.roomId = roomId;params.userId = userId;params.userSig = "userSig";// 开启本地音视频采集startLocalMedia();// 以互动直播场景进房trtcCloud.enterRoom(params, TRTCAppScene.live);}// 开启本地视频预览及音频采集startLocalMedia() {// 设置视频编码参数,决定远端用户看到的画面质量TRTCVideoEncParam encParams = new TRTCVideoEncParam();encParams.videoResolution = TRTCVideoResolution.res_640_360;encParams.videoBitrate = 550;encParams.videoFps = 15;trtcCloud.setVideoEncoderParam(encParams);// isFrontCamera 可指定使用前置/后置摄像头进行视频采集trtcCloud.startLocalPreview(isFrontCamera, viewId);// 这里可指定声音音质,从低到高分别为 SPEECH/DEFAULT/MUSICtrtcCloud.startLocalAudio(TRTCAudioQuality.speech);}// 进房结果事件回调TRTCCloudListener getListener() {return TRTCCloudListener(onEnterRoom:(int result) {if (result > 0) {// result 代表加入房间所消耗的时间(毫秒)debugPrint("Enter room sucess! cost time = ${result}");} else {// result 代表进房失败的错误码debugPrint("Enter room failed! error code = ${result}");}});}
注意:
2. 连麦观众进房成功后开始订阅主播音视频流。
TRTCCloudListener getListener() {return TRTCCloudListener(onUserAudioAvailable: (userId, available) {// 某远端用户发布/取消了自己的音频// 在自动订阅模式下,您无需做任何操作,SDK 会自动播放远端用户音频},onUserVideoAvailable: (userId, available) {// 某远端用户发布/取消了主路视频画面if (available) {// 订阅远端用户的视频流,并绑定视频渲染控件trtcCloud.startRemoteView(userId, TRTCVideoStreamType.big, viewId);} else {// 停止订阅远端用户的视频流,并释放渲染控件trtcCloud.stopRemoteView(userId, TRTCVideoStreamType.big);}},onFirstVideoFrame: (userId, streamType, width, height) {// SDK 开始渲染本地或远端用户的首帧画面if (userId.isNotEmpty) {// 收到主播首帧画面,停止播放 CDN 流livePlayer.stopPlay();}},);}
注意:
TRTC 拉流
startRemoteView
可直接复用之前 CDN 拉流 setRenderView
的视频渲染控件。为避免在切换拉流器时画面中断,建议等待收到 TRTC 首帧回调
onFirstVideoFrame
后再停止 CDN 拉流。3. 主播更新发布混合媒体流。
// 连麦观众进房事件回调TRTCCloudListener getListener() {return TRTCCloudListener(onRemoteUserEnterRoom:(userId) {if (!mixUserList.contains(userId)) {mixUserList.add(userId);}updatePublishMediaToCDN();},// 更新媒体流的事件回调onUpdatePublishMediaStream: (taskId, errCode, errMsg, extraInfo) {// 您调用媒体流发布接口 (updatePublishMediaStream) 时传入的 taskId,会通过此回调再带回给您,用于标识该回调属于哪一次更新请求// code: 回调结果,0 表示成功,其余值表示失败},);}// 更新发布混合媒体流到直播 CDNupdatePublishMediaToCDN() {// 设定推流地址过期时间DateTime now = DateTime.now();DateTime expiration = now.add(Duration(days: 1));int time = expiration.millisecondsSinceEpoch ~/ 1000; // 转换为秒// 生成鉴权信息,getSafeUrl 方法可在云直播控制台-域名管理-推流配置-推流地址示例代码获取String secretParam = getSafeUrl(LIVE_URL_KEY, streamName, time);// 媒体流发布的目标地址TRTCPublishTarget target = TRTCPublishTarget();// 目标地址设定为旁路转推到 CDNtarget.mode = TRTCPublishMode.bigStreamToCdn;TRTCPublishCdnUrl cdnUrl = new TRTCPublishCdnUrl();// 拼接发布到直播服务商的推流地址(RTMP 格式)cdnUrl.rtmpUrl = "rtmp://$PUSH_DOMAIN
/live/$streamName?$secretParam";// 腾讯云直播推流地址为 true,第三方为 falsecdnUrl.isInternalLine = true;NSMutableArray* cdnUrlList = [NSMutableArray array];// 可以添加多个 CDN 推流地址target.cdnUrlList?.add(cdnUrl);// 设置媒体流编码输出参数(可根据业务需求自定义)TRTCStreamEncoderParam encoderParam = TRTCStreamEncoderParam();encoderParam.audioEncodedSampleRate = 48000;encoderParam.audioEncodedChannelNum = 1;encoderParam.audioEncodedKbps = 50;encoderParam.audioEncodedCodecType = 0;encoderParam.videoEncodedWidth = 540;encoderParam.videoEncodedHeight = 960;encoderParam.videoEncodedFPS = 15;encoderParam.videoEncodedGOP = 2;encoderParam.videoEncodedKbps = 1300;// 设置布局配置TRTCStreamMixingConfig config = TRTCStreamMixingConfig();TRTCUser selfUser = TRTCUser();selfUser.userId = localUserId;selfUser.intRoomId = localRoomId;TRTCVideoLayout selfVideoLayout = TRTCVideoLayout();selfVideoLayout.fixedVideoStreamType = TRTCVideoStreamType.big;selfVideoLayout.rect = TRTCRect(left: 0, top: 0, right: 1080, bottom: 1920);selfVideoLayout.zOrder = 0;selfVideoLayout.fixedVideoUser = selfUser;selfVideoLayout.fillMode = TRTCVideoFillMode.fit;config.videoLayoutList.add(selfVideoLayout);TRTCUser remoteUser = TRTCUser();remoteUser.userId = remoteUserId;remoteUser.intRoomId = remoteRoomId;TRTCVideoLayout remoteVideoLayout = TRTCVideoLayout();remoteVideoLayout.fixedVideoStreamType = TRTCVideoStreamType.big;remoteVideoLayout.rect = TRTCRect(left: 100, top: 50, right: 316, bottom: 384);remoteVideoLayout.zOrder = 1;remoteVideoLayout.fixedVideoUser = remoteUser;remoteVideoLayout.fillMode = TRTCVideoFillMode.fit;config.videoLayoutList.add(remoteVideoLayout);// 开始发布媒体流trtcCloud.startPublishMediaStream(target, encoderParam, config);}
注意:
为了保证 CDN 播放连续并且不会发生断流,您需要保持媒体流编码输出参数
encoderParam
以及流名称 streamName
不变。媒体流编码输出参数、混合画面布局参数可根据业务需求自定义;当前仅支持最高16路音视频输入,如果用户只有音频也会被算作一路。
同一个任务不支持纯音频、音视频、纯视频之间的切换。
4. 观众下麦退房,主播更新混流任务。
// 设置播放器回调监听var livePlayer = new V2TXLivePlayer();// 可复用 TRTC 视频渲染控件livePlayer.addListener(onPlayerObserver);// 重新开始播放 CDN 媒体流livePlayer.startLivePlay(flvUrl);onPlayerObserver(V2TXLivePlayerListenerType type, params) {if (type == V2TXLivePlayerListenerType.onVideoLoading) {// 视频加载事件} else if (type == V2TXLivePlayerListenerType.onVideoPlaying) {// 视频播放事件if (params.firstPlay) {trtcCloud.stopAllRemoteView();trtcCloud.stopLocalAudio();trtcCloud.stopLocalPreview();trtcCloud.exitRoom();}}}
注意:
为避免在切换拉流器时画面中断,建议等待收到播放器视频播放事件
onVideoPlaying
后再退出 TRTC 房间。TRTCCloudListener getListener() {return TRTCCloudListener(// 连麦观众退房事件回调onRemoteUserLeaveRoom: (userId, reason) {if (mixUserList.contains(userId)) {mixUserList.remove(userId);}// 主播更新混流任务updatePublishMediaToCDN();},onUpdatePublishMediaStream: (taskId, errCode, errMsg, extraInfo) {// 您调用媒体流发布接口 (updatePublishMediaStream) 时传入的 taskId,会通过此回调再带回给您,用于标识该回调属于哪一次更新请求// code: 回调结果,0 表示成功,其余值表示失败},);}
步骤4:主播关播与退房
exitRoom() {// 停止所有发布的媒体流trtcCloud.stopAllRemoteView();trtcCloud.stopLocalAudio();trtcCloud.stopLocalPreview();trtcCloud.exitRoom();}TRTCCloudListener getListener() {return TRTCCloudListener(// 停止媒体流的事件回调onStopPublishMediaStream: (taskId, errCode, errMsg, extraInfo) {// 您调用停止发布媒体流 (stopPublishMediaStream) 时传入的 taskId,会通过此回调再带回给您,用于标识该回调属于哪一次停止请求// code: 回调结果,0 表示成功,其余值表示失败},onExitRoom: (int reason) {if (reason == 0) {//主动调用 exitRoom 退出房间} else if (reason == 1) {//被服务器踢出当前房间} else if (reason == 2) {//当前房间整个被解散}},);}
注意:
停止发布媒体流传参
taskId
填写空字符串,将会停止所有您发布的媒体流。待 SDK 占用的所有资源释放完毕后,SDK 会抛出
onExitRoom
回调通知到您。高级功能
商品信息弹窗
自定义消息
1. 发送自定义消息。
方式一:主播在客户端发送商品弹窗相关的群组自定义消息。
var timManager = TencentImSDKPlugin.v2TIMManager.v2TIMMessageManager;// 构造商品弹窗消息体var messageRet = await timManager.createCustomMessage(data: json.encode({"msg": {"itemNumber": "123",// 商品编号"itemPrice": 99.99,// 商品价格"itemTitle": "title",// 商品标题"itemUrl": "link_url"// 商品图片地址},"cmd": "item_popup_msg"}));if (messageRet.code == 0) {// 创建消息成功final sendMsgRes = await timManager.sendMessage(message: messageRet.data?.messageInfo, receiver: '', groupID: "groupID");if (sendMsgRes.code == 0) {// 发送商品弹窗消息成功// 本地渲染商品弹窗效果} else {// 发送商品弹窗消息失败}} else {// 创建消息失败}
方式二:后台运营在服务端发送商品弹窗相关的群组自定义消息。
请求 URL 示例:
https://xxxxxx/v4/group_open_http_svc/send_group_msg?sdkappid=88888888&identifier=admin&usersig=xxx&random=99999999&contenttype=json
请求包体示例:
{"GroupId": "@TGS#12DEVUDHQ","Random": 2784275388,"MsgPriority": "High", // 消息的优先级,商品弹窗消息建议设置为高优先级"MsgBody": [{"MsgType": "TIMCustomElem","MsgContent": {// itemNumber: 商品编号; itemPrice: 商品价格; itemTitel: 商品标题; itemUrl: 商品图片地址"Data": "{\\"cmd\\": \\"item_popup_msg\\", \\"msg\\": {\\"itemNumber\\": 1, \\"itemPrice\\": 199.0, \\"itemTitle\\": \\"xxx\\", \\"itemUrl\\": \\"xxx\\"}}"}}]}
2. 接收自定义消息。
房间内其他用户客户端收到群自定义消息回调,然后进行消息解析和商品弹窗效果渲染。
// 收到群自定义消息V2TimAdvancedMsgListener _advancedMsgListener = V2TimAdvancedMsgListener(onRecvNewMessage: (message) {if (message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT) {if (message.groupID != null && message.groupID!.isNotEmpty) {// 这里处理 根据商品编号、商品价格、商品标题、商品图片地址渲染商品弹窗效果}}});var timManager = TencentImSDKPlugin.v2TIMManager.v2TIMMessageManager;timManager.addAdvancedMsgListener(listener: _advancedMsgListener);
SEI 信息
SEI 信息会插入到主播视频流中进行传输,能够实现商品信息弹窗和主播直播画面的精准同步。
1. 发送 SEI 信息。
主播在 TRTC 客户端发送商品弹窗相关的 SEI 消息。
// 发送 SEI 信息trtcCloud.sendSEIMsg(json.encode({"msg": {"itemNumber": "123",// 商品编号"itemPrice": 99.99,// 商品价格"itemTitle": "title",// 商品标题"itemUrl": "link_url"// 商品图片地址},"cmd": "item_popup_msg"}), 1);
2. 接收 SEI 信息。
方式一:观众在 TRTC 客户端接收 SEI 消息,然后进行消息解析和商品弹窗效果渲染。
TRTCCloudListener getListener() {return TRTCCloudListener(onRecvSEIMsg: (userId, message) {// 这里处理 根据商品编号、商品价格、商品标题、商品图片地址渲染商品弹窗效果},);}
方式二:观众在 CDN 流播放器端接收 SEI 消息,然后进行消息解析和商品弹窗效果渲染。
// 设置 TRTC 发送端 SEI 消息的 PayloadType(需在发送前设置)trtcCloud.callExperimentalAPI("{\\"api\\":\\"setSEIPayloadType\\",\\"params\\":{\\"payloadType\\":5}}");// 开启播放器端接收 SEI 消息, 同时设置 PayloadTypelivePlayer.enableReceiveSeiMessage(true, 5);// SEI 消息回调与解析TRTCCloudListener getListener() {return TRTCCloudListener(onRecvSEIMsg: (userId, message) {// 这里处理 根据商品编号、商品价格、商品标题、商品图片地址渲染商品弹窗效果},);}
注意:
需要确保 TRTC 发送端和播放器接收端的 SEI PayloadType 一致,观众端才能顺利接收到经由 TRTC 旁路转推的 SEI 消息。
商品讲解回放
通过播放提前录制好的商品讲解视频,实现商品讲解回放功能。
TXVodPlayerController 内部会自动识别播放协议,您只需要将您的播放 URL 传给 startVodPlay 函数即可。
TXVodPlayerController _controller = TXVodPlayerController();// 播放视频资源String _url ="http://1400329073.vod2.example.com/d62d88a7vodtranscq1400329073/59c68fe75285890800381567412/adp.10.m3u8";await _controller.startVodPlay(_url);
TXVodPlayerController _controller = TXVodPlayerController();// psign 即播放器签名,签名介绍和生成方式参见链接:https://cloud.tencent.com/document/product/266/42436TXPlayInfoParams params = TXPlayInfoParams(appId: 1252463788,fileId: "4564972819220421305", psign: "psignxxxxxxx");await _controller.startVodPlayWithParams(params);
播放控制:调整进度、暂停播放、恢复播放、结束播放。
TXVodPlayerController _controller = TXVodPlayerController();// 调整进度(秒)_controller.seek(time);// 暂停播放_controller.pause();// 恢复播放_controller.resume();// 结束播放_controller.stop();
跨房 PK 连麦
1. 任意一方发起跨房 PK 连麦。
connectOtherRoom(String roomId ){Map jsonDict = new Map();// 字符房间号为 strRoomIdjsonDict['roomId'] = otherRoomId;jsonDict['userId'] = otherUserId;trtcCloud.connectOtherRoom(jsonEncode(jsonDict));}// 请求跨房连麦的结果回调TRTCCloudListener getListener() {return TRTCCloudListener(onConnectOtherRoom: (userId, errCode, errMsg) {// 要跨房通话的另一个房间中的主播的用户 ID// 错误码,ERR_NULL 代表请求成功// 错误信息},);}
注意:
跨房 PK 连麦的本地用户和对端用户必须都为主播角色,且必须都有音频/视频上行。
可通过多次调用
ConnectOtherRoom()
来实现与多个房间主播跨房连麦,目前限制一个房间最多可以和其他三个房间的主播跨房连麦,一个房间中最多10个主播可与其他房间的主播跨房连麦。2. 两个房间中的所有用户都会收到来自另一个房间中的 PK 主播的音视频流可用回调。
TRTCCloudListener getListener() {return TRTCCloudListener(onUserVideoAvailable: (userId, available) {// 某远端用户发布/取消了主路视频画面if (available) {// 订阅远端用户的视频流,并绑定视频渲染控件trtcCloud.startRemoteView(otherUserId, TRTCVideoStreamType.big, viewId);} else {// 停止订阅远端用户的视频流,并释放渲染控件trtcCloud.stopRemoteView(otherUserId, TRTCVideoStreamType.big);}},onUserAudioAvailable: (userId, available) {// 某远端用户发布/取消了自己的音频// 在自动订阅模式下,您无需做任何操作,SDK 会自动播放远端用户音频},);}
3. 任意一方退出跨房 PK 连麦。
// 退出跨房连麦trtcCloud.disconnectOtherRoom();// 退出跨房连麦的结果回调TRTCCloudListener getListener() {return TRTCCloudListener(// 退出跨房连麦的结果回调onDisconnectOtherRoom: (errCode, errMsg) {},);}
注意:
调用
DisconnectOtherRoom()
后,即退出与所有其他房间主播的跨房 PK 连麦。跨房 PK 连麦的发起端和接收端任意一端均可调用
DisconnectOtherRoom()
退出跨房 PK 连麦。第三方美颜接入
由于 Flutter 端的 GL 环境与原生端环境进行了隔离,所以 Flutter 中接入美颜时无法直接建立绑定关系,需要在原生端进行关系的绑定,如下图所示:

流程:
1. 美颜侧抽象一层接口,并在美颜侧实现了接口。
2. 在应用启动时将此接口注册到三方推流端,这样三方推流端就可以通过此接口进行创建、使用、销毁美颜实例。
3. 第三方推流端再将创建和销毁美颜的能力暴露给自己的 Flutter 端供客户使用。
4. 美颜属性设置可通过美颜提供的 Flutter SDK 能力进行处理。
TRTC 支持接入第三方美颜特效产品,下面以腾讯特效为例,展示第三方美颜接入流程。
1. 集成腾讯特效 SDK、申请授权 License,详情请参见 接入准备 步骤实现。
2. 设置 SDK 素材资源路径(如有),细节可以参见 美颜 Demo。
String resourceDir = await ResPathManager.getResManager().getResPath();TXLog.printlog('$TAG method is _initResource ,xmagic resource dir is $resourceDir');TencentEffectApi.getApi()?.setResourcePath(resourceDir);/// Copying the resource only needs to be done once. Once it has been successfully copied in the current version, there is no need to copy it again in future versions.if (await isCopiedRes()) {callBack.call(true);return;} else {_copyRes(callBack);}void _copyRes(InitXmagicCallBack callBack) {_showDialog(context);TencentEffectApi.getApi()?.initXmagic((result) {if (result) {saveResCopied();}_dismissDialog(context);callBack.call(result);if (!result) {Fluttertoast.showToast(msg: "initialization failed");}});}
3. 设置第三方美颜的视频数据回调,将美颜 SDK 处理每帧数据结果传入 TRTC SDK 内部做渲染处理。
开发者只需要实现 ITXCustomBeautyProcesser 方法,在 onProcessVideoFrame 里面处理美颜数据即可。
1. 美颜预处理。
实现第三方美颜关键就是要实现2个接口:ITXCustomBeautyProcesserFactory 和 ITXCustomBeautyProcesser。第三方美颜处理的具体操作在 ITXCustomBeautyProcesser 的 onProcessVideoFrame 方法实现。
import com.tencent.live.beauty.custom.ITXCustomBeautyProcesser;import com.tencent.live.beauty.custom.ITXCustomBeautyProcesserFactory;import com.tencent.live.beauty.custom.TXCustomBeautyDef;public class TXThirdBeauty implements ITXCustomBeautyProcesserFactory {private BeatuyProcessor customBeautyProcessor;@Overridepublic ITXCustomBeautyProcesser createCustomBeautyProcesser() {customBeautyProcessor = new BeatuyProcessor();return customBeautyProcessor;}@Overridepublic void destroyCustomBeautyProcesser() {if (null != customBeautyProcessor) {customBeautyProcessor.destory();customBeautyProcessor = null;}}}class BeatuyProcessor implements ITXCustomBeautyProcesser {@Overridepublic void onProcessVideoFrame(TXCustomBeautyDef.TXCustomBeautyVideoFrame txCustomBeautyVideoFrame, TXCustomBeautyDef.TXCustomBeautyVideoFrame txCustomBeautyVideoFrame1) {if (mXmagicApi != null) {// 第三方处理txCustomBeautyVideoFrame1.texture.textureId = mXmagicApi.process(txCustomBeautyVideoFrame.texture.textureId,txCustomBeautyVideoFrame.width, txCustomBeautyVideoFrame.height);} else {txCustomBeautyVideoFrame1.texture.textureId = txCustomBeautyVideoFrame.texture.textureId;}return txCustomBeautyVideoFrame1.texture.textureId;}}
2. 注册第三方美颜对象。
根据自己的业务需求在需要的地方调用 TRTCCloudPlugin 的 register(ITXCustomBeautyProcesserFactory beautyProcesserFactory) 方法来注册美颜实例。
public class MainActivity extends FlutterActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);TRTCPlugin.setBeautyProcesserFactory(new TXThirdBeauty());}}
3. 开启美颜。
完成上述操作后,您可以通过 TRTC 的隐藏接口来开启/关闭美颜。
_enableCustomBeautyByNative(bool open) {trtcCloud.callExperimentalAPI("{\\"api\\": \\"enableVideoProcessByNative\\", \\"params\\": {\\"enable\\": $open}}");}
1. 美颜预处理。
实现第三方美颜关键就是要实现2个接口:ITXCustomBeautyProcesserFactory 和 ITXCustomBeautyProcesser。第三方美颜处理的具体操作在 ITXCustomBeautyProcesser 的 onProcessVideoFrame 方法实现。
import Foundationimport tencent_rtc_sdkimport TXCustomBeautyProcesserPluginclass TRTCVideoCustomPreprocessor: ITXCustomBeautyProcesserFactory, ITXCustomBeautyProcesser {func getSupportedPixelFormat() -> TXCustomBeautyProcesserPlugin.ITXCustomBeautyPixelFormat {return .Texture2D}func getSupportedBufferType() -> TXCustomBeautyProcesserPlugin.ITXCustomBeautyBufferType {return .Texture}func onProcessVideoFrame(srcFrame: TXCustomBeautyProcesserPlugin.ITXCustomBeautyVideoFrame, dstFrame: TXCustomBeautyProcesserPlugin.ITXCustomBeautyVideoFrame) -> TXCustomBeautyProcesserPlugin.ITXCustomBeautyVideoFrame {let outputTextureId = processTexture(srcFrame.textureId, width: Uint32(srcFrame.width), height: Uint32(srcFrame.height))dstFrame.textureId = outputTextureIdreturn desdstFrame}func createCustomBeautyProcesser() -> any TXCustomBeautyProcesserPlugin.ITXCustomBeautyProcesser {return self;}func destroyCustomBeautyProcesser() {invalidateBindedTetxure()}}
2. 注册第三方美颜对象。
根据自己的业务需求在需要的地方调用 TencentTRTCCloud 的 register(ITXCustomBeautyProcesserFactory beautyProcesserFactory) 方法来注册美颜实例。
@objc class AppDelegate: FlutterAppDelegate {override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool {lazy var customBeautyInstance: TRTCVideoCustomPreprocessor = {let customBeautyInstance = TRTCVideoCustomPreprocessor()return customBeautyInstance}()TencentRTCCloud.setBeautyProcesserFactory(factory: customBeautyInstance)return super.application(application, didFinishLaunchingWithOptions: launchOptions)}}
3. 开启美颜。
完成上述操作后,您可以通过 TRTC 的隐藏接口来开启/关闭美颜。
_enableCustomBeautyByNative(bool open) {trtcCloud.callExperimentalAPI("{\\"api\\": \\"enableVideoProcessByNative\\", \\"params\\": {\\"enable\\": $open}}");}
注意:
步骤1、步骤2根据不同的第三方美颜产品实现方式有所不同,而步骤3是 TRTC 集成第三方美颜的通用且重要步骤。
场景化集成腾讯美颜特效指引详见 TRTC SDK 集成腾讯特效,独立集成腾讯美颜特效指引详见 独立集成腾讯特效。
双路编码模式
开启双路编码模式后,当前用户的编码器会同时输出【高清大画面】和【低清小画面】两路视频流(但只有一路音频流)。这样,房间中的其他用户就可以根据自身的网络情况或屏幕大小选择订阅【高清大画面】或是【低清小画面】。
1. 开启大小画面双路编码模式。
enableDualStreamMode(bool enable) {TRTCVideoEncParam smallVideoEncParam = new TRTCVideoEncParam();smallVideoEncParam.videoResolution = TRTCVideoResolution.res_480_270;smallVideoEncParam.videoBitrate = 550;smallVideoEncParam.videoFps = 15;smallVideoEncParam.videoResolutionMode = TRTCVideoResolutionMode.portrait;trtcCloud.enableSmallVideoStream(enable, smallVideoEncParam);}
注意:
双路编码开启后,会消耗更多的 CPU 和 网络带宽,所以 Mac、Windows 或者高性能 Pad 可以考虑开启,不建议手机端开启。
2. 选择拉取远端用户视频流类型。
// 订阅远端用户视频流时可选视频流类型trtcCloud.startRemoteView(userId, TRTCVideoStreamType.big, viewId);// 亦可随时切换指定远端用户的大小画面trtcCloud.setRemoteVideoStreamType(userId, TRTCVideoStreamType.small);
注意:
双路编码开启后,可通过指定
streamType
视频流类型为 TRTCVideoStreamType.small 来拉取低清小画面观看。视图渲染控件
如果您业务涉及到切换显示区域的交互场景,可以使用 TRTC SDK 更新本地预览画面、更新远端用户视频渲染控件功能实现。
// 更新本地预览画面渲染控件trtcCloud.updateLocalView(viewId);// 更新远端用户视频渲染控件trtcCloud.updateRemoteView(userId, TRTCVideoStreamType.big, viewId);
注意:
传参
view
为目标视频渲染控件id,streamType
仅支持 TRTCVideoStreamType.big 和 TRTCVideoStreamType.sub。异常处理
异常错误处理
1. UserSig 相关。UserSig 校验失败会导致进房失败,您可参见 UserSig 生成与校验 进行校验。
枚举 | 取值 | 描述 |
ERR_USER_SIG_INVALID | -3320 | 进房参数 userSig 不正确 |
ERR_SERVER_INFO_ECDH_GET_TINYID | -100018 | userSig 校验失败,请检查 userSig 是否正确 |
2. 进退房相关。进房失败请先检查进房参数是否正确,且进退房接口必须成对调用,即便进房失败也需要调用退房接口。
枚举 | 取值 | 描述 |
ERR_ROOM_REQUEST_ENTER_ROOM_TIMEOUT | -3308 | 请求进房超时,请检查网络 |
ERR_SDK_APPID_INVALID | -3317 | 进房参数 sdkAppId 错误 |
ERR_ROOM_ID_INVALID | -3318 | 进房参数 roomId 错误 |
ERR_USER_ID_INVALID | -3319 | 进房参数 userID 不正确 |
3. 设备相关。可监听设备相关错误,在出现相关错误时 UI 提示用户。
枚举 | 取值 | 描述 |
ERR_CAMERA_START_FAIL | -1301 | 打开摄像头失败,例如在 Windows 或 Mac 设备,摄像头的配置程序(驱动程序)异常,禁用后重新启用设备,或者重启机器,或者更新配置程序 |
ERR_MIC_START_FAIL | -1302 | 打开麦克风失败,例如在 Windows 或 Mac 设备,麦克风的配置程序(驱动程序)异常,禁用后重新启用设备,或者重启机器,或者更新配置程序。 |
ERR_CAMERA_NOT_AUTHORIZED | -1314 | 摄像头设备未授权,通常在移动设备出现,可能是权限被用户拒绝了。 |
ERR_MIC_NOT_AUTHORIZED | -1317 | 麦克风设备未授权,通常在移动设备出现,可能是权限被用户拒绝了。 |
ERR_CAMERA_OCCUPY | -1316 | 摄像头正在被占用中,可尝试打开其他摄像头。 |
ERR_MIC_OCCUPY | -1319 | 麦克风正在被占用中,例如移动设备正在通话时,打开麦克风会失败。 |
本地和远端镜像模式设置
默认情况下,TRTC 本地和远端画面的镜像模式策略如下:
使用前置摄像头采集:本地预览画面无镜像效果,远端观众观看有镜像效果。
使用后置摄像头采集:本地预览画面无镜像效果,远端观众观看无镜像效果。
当然,您也可以通过
setLocalRenderParams
和 setVideoEncoderMirror
分别自定义本地预览画面镜像效果,以及视频编码输出画面的镜像效果(远端观众及云端录制的镜像模式)。如果您希望同时设置本地预览的镜像效果和远端观众观看的镜像效果,请按照如下方法进行编码。// 设置本地画面的渲染参数TRTCRenderParams renderParams = new TRTCRenderParams();renderParams.fillMode = TRTCVideoFillMode.fill;renderParams.rotation = TRTCVideoRotation.rotation0;renderParams.mirrorType = TRTCVideoMirrorType.enable;trtcCloud.setLocalRenderParams(renderParams);
采集设备旋转导致观众画面旋转
如果您遇到采集设备(主播端手机)在旋转翻转时,观众端拉流观看的画面渲染方向发生变化,您可以通过关闭 SDK 重力感应来避免这种现象。
trtcCloud.setGravitySensorAdaptiveMode(TRTCGSensorMode.disable);
说明:
RTMP 与 TRTC 互通问题
RTMP 属于 TRTC 一个子模块,能与 TRTC 其他端互通,互通延迟正常情况下小于600ms,也可使用 TRTC 录制、转推等已有能力。网络架构如下图所示。


推流地址生成
rtmp://rtmp.rtc.qq.com/push/房间号?sdkappid=应用&userid=用户名&usersig=签名
注意:
本功能自2023年02月15日起结束免费内测,如需使用该功能需要订阅 TRTC 应用包月套餐尊享版或旗舰版来解锁功能。
本功能默认只支持字符串房间号,不超过64个字符;如需使用数字房间号,则需在推拉流 URL 地址中拼接参数
&use_number_room_id=1
。摄像头缩放/对焦/切换问题
电商直播场景下,主播可能会对摄像头有自定义调整的需求,TRTC SDK 设备管理类下也有解决此类需求的相关接口。
1. 查询、设置摄像头的缩放倍数。
// 获取摄像头的最大缩放倍数(仅适用于移动端)double zoomRatio = trtcCloud.getDeviceManager().getCameraZoomMaxRatio();// 设置摄像头的缩放倍数(仅适用于移动端)// 取值范围1-5,取值为1表示最远视角(正常镜头),取值为5表示最近视角(放大镜头);最大值推荐为5,若超过5,视频数据会变得模糊不清trtcCloud.getDeviceManager().setCameraZoomRatio(zoomRatio);
2. 设置摄像头的对焦功能及位置。
// 开启或关闭摄像头的自动对焦功能(仅适用于移动端)trtcCloud.getDeviceManager().enableCameraAutoFocus(true);// 设置摄像头的对焦位置(仅适用于移动端)// 使用该接口的前提是先通过 enableCameraAutoFocus 关闭自动对焦功能trtcCloud.getDeviceManager().setCameraFocusPosition(x, y);
3. 判断、切换前置或后置摄像头。
// 判断当前是否为前置摄像头(仅适用于移动端)bool isFrontCamera = trtcCloud.getDeviceManager().isFrontCamera();// 切换前置或后置摄像头(仅适用于移动端)// 传入true: 切换为前置;传入false: 切换为后置trtcCloud.getDeviceManager().switchCamera(!isFrontCamera);