Android

业务流程

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



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



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




接入准备

步骤一:开通服务

秀场直播场景通常需要依赖 RTC Engine美颜 AR 两项付费 PaaS 服务构建。其中 RTC Engine 负责提供实时音视频互动能力,美颜 AR 负责提供美颜特效能力。如果您使用第三方美颜产品,可忽略美颜 AR 集成部分。
开通 RTC Engine 服务
开通美颜 AR 服务
1. 首先,您需要登录 RTC Engine 控制台 创建应用,您可根据需要选择升级 RTC Engine 应用版本,例如专业版可解锁更多增值功能服务。



说明:
建议创建两个应用分别用于测试环境和生产环境,一年内每个腾讯云账号(UIN)每月赠送10,000分钟免费时长。
RTC Engine 包月套餐分为体验版(默认)、轻量版、标准版、专业版,可解锁不同的增值功能服务,详情可见 版本功能与包月套餐说明
2. 应用创建完毕之后,您可以在应用管理-应用概览栏目看到该应用的基本信息,其中需要您保管好 SDKAppIDSDKSecretKey 便于后续使用,同时应避免密钥泄露造成流量盗刷。



1. 登录 美颜 AR 控制台 > 移动端 License,单击新建测试 License(测试版 License 免费测试有效期为14天,可续期1次,共28天)。选择移动,并根据实际需要填写 App Name、Package Name 和 Bundle ID。勾选您想试用的功能:所有美颜功能虚拟背景人脸识别手势识别礼物动画特效,然后单击确认

2. 激活后,您可以在当前页面查看您的信息,并参考上方集成指南进行集成。可在 集成指南 中查看 License Key 和 License URL 如何使用。


步骤二:导入 SDK

