三、步骤1:开通服务
电商直播场景通常需要依赖腾讯云 实时音视频 TRTC、腾讯美颜特效、播放器 SDK 等付费 PaaS 服务构建。其中,TRTC 负责提供实时音视频互动能力,腾讯特效负责提供美颜特效能力,播放器负责提供直播和点播播放能力。你可根据实际业务需求自由选择开通上述服务。
开通 TRTC 服务开通腾讯特效服务开通播放器服务
1. 首先,你需要登录 实时音视频 TRTC 控制台 > 应用管理 创建应用,你可根据需要选择升级 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 两个参数,请妥善保存以下信息。
四、步骤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 端集成
方式一:
远程依赖:在你工程的 pubspec.yaml 文件中添加如下引用,详细版本可以查看 tencent_rtc_sdk。
dependencies: tencent_rtc_sdk: ^12.5.4 live_flutter_plugin: 12.5.0 super_player: git: url: https://github.com/LiteAVSDK/Player_Flutter path: Flutter ref: release_pro_v12.0.0 tencent_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.4 live_flutter_plugin: 12.5.0 super_player: git: url: https://github.com/LiteAVSDK/Player_Flutter path: Flutter ref: release_pro_v12.0.0 tencent_effect_flutter: path: ../
执行如下命令:
flutter pub get
注意:tencent_effect_flutter 只是提供一个桥接,美颜功能依赖各个平台提供的美颜SDK,所以如果需要更新美颜 SDK,你可以通过以下步骤进行 SDK 升级。
注意:AndroidiOSAndroid 在你的工程下的 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 命令即可。
七、原生端集成
原生部分集成步骤如下,美颜特效 demo,可以参考 美颜特效 Demo 下载。
AndroidiOS1. 在 app 模块下找到 build.gradle 文件,添加你对应套餐的 maven 引用地址,例如你选择的是S1-04套餐,则添加如下: dependencies { implementation 'com.tencent.mediacloud:TencentEffect_S1-04:latest.release'}各套餐对应的 maven 地址,请参见 文档,SDK 的最新版本号可以在 版本历史 中查看。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-library android: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添加后如下:将 美颜特效 Demo 工程中 ios/Runner 目录下的 xmagic 文件夹复制到你工程中 ios/Runner 目录下,添加后如下:
注意:上述从 美颜特效 Demo 工程中复制的素材是测试素材,正式素材需要你在购买套餐之后 联系我们 进行获取并重新添加。
八、步骤3:工程配置
AndroidiOS
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" />
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:鉴权与许可
TRTC 鉴权凭证腾讯特效鉴权许可
UserSig 是腾讯云设计的一种安全保护签名,目的是为了阻止恶意攻击者盗用你的云服务使用权,TRTC 在进房时校验该鉴权凭证。
·调试跑通阶段:可以通过 客户端示例代码 和 控制台 两种方法计算生成 UserSig,仅用于调试测试。
·正式运行阶段:推荐安全等级更高的服务端计算 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 提供实时音视频服务。
使用腾讯美颜特效之前,需要向腾讯云校验许可凭证。设置 License 需要用到 License Key 和 License Url,示例代码如下。
TencentEffectApi.getApi()?.setLicense(licenseKey, licenseUrl, (errorCode, msg) { TXLog.printlog("打印鉴权结果 errorCode = $errorCode msg = $msg"); if (errorCode == 0) { //鉴权成功 }});
十、步骤5:与直播关联
使用TRTC场景,或者直播场景。需要把使用的美颜功能和直播关联起来,需要添加处理原生工程的关联逻辑。
AndroidiOS在应用的 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
初始化 TRTC SDK初始化腾讯特效 SDK初始化播放器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);说明:建议监听 SDK 事件通知,对一些常见错误进行日志打印和处理,详见 错误码表。
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"); } });}说明:初始化腾讯特效 SDK 之前,还需进行资源拷贝等准备工作,详细步骤可参考 腾讯特效 SDK 使用流程。
·点播播放场景 SDK 初始化。
// 创建 controllerTXVodPlayerController _controller = TXVodPlayerController();// 设置监听事件// 监听视频宽高变化,设置合适的宽高比例,也可自行设置宽高比例,视频纹理也会根据比例进行相应拉伸playEventSubscription = _controller.onPlayerEventBroadcast.listen((event) async { // Subscribe to event distribution final 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);
·直播播放场景 SDK 初始化。
// 创建 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);
十四、步骤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/MUSIC trtcCloud.startLocalAudio(TRTCAudioQuality.speech); // 设置事件回调 trtcCloud.registerListener(listener);}
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}"); } } ); }
十五、步骤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/MUSIC trtcCloud.startLocalAudio(TRTCAudioQuality.speech); // isFrontCamera 可指定使用前置/后置摄像头进行视频采集 trtcCloud.startLocalPreview(isFrontCamera, viewId);}
注意:你可根据业务需求自行设置视频编码参数 TRTCVideoEncParam,各档位最佳分辨率、码率搭配详见 TRTCVideoResolution。
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) { //当前房间整个被解散 } }, );}
2. 解散房间。
·服务端解散房间
TRTC 提供了服务端解散数字类型房间 API DismissRoom,以及解散字符串类型房间 API DismissRoomByStrRoomId,你可以通过调用服务端解散房间接口把房间内所有用户从房间移出,并解散房间。
·客户端解散房间
客户端没有直接解散房间的 API,需要各个客户端调用 exitRoom 退出房间,当房间内的所有主播和观众完成退房后,根据 TRTC 房间生命周期规则,房间将会自动解散,详情请参见 TRTC 退出房间。
注意:当一次直播结束之后,建议你调用服务端解散房间 API 确保房间被解散,防止因部分用户未如期退房导致房间存续,从而产生非期望的费用。
20、步骤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/MUSIC trtcCloud.startLocalAudio(TRTCAudioQuality.defaultMode);}
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}"); } } );}
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(); // 目标地址设定为旁路转推到 CDN target.mode = TRTCPublishMode.bigStreamToCdn; TRTCPublishCdnUrl cdnUrl = new TRTCPublishCdnUrl(); // 拼接发布到直播服务商的推流地址(RTMP 格式) cdnUrl.rtmpUrl = "rtmp://$PUSH_DOMAIN/live/$streamName?$secretParam"; // 腾讯云直播推流地址为 true,第三方为 false cdnUrl.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);}
发布媒体流后,SDK 会通过回调 onStartPublishMediaStream 带给你后台启动的任务标识(即 taskId)。
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; } );}
21、步骤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);
22、步骤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/MUSIC trtcCloud.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}"); } });}
注意:你可根据业务需求自行设置视频编码参数 TRTCVideoEncParam,各档位最佳分辨率、码率搭配详见 TRTCVideoResolution。
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(); } }, ); }
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(); // 目标地址设定为旁路转推到 CDN target.mode = TRTCPublishMode.bigStreamToCdn; TRTCPublishCdnUrl cdnUrl = new TRTCPublishCdnUrl(); // 拼接发布到直播服务商的推流地址(RTMP 格式) cdnUrl.rtmpUrl = "rtmp://$PUSH_DOMAIN/live/$streamName?$secretParam"; // 腾讯云直播推流地址为 true,第三方为 false cdnUrl.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);}
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 表示成功,其余值表示失败 }, );}
23、步骤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) { //当前房间整个被解散 } }, );}
25、商品信息弹窗
商品信息弹窗功能可以通过 IM 自定义消息 实现,也可以通过 SEI 信息 实现,下面将分别介绍这两种实现方式。
26、自定义消息
自定义消息需要依赖腾讯云 即时通信 IM 的能力,你需提前开通服务并导入 IM SDK,详细指引请参见 语聊房接入指引-接入准备。
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);
27、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 消息。
28、商品讲解回放
通过播放提前录制好的商品讲解视频,实现商品讲解回放功能。
首先需要进行 播放器初始化,然后开始播放录制视频。TXVodPlayer 支持两种播放模式,你可以根据需要自行选择:
TXVodPlayerController 内部会自动识别播放协议,你只需要将你的播放 URL 传给 startVodPlay 函数即可。
通过 URL 方式通过 FileId 方式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();
29、跨房 PK 连麦
1. 任意一方发起跨房 PK 连麦。
connectOtherRoom(String roomId ){ Map jsonDict = new Map(); // 字符房间号为 strRoomId jsonDict['roomId'] = otherRoomId; jsonDict['userId'] = otherUserId; trtcCloud.connectOtherRoom(jsonEncode(jsonDict));}// 请求跨房连麦的结果回调TRTCCloudListener getListener() { return TRTCCloudListener( onConnectOtherRoom: (userId, errCode, errMsg) { // 要跨房通话的另一个房间中的主播的用户 ID // 错误码,ERR_NULL 代表请求成功 // 错误信息 }, );}
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) { }, );}
30、第三方美颜接入
由于 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 里面处理美颜数据即可。
AndroidiOS1. 美颜预处理。实现第三方美颜关键就是要实现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; @Override public ITXCustomBeautyProcesser createCustomBeautyProcesser() { customBeautyProcessor = new BeatuyProcessor(); return customBeautyProcessor; } @Override public void destroyCustomBeautyProcesser() { if (null != customBeautyProcessor) { customBeautyProcessor.destory(); customBeautyProcessor = null; } }}class BeatuyProcessor implements ITXCustomBeautyProcesser { @Override public 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 { @Override protected 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 = outputTextureId return 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}}");}
31、双路编码模式
开启双路编码模式后,当前用户的编码器会同时输出【高清大画面】和【低清小画面】两路视频流(但只有一路音频流)。这样,房间中的其他用户就可以根据自身的网络情况或屏幕大小选择订阅【高清大画面】或是【低清小画面】。
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 来拉取低清小画面观看。
32、视图渲染控件
如果你业务涉及到切换显示区域的交互场景,可以使用 TRTC SDK 更新本地预览画面、更新远端用户视频渲染控件功能实现。
// 更新本地预览画面渲染控件trtcCloud.updateLocalView(viewId);// 更新远端用户视频渲染控件trtcCloud.updateRemoteView(userId, TRTCVideoStreamType.big, viewId);
注意:传参 view 为目标视频渲染控件id,streamType 仅支持 TRTCVideoStreamType.big 和 TRTCVideoStreamType.sub。
34、异常错误处理
TRTC SDK 遇到不可恢复的错误会在 onError 回调中抛出,详见 TRTC 错误码表。
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 | 麦克风正在被占用中,例如移动设备正在通话时,打开麦克风会失败。 |
35、本地和远端镜像模式设置
默认情况下,TRTC 本地和远端画面的镜像模式策略如下:
·使用前置摄像头采集:本地预览画面无镜像效果,远端观众观看有镜像效果。
·使用后置摄像头采集:本地预览画面无镜像效果,远端观众观看无镜像效果。
当然,你也可以通过 setLocalRenderParams 和 setVideoEncoderMirror 分别自定义本地预览画面镜像效果,以及视频编码输出画面的镜像效果(远端观众及云端录制的镜像模式)。如果你希望同时设置本地预览的镜像效果和远端观众观看的镜像效果,请按照如下方法进行编码。
// 设置本地画面的渲染参数TRTCRenderParams renderParams = new TRTCRenderParams();renderParams.fillMode = TRTCVideoFillMode.fill;renderParams.rotation = TRTCVideoRotation.rotation0;renderParams.mirrorType = TRTCVideoMirrorType.enable;trtcCloud.setLocalRenderParams(renderParams);
36、采集设备旋转导致观众画面旋转
如果你遇到采集设备(主播端手机)在旋转翻转时,观众端拉流观看的画面渲染方向发生变化,你可以通过关闭 SDK 重力感应来避免这种现象。
trtcCloud.setGravitySensorAdaptiveMode(TRTCGSensorMode.disable);
注意:说明:
注意:该接口只需在采集端(主播端)调用,更多关于横竖屏视频画面旋转的控制方案详见 视频画面旋转。
37、RTMP 与 TRTC 互通问题
为降低客户接入门槛,TRTC 支持 RTMP 标准协议推拉流。你可根据实际情况选择安装 OBS 、FFmpeg 或其他 RTMP 库进行推流。
本功能目前已结束免费内测,接入的 RTMP 流会作为房间中的虚拟用户产生正常的通话费用。具体接入指引详见 实时音视频 通过 RTMP 与 TRTC 互通。
RTMP 属于 TRTC 一个子模块,能与 TRTC 其他端互通,互通延迟正常情况下小于600ms,也可使用 TRTC 录制、转推等已有能力。网络架构如下所示。
38、推流地址生成
rtmp://rtmp.rtc.qq.com/push/房间号?sdkappid=应用&userid=用户名&usersig=签名
39、摄像头缩放/对焦/切换问题
电商直播场景下,主播可能会对摄像头有自定义调整的需求,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);