RTC Engine SDK 和美颜 AR SDK 已经发布到 mavenCentral 库,您可以通过配置 gradle 自动下载更新。
1. 在 dependencies 中添加合适版本 SDK 的依赖。
dependencies {
// RTC Engine 精简版 SDK, 包含 RTC Engine 和直播播放两项功能, 体积小巧
implementation 'com.tencent.liteav:LiteAVSDK_TRTC:latest.release'
// RTC Engine 全功能版 SDK, 另含直播、短视频、点播等多项功能, 体积略大
// implementation 'com.tencent.liteav:LiteAVSDK_Professional:latest.release'
// 美颜 AR SDK 例如:S1-07套餐如下
implementation 'com.tencent.mediacloud:TencentEffect_S1-07:latest.release'
}
说明:
除了推荐的自动加载方式,您还可以选择下载 SDK 并手动导入,详见 手动集成 RTC Engine SDK手动集成美颜 AR SDK
2. 在 defaultConfig 中,指定 App 使用的 CPU 架构。
defaultConfig {
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
说明:
RTC Engine SDK 支持 armeabi-v7a/arm64-v8a 架构,另外支持模拟器专用的 x86/x86_64 架构。
美颜 AR SDK 目前仅支持 armeabi-v7a/arm64-v8a 架构。
3. 单击 Sync Now,自动下载 SDK 并集成到工程里。如果您的美颜 AR 套餐包含动效和滤镜功能,那么您需要在 SDK 下载页面 下载对应的套餐包,将包内免费的滤镜素材(./assets/lut)和贴纸动效(./MotionRes)解压并放置在您工程下的如下目录:
动效:../assets/MotionRes
滤镜:../assets/lut

步骤三:工程配置

1. 权限配置。
在 AndroidManifest.xml 中配置 App 权限,秀场直播场景下 RTC Engine SDK 及美颜 AR 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",关闭硬件加速之后,会导致对方的视频流无法渲染。
RTC Engine SDK 没有内置权限申请逻辑,需要您自行声明相应的权限和特性,部分权限(如存储、录音、相机等)还需要在运行时动态申请。
若 Android 项目 targetSdkVersion 为 31 或者目标设备涉及到 Android 12 及更高系统版本,官方要求需要在代码中动态申请 android.permission.BLUETOOTH_CONNECT 权限,以正常使用蓝牙功能,具体信息请参见 蓝牙权限
2. 混淆配置。
由于我们在 SDK 内部使用了 Java 的反射特性,需要您在 proguard-rules.pro 文件中将 SDK 相关类加入不混淆名单:
-keep class com.tencent.** { *; }
-keep class org.light.** { *;}
-keep class org.libpag.** { *;}
-keep class org.extra.** { *;}
-keep class com.gyailib.**{ *;}
-keep class androidx.exifinterface.** { *;}

步骤四:鉴权与许可

RTC Engine 鉴权凭证
美颜 AR 鉴权许可
UserSig 是腾讯云设计的一种安全保护签名,目的是为了阻止恶意攻击者盗用您的云服务使用权,RTC Engine 在进房时鉴权。
调试跑通阶段:可以通过 客户端示例代码控制台获取 两种方法计算生成 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. 校验通过后,会向 RTC Engine SDK 提供实时音视频服务。



注意:
调试跑通阶段的本地 UserSig 计算方式不推荐应用到线上环境,容易被逆向破解导致密钥泄露。
我们提供了多个语言版本(Java/GO/PHP/Node.js/Python/C#/C++)的 UserSig 服务端计算源代码,详见 服务端计算 UserSig
使用美颜 AR 特效之前,需要向腾讯云校验许可凭证。设置 License 需要用到 License Key 和 License URL,示例代码如下。
import com.tencent.xmagic.telicense.TELicenseCheck;

// 如果仅仅是为了触发下载或更新 License,而不关心鉴权结果,则第4个参数传入 null
TELicenseCheck.getInstance().setTELicense(context, URL, KEY, new TELicenseCheck.TELicenseCheckListener() {
@Override
public void onLicenseCheckFinish(int errorCode, String msg) {
// 注意:此回调不一定在调用线程
if (errorCode == TELicenseCheck.ERROR_OK) {
// 鉴权成功
} else {
// 鉴权失败
}

}
});
注意:
建议在相关业务模块的初始化代码中触发鉴权许可,避免在使用前才临时去下载 License,同时鉴权时应具有网络权限。
实际应用的包名必须和创建 License 时绑定的 Package Name 完全匹配,否则会导致 License 校验失败,详情可参见 鉴权错误码

步骤五:初始化 SDK

初始化 RTC Engine SDK
初始化美颜 AR SDK
// 创建 RTC Engine SDK 实例(单例模式)
TRTCCloud mTRTCCloud = TRTCCloud.sharedInstance(context);
// 设置事件监听器
mTRTCCloud.addListener(trtcSdkListener);

// 来自 SDK 的各类事件通知(比如:错误码,警告码,音视频状态参数等)
private TRTCCloudListener trtcSdkListener = new TRTCCloudListener() {
@Override
public void onError(int errCode, String errMsg, Bundle extraInfo) {
Log.d(TAG, errCode + errMsg);
}
@Override
public void onWarning(int warningCode, String warningMsg, Bundle extraInfo) {
Log.d(TAG, warningCode + warningMsg);
}
};

// 移除事件监听器
mTRTCCloud.removeListener(trtcSdkListener);
// 销毁 RTC Engine SDK 实例(单例模式)
TRTCCloud.destroySharedInstance();
说明:
建议监听 SDK 事件通知,对一些常见错误进行日志打印和处理,详情可参见 错误码表
import com.tencent.xmagic.XmagicApi;

// 初始化美颜 SDK
XmagicApi mXmagicApi = new XmagicApi(context, XmagicResParser.getResPath(), new XmagicApi.OnXmagicPropertyErrorListener());

// 开发调试时,可以把日志级别设置为DEBUG,发布包请设置为 WARN,否则会影响性能
mXmagicApi.setXmagicLogLevel(Log.WARN);

// 释放美颜 SDK,此方法需要在 GL 线程中调用
mXmagicApi.onDestroy();
说明:
初始化美颜 AR SDK 之前,还需进行资源拷贝等准备工作,详细步骤可参见 美颜 AR SDK 使用流程

接入过程

API 时序图





步骤一:主播进房推流

RTC Engine SDK 用于承载视频画面的控件只支持传入 TXCloudVideoView 类型,因此您需要先在布局文件中定义视图渲染控件。
<com.tencent.rtmp.ui.TXCloudVideoView
android:id="@+id/live_cloud_view_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
注意:
如果您需要指定使用 TextureViewSurfaceView 作为视图渲染控件,请参见 高级功能-视图渲染控件
1. 主播进房前开启本地视频预览及音频采集。
// 获取用于展示主播本地画面预览的视频渲染控件
TXCloudVideoView mTxcvvAnchorPreviewView = findViewById(R.id.live_cloud_view_main);

// 设置视频编码参数,决定远端用户看到的画面质量
TRTCCloudDef.TRTCVideoEncParam encParam = new TRTCCloudDef.TRTCVideoEncParam();
encParam.videoResolution = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_960_540;
encParam.videoFps = 15;
encParam.videoBitrate = 1300;
encParam.videoResolutionMode = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_MODE_PORTRAIT;
mTRTCCloud.setVideoEncoderParam(encParam);

// boolean mIsFrontCamera 可指定使用前置/后置摄像头进行视频采集
mTRTCCloud.startLocalPreview(mIsFrontCamera, mTxcvvAnchorPreviewView);

// 这里可指定声音音质,从低到高分别为 SPEECH/DEFAULT/MUSIC
mTRTCCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_DEFAULT);
注意:
您可根据业务需求自行设置视频编码参数 TRTCVideoEncParam,各档位最佳分辨率和码率搭配详见 分辨率码率参照表
enterRoom 之前调用以上接口,SDK 只会开启摄像头预览和音频采集,并一直等到您调用 enterRoom 之后才开始推流。
enterRoom 之后调用以上接口,SDK 会开启摄像头预览和音频采集,并自动开始推流。
2. 主播设置本地画面的渲染参数,以及编码器输出画面模式(可选项)。
TRTCCloudDef.TRTCRenderParams params = new TRTCCloudDef.TRTCRenderParams();
params.mirrorType = TRTCCloudDef.TRTC_VIDEO_MIRROR_TYPE_AUTO; // 画面镜像模式
params.fillMode = TRTCCloudDef.TRTC_VIDEO_RENDER_MODE_FILL; // 画面填充模式
params.rotation = TRTCCloudDef.TRTC_VIDEO_ROTATION_0; // 画面旋转角度
// 设置本地画面的渲染参数
mTRTCCloud.setLocalRenderParams(params);

// 设置编码器输出的画面镜像模式
mTRTCCloud.setVideoEncoderMirror(boolean mirror);
// 设置视频编码器输出的画面方向
mTRTCCloud.setVideoEncoderRotation(int rotation);
注意:
设置本地画面渲染参数仅影响本地画面的渲染效果。
设置编码器输出模式会影响房间中其他用户所观看到(以及云端录制文件)的画面效果。
3. 主播正式开始直播,进房推流。
public void enterRoomByAnchor(String roomId, String userId) {
TRTCCloudDef.TRTCParams params = new TRTCCloudDef.TRTCParams();
// 以字符串房间号为例
params.strRoomId = roomId;
params.userId = userId;
// 从业务后台获取到的 UserSig
params.userSig = getUserSig(userId);
// 替换成您的 SDKAppID
params.sdkAppId = SDKAppID;
// 指定主播角色
params.role = TRTCCloudDef.TRTCRoleAnchor;
// 以互动直播场景进房
mTRTCCloud.enterRoom(params, TRTCCloudDef.TRTC_APP_SCENE_LIVE);
}

// 进房结果事件回调
@Override
public void onEnterRoom(long result) {
if (result > 0) {
// result 代表加入房间所消耗的时间(毫秒)
Log.d(TAG, "Enter room succeed");
} else {
// result 代表进房失败的错误码
Log.d(TAG, "Enter room failed");
}
}
注意:
RTC Engine 房间号分为整型 roomId 和字符串类型 strRoomId,两种类型的房间不互通,建议统一房间号类型。
RTC Engine 用户角色分为主播和听众,只有主播才有推流权限,进房时需指定用户角色,如未指定则默认为主播角色。
秀场直播场景下,进房模式建议选用 TRTC_APP_SCENE_LIVE

步骤二:观众进房拉流

1. 观众进入 RTC Engine 房间。
public void enterRoomByAudience(String roomId, String userId) {
TRTCCloudDef.TRTCParams params = new TRTCCloudDef.TRTCParams();
// 以字符串房间号为例
params.strRoomId = roomId;
params.userId = userId;
// 从业务后台获取到的 UserSig
params.userSig = getUserSig(userId);
// 替换成您的 SDKAppID
params.sdkAppId = SDKAppID;
// 指定观众角色
params.role = TRTCCloudDef.TRTCRoleAudience;
// 以互动直播场景进房
mTRTCCloud.enterRoom(params, TRTCCloudDef.TRTC_APP_SCENE_LIVE);
}

// 进房结果事件回调
@Override
public void onEnterRoom(long result) {
if (result > 0) {
// result 代表加入房间所消耗的时间(毫秒)
Log.d(TAG, "Enter room succeed");
} else {
// result 代表进房失败的错误码
Log.d(TAG, "Enter room failed");
}
}
2. 观众订阅主播音视频流。
@Override
public void onUserAudioAvailable(String userId, boolean available) {
// 某远端用户发布/取消了自己的音频
// 在自动订阅模式下,您无需做任何操作,SDK 会自动播放远端用户音频
}

@Override
public void onUserVideoAvailable(String userId, boolean available) {
// 某远端用户发布/取消了主路视频画面
if (available) {
// 订阅远端用户的视频流,并绑定视频渲染控件
mTRTCCloud.startRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG, TXCloudVideoView view);
} else {
// 停止订阅远端用户的视频流,并释放渲染控件
mTRTCCloud.stopRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG);
}
}
3. 观众设置远端画面的渲染模式(可选项)。
TRTCCloudDef.TRTCRenderParams params = new TRTCCloudDef.TRTCRenderParams();
params.mirrorType = TRTCCloudDef.TRTC_VIDEO_MIRROR_TYPE_AUTO; // 画面镜像模式
params.fillMode = TRTCCloudDef.TRTC_VIDEO_RENDER_MODE_FILL; // 画面填充模式
params.rotation = TRTCCloudDef.TRTC_VIDEO_ROTATION_0; // 画面旋转角度
// 设置远端画面的渲染模式
mTRTCCloud.setRemoteRenderParams(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG, params)

步骤三:观众连麦互动

1. 观众切换为主播角色。
// 切换为主播角色
mTRTCCloud.switchRole(TRTCCloudDef.TRTCRoleAnchor);

// 切换角色事件回调
@Override
public void onSwitchRole(int errCode, String errMsg) {
if (errCode == TXLiteAVCode.ERR_NULL) {
// 切换角色成功
}
}
2. 观众开始本地音视频采集和推流。
// 获取用于展示连麦观众本地画面预览的视频渲染控件
TXCloudVideoView mTxcvvAudiencePreviewView = findViewById(R.id.live_cloud_view_sub);

// 设置视频编码参数,决定远端用户看到的画面质量
TRTCCloudDef.TRTCVideoEncParam encParam = new TRTCCloudDef.TRTCVideoEncParam();
encParam.videoResolution = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_480_270;
encParam.videoFps = 15;
encParam.videoBitrate = 550;
encParam.videoResolutionMode = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_MODE_PORTRAIT;
mTRTCCloud.setVideoEncoderParam(encParam);

// boolean mIsFrontCamera 可指定使用前置/后置摄像头进行视频采集
mTRTCCloud.startLocalPreview(mIsFrontCamera, mTxcvvAudiencePreviewView);

// 这里可指定声音音质,从低到高分别为 SPEECH/DEFAULT/MUSIC
mTRTCCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_DEFAULT);
注意:
您可根据业务需求自行设置视频编码参数 TRTCVideoEncParam,各档位最佳分辨率和码率搭配详见 分辨率码率参照表
3. 观众下麦停止推流。
// 切换为观众角色
mTRTCCloud.switchRole(TRTCCloudDef.TRTCRoleAudience);

// 切换角色事件回调
@Override
public void onSwitchRole(int errCode, String errMsg) {
if (errCode == TXLiteAVCode.ERR_NULL) {
// 停止摄像头采集推流
mTRTCCloud.stopLocalPreview();
// 停止麦克风采集推流
mTRTCCloud.stopLocalAudio();
}
}

步骤四:退出与解散房间

1. 退出房间。
public void exitRoom() {
mTRTCCloud.stopLocalAudio();
mTRTCCloud.stopLocalPreview();
mTRTCCloud.exitRoom();
}

// 离开房间事件回调
@Override
public void onExitRoom(int reason) {
if (reason == 0) {
Log.d(TAG, "主动调用 exitRoom 退出房间");
} else if (reason == 1) {
Log.d(TAG, "被服务器踢出当前房间");
} else if (reason == 2) {
Log.d(TAG, "当前房间整个被解散");
}
}
注意:
待 SDK 占用的所有资源释放完毕后,SDK 会抛出 onExitRoom 回调通知到您。
如果您要再次调用 enterRoom 或者切换到其他的音视频 SDK,请等待 onExitRoom 回调到来后再执行相关操作。否则可能会遇到例如摄像头、麦克风设备被强占等各种异常问题。
2. 解散房间
服务端解散:RTC Engine 提供了服务端解散房间 API DismissRoom(区分数字房间 ID 和字符串房间 ID),您可以调用此接口把房间所有用户从房间移出,并解散房间。
客户端解散:通过各个客户端的退出房间 exitRoom 接口,将房间内的所有主播和听众完成退房,退房后,根据 RTC Engine 房间生命周期规则,房间将会自动解散,详情请参见 退出房间
注意:
建议您当一次直播结束之后,可以在服务端调用解散房间 API 确保房间解散,防止观众意外进房导致产生非期望的费用。

高级功能

主播跨房 PK 连麦

1. 任意一方发起跨房 PK 连麦。
public void connectOtherRoom(String roomId, String userId) {
try {
JSONObject jsonObj = new JSONObject();
// 数字房间号为 roomId
jsonObj.put("strRoomId", roomId);
jsonObj.put("userId", userId);
mTRTCCloud.ConnectOtherRoom(jsonObj.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}

// 请求跨房连麦的结果回调
@Override
public void onConnectOtherRoom(String userId, int errCode, String errMsg) {
// 要跨房通话的另一个房间中的主播的用户 ID
// 错误码,ERR_NULL 代表请求成功
// 错误信息
}
注意:
跨房 PK 连麦的本地用户和对端用户必须都为主播角色,且必须都有音频或视频上行。
2. 两个房间中的所有用户都会收到来自另一个房间中的 PK 主播的音视频流可用回调。
@Override
public void onUserAudioAvailable(String userId, boolean available) {
// 某远端用户发布/取消了自己的音频
// 在自动订阅模式下,您无需做任何操作,SDK 会自动播放远端用户音频
}

@Override
public void onUserVideoAvailable(String userId, boolean available) {
// 某远端用户发布/取消了主路视频画面
if (available) {
// 订阅远端用户的视频流,并绑定视频渲染控件
mTRTCCloud.startRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG, TXCloudVideoView view);
} else {
// 停止订阅远端用户的视频流,并释放渲染控件
mTRTCCloud.stopRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG);
}
}
3. 任意一方退出跨房 PK 连麦。
// 退出跨房连麦
mTRTCCloud.DisconnectOtherRoom();

// 退出跨房连麦的结果回调
@Override
public void onDisConnectOtherRoom(int errCode, String errMsg) {
super.onDisConnectOtherRoom(errCode, errMsg);
}
注意:
调用 DisconnectOtherRoom() 后,即退出与所有其他房间主播的跨房 PK 连麦。
跨房 PK 连麦的发起端和接收端任意一端均可调用 DisconnectOtherRoom() 退出跨房 PK 连麦。

第三方美颜接入

RTC Engine 支持接入第三方美颜特效产品,下面以美颜 AR 为例,展示第三方美颜接入流程。
1. 集成美颜 AR SDK、申请授权 License,详情请参见 接入准备 步骤实现。
2. 资源拷贝(如有)。如果您的资源文件是内置在 assets 目录的,那么使用前需要 copy 到 App 的私有目录。
XmagicResParser.setResPath(new File(getFilesDir(), "xmagic").getAbsolutePath());
//loading

//copy资源文件到私有目录,只需要做一次
XmagicResParser.copyRes(getApplicationContext());
如果您的资源文件是从 网络动态下载 的,下载成功后,需要设置资源文件路径。
XmagicResParser.setResPath(下载的资源文件本地路径);
3. 设置第三方美颜的视频数据回调,将美颜 SDK 处理每帧数据结果传入 RTC Engine SDK 内部做渲染处理。
mTRTCCloud.setLocalVideoProcessListener(TRTCCloudDef.TRTC_VIDEO_PIXEL_FORMAT_Texture_2D, TRTCCloudDef.TRTC_VIDEO_BUFFER_TYPE_TEXTURE, new TRTCCloudListener.TRTCVideoFrameListener() {
@Override
public void onGLContextCreated() {
// SDK 内部 OpenGL 环境已经创建,此时可进行第三方美颜的初始化工作
if (mXmagicApi == null) {
XmagicApi mXmagicApi = new XmagicApi(context, XmagicResParser.getResPath(), new XmagicApi.OnXmagicPropertyErrorListener());
} else {
mXmagicApi.onResume();
}
}

@Override
public int onProcessVideoFrame(TRTCCloudDef.TRTCVideoFrame srcFrame, TRTCCloudDef.TRTCVideoFrame dstFrame) {
// 用于对接第三方美颜组件的视频处理回调
if (mXmagicApi != null) {
dstFrame.texture.textureId = mXmagicApi.process(srcFrame.texture.textureId, srcFrame.width, srcFrame.height);
}
return 0;
}

@Override
public void onGLContextDestory() {
// SDK 内部 OpenGL 环境被销毁,此时可进行第三方美颜的资源销毁工作
mXmagicApi.onDestroy();
}
});
注意:
步骤1、步骤2根据不同的第三方美颜产品实现方式有所不同,而步骤3是 RTC Engine 集成第三方美颜的通用且重要步骤

双路编码模式

开启双路编码模式后,当前用户的编码器会同时输出【高清大画面】和【低清小画面】两路视频流(但只有一路音频流)。这样,房间中的其他用户就可以根据自身的网络情况或屏幕大小选择订阅【高清大画面】或是【低清小画面】。
1. 开启大小画面双路编码模式。
public void enableDualStreamMode(boolean enable) {
// 小流的视频编码参数(可自定义)
TRTCCloudDef.TRTCVideoEncParam smallVideoEncParam = new TRTCCloudDef.TRTCVideoEncParam();
smallVideoEncParam.videoResolution = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_480_270;
smallVideoEncParam.videoFps = 15;
smallVideoEncParam.videoBitrate = 550;
smallVideoEncParam.videoResolutionMode = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_MODE_PORTRAIT;
mTRTCCloud.enableEncSmallVideoStream(enable, smallVideoEncParam);
}
注意:
双路编码开启后,会消耗更多的 CPU 和 网络带宽,所以 Mac、Windows 或者高性能 Pad 可以考虑开启,不建议手机端开启。
2. 选择拉取远端用户视频流类型。
// 订阅远端用户视频流时可选视频流类型
mTRTCCloud.startRemoteView(userId, streamType, videoView);

// 亦可随时切换指定远端用户的大小画面
mTRTCCloud.setRemoteVideoStreamType(userId, streamType);
注意:
双路编码开启后,可通过指定 streamType 视频流类型为 TRTC_VIDEO_STREAM_TYPE_SMALL 来拉取低清小画面观看。

视图渲染控件

RTC Engine 中有很多需要操控视频画面的接口,这些接口都需要您指定视频渲染控件。在 Android 平台中,使用 TXCloudVideoView 作为视频渲染控件,支持 SurfaceViewTextureView 两种渲染方案。下面介绍指定渲染控件类型,以及更新视频渲染控件的方法。
1. 如果您希望强制使用某一种方案,或者将本地视频渲染控件转换为 TXCloudVideoView,您可以按照如下方法进行编码。
// 强制使用 TextureView
TextureView textureView = findViewById(R.id.texture_view);
TXCloudVideoView cloudVideoView = new TXCloudVideoView(context);
cloudVideoView.addVideoView(textureView);

// 强制使用 SurfaceView
SurfaceView surfaceView = findViewById(R.id.surface_view);
TXCloudVideoView cloudVideoView = new TXCloudVideoView(surfaceView);
2. 如果您业务涉及到切换显示区域的交互场景,可以使用 RTC Engine SDK 更新本地预览画面、更新远端用户视频渲染控件功能实现。
// 更新本地预览画面渲染控件
mTRTCCloud.updateLocalView(videoView);

// 更新远端用户视频渲染控件
mTRTCCloud.updateRemoteView(userId, streamType, videoView);
注意:
传参 videoView 为目标视频渲染控件,streamType 仅支持 TRTC_VIDEO_STREAM_TYPE_BIGTRTC_VIDEO_STREAM_TYPE_SUB

直播互动消息

直播互动在直播场景中尤为重要,用户通过 点赞消息礼物消息弹幕消息 等方式与主播进行互动。实现直播互动功能的前提是开通 Chat 服务,并导入 Chat SDK,详细指引请参见 语聊房接入指引-接入准备

点赞消息

1. 点赞者在客户端发送点赞相关的群组自定义消息,发送成功后业务方在本地渲染点赞效果。
// 构造点赞消息体
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("cmd", "like_msg");
JSONObject msgJsonObject = new JSONObject();
msgJsonObject.put("type", 1); // 点赞类型
msgJsonObject.put("likeCount", 10); // 点赞数量
jsonObject.put("msg", msgJsonObject);
} catch (JSONException e) {
e.printStackTrace();
}
String data = jsonObject.toString();

// 发送群自定义消息(点赞消息建议设置为低优先级)
V2TIMManager.getInstance().sendGroupCustomMessage(data.getBytes(), mRoomId,
V2TIMMessage.V2TIM_PRIORITY_LOW, new V2TIMValueCallback<V2TIMMessage>() {
@Override
public void onError(int i, String s) {
// 发送点赞消息失败
}

@Override
public void onSuccess(V2TIMMessage v2TIMMessage) {
// 发送点赞消息成功
// 本地渲染点赞效果
}
});
2. 房间内其他用户客户端收到群自定义消息回调,然后进行消息解析和点赞效果渲染。
// 收到群自定义消息
V2TIMManager.getInstance().addSimpleMsgListener(new V2TIMSimpleMsgListener() {
@Override
public void onRecvGroupCustomMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, byte[] customData) {
String customStr = new String(customData);
if (!customStr.isEmpty()) {
try {
JSONObject jsonObject = new JSONObject(customStr);
String command = jsonObject.getString("cmd");
JSONObject messageJsonObject = jsonObject.getJSONObject("msg");
if (command.equals("like_msg")) {
int type = messageJsonObject.getInt("type"); // 点赞类型
int likeCount = messageJsonObject.getInt("likeCount"); // 点赞数量
// 根据点赞类型和数量渲染点赞效果
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
});

礼物消息

1. 送礼者向业务服务端发起请求,业务服务端完成计费结算后调用 REST API 向群组中发送自定义消息。
1.1 请求 URL 示例。
https://xxxxxx/v4/group_open_http_svc/send_group_msg?sdkappid=88888888&identifier=admin&usersig=xxx&random=99999999&contenttype=json
1.2 请求包体示例。
{
"GroupId": "@TGS#12DEVUDHQ",
"Random": 2784275388,
"MsgPriority": "High", // 消息的优先级,礼物消息应设置为高优先级
"MsgBody": [
{
"MsgType": "TIMCustomElem",
"MsgContent": {
// type: 礼物类型; giftUrl: 礼物资源地址; giftName: 礼物名称; giftCount: 礼物数量
"Data": "{\"cmd\": \"gift_msg\", \"msg\": {\"type\": 1, \"giftUrl\": \"xxx\", \"giftName\": \"xxx\", \"giftCount\": 1}}"
}
}
]
}
2. 房间内其他用户客户端收到群自定义消息回调,然后进行消息解析和礼物特效渲染。
// 收到群自定义消息
V2TIMManager.getInstance().addSimpleMsgListener(new V2TIMSimpleMsgListener() {
@Override
public void onRecvGroupCustomMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, byte[] customData) {
String customStr = new String(customData);
if (!customStr.isEmpty()) {
try {
JSONObject jsonObject = new JSONObject(customStr);
String command = jsonObject.getString("cmd");
JSONObject messageJsonObject = jsonObject.getJSONObject("msg");
if (command.equals("gift_msg")) {
int type = messageJsonObject.getInt("type"); // 礼物类型
int giftCount = messageJsonObject.getInt("giftCount"); // 礼物数量
String giftUrl = messageJsonObject.getString("giftUrl"); // 礼物资源地址
String giftName = messageJsonObject.getString("giftName"); // 礼物名称
// 根据礼物类型、礼物数量、礼物资源地址、礼物名称渲染礼物特效
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
});

弹幕消息

秀场直播间通常会有文本形式的弹幕消息互动,这里可以通过 Chat 的发送及接收群聊普通文本消息来实现。
// 发送公屏弹幕消息
V2TIMManager.getInstance().sendGroupTextMessage(text, groupID, V2TIMMessage.V2TIM_PRIORITY_NORMAL, new V2TIMValueCallback<V2TIMMessage>() {
@Override
public void onError(int i, String s) {
// 发送弹幕消息失败
}

@Override
public void onSuccess(V2TIMMessage v2TIMMessage) {
// 发送弹幕消息成功
// 本地展示消息文本
}
});

// 接收公屏弹幕消息
V2TIMManager.getInstance().addSimpleMsgListener(new V2TIMSimpleMsgListener() {
@Override
public void onRecvGroupTextMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, String text) {
// 根据发送者信息 sender 和消息文本 text 渲染弹幕消息
}
});
注意:
建议将礼物消息设置为高优先级;弹幕消息设置为正常优先级;点赞消息设置为低优先级。
本人在客户端发送群聊消息,不会触发消息接收回调,只有群组内的其他用户可以接收到。

异常处理

异常错误处理

RTC Engine SDK 遇到不可恢复的错误会在 onError 回调中抛出,详见 错误码表
1. UserSig 相关。UserSig 校验失败会导致进房失败,您可使用 UserSig 工具 进行校验。
枚举
取值
描述
ERR_TRTC_INVALID_USER_SIG
-3320
进房参数 UserSig 不正确,请检查 TRTCParams.userSig 是否为空。
ERR_TRTC_USER_SIG_CHECK_FAILED
-100018
UserSig 校验失败,请检查参数 TRTCParams.userSig 是否填写正确或已经过期。
2. 进退房相关。进房失败请先检查进房参数是否正确,且进退房接口必须成对调用,即便进房失败也需要调用退房接口。
枚举
取值
描述
ERR_TRTC_CONNECT_SERVER_TIMEOUT
-3308
请求进房超时,请检查是否断网或者是否开启 VPN,您也可以切换4G进行测试。
ERR_TRTC_INVALID_SDK_APPID
-3317
进房参数 sdkAppId 错误,请检查 TRTCParams.sdkAppId 是否为空
ERR_TRTC_INVALID_ROOM_ID
-3318
进房参数 roomId 错误,请检查 TRTCParams.roomIdTRTCParams.strRoomId 是否为空,注意 roomId 和 strRoomId 不可混用。
ERR_TRTC_INVALID_USER_ID
-3319
进房参数 userId 不正确,请检查 TRTCParams.userId 是否为空。
ERR_TRTC_ENTER_ROOM_REFUSED
-3340
进房请求被拒绝,请检查是否连续调用 enterRoom 进入相同 ID 的房间。
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
麦克风正在被占用中,例如移动设备正在通话时,打开麦克风会失败。

远端镜像模式无效问题

RTC Engine 设置画面镜像分为本地预览镜像 setLocalRenderParams 和视频编码镜像 setVideoEncoderMirror,两者分别影响本地预览画面镜像效果,以及视频编码输出画面的镜像效果(远端观众及云端录制的镜像模式)。如果希望本地预览的镜像效果同时在远端观众侧生效,请按照如下方法进行编码。
// 设置本地画面的渲染参数
TRTCCloudDef.TRTCRenderParams params = new TRTCCloudDef.TRTCRenderParams();
params.mirrorType = TRTCCloudDef.TRTC_VIDEO_MIRROR_TYPE_ENABLE; // 画面镜像模式
params.fillMode = TRTCCloudDef.TRTC_VIDEO_RENDER_MODE_FILL; // 画面填充模式
params.rotation = TRTCCloudDef.TRTC_VIDEO_ROTATION_0; // 画面旋转角度
mTRTCCloud.setLocalRenderParams(params);

// 设置编码器输出的画面镜像模式
mTRTCCloud.setVideoEncoderMirror(true);

摄像头缩放/对焦/切换问题

秀场直播场景下,主播可能会对摄像头有自定义调整的需求,RTC Engine SDK 设备管理类下也有解决此类需求的相关接口。
1. 查询、设置摄像头的缩放倍数。
// 获取摄像头的最大缩放倍数(仅适用于移动端)
float zoomRatio = mTRTCCloud.getDeviceManager().getCameraZoomMaxRatio();
// 设置摄像头的缩放倍数(仅适用于移动端)
// 取值范围1-5,取值为1表示最远视角(正常镜头),取值为5表示最近视角(放大镜头);最大值推荐为5,若超过5,视频数据会变得模糊不清
mTRTCCloud.getDeviceManager().setCameraZoomRatio(zoomRatio);
2. 设置摄像头的对焦功能及位置。
// 开启或关闭摄像头的自动对焦功能(仅适用于移动端)
mTRTCCloud.getDeviceManager().enableCameraAutoFocus(false);
// 设置摄像头的对焦位置(仅适用于移动端)
// 使用该接口的前提是先通过 enableCameraAutoFocus 关闭自动对焦功能
mTRTCCloud.getDeviceManager().setCameraFocusPosition(int x, int y);
3. 判断、切换前置或后置摄像头。
// 判断当前是否为前置摄像头(仅适用于移动端)
boolean isFrontCamera = mTRTCCloud.getDeviceManager().isFrontCamera();
// 切换前置或后置摄像头(仅适用于移动端)
// 传入true: 切换为前置;传入false: 切换为后置
mTRTCCloud.getDeviceManager().switchCamera(!isFrontCamera);