tencent cloud

云顾问-Tencent RTC 云助手

产品动态
产品简介
产品概述
产品优势
应用场景
购买指南
新手指引
场景化方案
场景化方案概述
社交娱乐
电商直播
音视频通话
远程实时操控
智能客服
AI 面试
模块化方案
模块化方案概述
网络质量监控
移动端应用保活方案
视频画中画方案
直播上下滑
跨房 PK 连麦方案
AI 对话 Chat 信令方案
常见问题
联系我们

Android

PDF
聚焦模式
字号
最后更新时间: 2026-02-06 09:27:46

业务流程

本节汇总了 1V1 音视频通话中一些常见的业务流程,帮助您更好地理解整个场景的实现过程。
语音通话流程
视频通话流程
下图展示了 1V1 语音通话的序列图,其中包含呼叫、接听、通话、挂断等流程。



下图展示了 1V1 视频通话的序列图,其中包含呼叫、接听、通话、挂断等流程。




接入准备

步骤一:开通服务

1V1 音视频通话场景通常需要依赖腾讯云 即时通信 IM实时音视频 TRTC 两项付费 PaaS 服务构建。
1. 首先您需要登录 实时音视频 TRTC 控制台 创建应用,此时在 即时通信 IM 控制台 会同步自动创建一个与当前 TRTC 应用相同 SDKAppID 的 IM 体验版应用,二者账号与鉴权体系可复用。后续您可根据需要选择升级 TRTC 或 IM 应用版本,例如高级版本可解锁更多增值功能服务。



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




步骤二:导入 SDK

TRTC SDK 和 IM SDK 已经发布到 mavenCentral 库,您可以通过配置 gradle 自动下载更新。
1. 在 dependencies 中添加合适版本 SDK 的依赖。
dependencies {
// TRTC 精简版 SDK, 包含 TRTC 和直播播放两项功能
implementation 'com.tencent.liteav:LiteAVSDK_TRTC:latest.release'
// 添加 IM SDK,推荐填写最新的版本号
implementation 'com.tencent.imsdk:imsdk-plus:Version number'

// 如果您需要添加 Quic 插件,请取消下一行的注释(注意:要求插件版本号和 IM SDK 版本号相同)
// implementation 'com.tencent.imsdk:timquic-plugin:Version number'
}
说明:
除了推荐的自动加载方式,您还可以选择下载 SDK 并手动导入,详见 手动集成 TRTC SDK手动集成 IM SDK
Quic 插件提供 axp-quic 多路传输协议,弱网抗性更优,网络丢包率达到 70% 的条件下,仍然可以提供服务。仅对旗舰版用户开放,对于非旗舰版,请 购买旗舰版套餐包 后可使用,详见 价格说明。为确保功能正常使用,请将终端 SDK 更新至 7.7.5282 及其以上的版本
2. 在 defaultConfig 中,指定 App 使用的 CPU 架构。
defaultConfig {
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
说明:
TRTC SDK 支持 armeabi/armeabi-v7a/arm64-v8a 架构,另外支持模拟器专用的 x86/x86_64 架构。
IM SDK 支持 armeabi-v7a/arm64-v8a/x86/x86_64 架构,为了缩减安装包体积,您可以选择只打包部分架构的 SO 文件。

步骤三:工程配置

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

步骤四:鉴权凭证

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



注意:
调试跑通阶段的本地 UserSig 计算方式不推荐应用到线上环境,容易被逆向破解导致密钥泄露。
我们提供了多个语言版本(Java/Go/PHP/Node.js/Python/C#/C++)的 UserSig 服务端计算源代码,详见 服务端计算 UserSig

步骤五:初始化 SDK

1. IM SDK 初始化与添加事件监听器
// 添加事件监听器
V2TIMManager.getInstance().addIMSDKListener(imSdkListener);
// 初始化 IM SDK,调用这个接口后,可以立即调用登录接口
V2TIMManager.getInstance().initSDK(context, sdkAppID, null);

// SDK 初始化后会抛出一些事件,例如连接状态、登录票据过期等
private V2TIMSDKListener imSdkListener = new V2TIMSDKListener() {
@Override
public void onConnecting() {
Log.d(TAG, "IM SDK 正在连接到腾讯云服务器");
}

@Override
public void onConnectSuccess() {
Log.d(TAG, "IM SDK 已经成功连接到腾讯云服务器");
}
};

// 移除事件监听器
V2TIMManager.getInstance().removeIMSDKListener(imSdkListener);
// 反初始化 IM SDK
V2TIMManager.getInstance().unInitSDK();
说明:
如果您的应用生命周期跟 SDK 生命周期一致,退出应用前可以不进行反初始化。若您只在进入特定界面后才初始化 SDK,退出界面后不再使用,可以对 SDK 进行反初始化。
2. TRTC SDK 创建实例与设置事件监听器
// 创建 TRTC SDK 实例(单例模式)
TRTCCloud mTRTCCloud = TRTCCloud.sharedInstance(context);
// 添加 TRTC 事件监听器
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);
}
};

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

接入过程

步骤一:登录

初始化 IM SDK 后,您需要调用 SDK 登录接口验证账号身份,获得账号的功能使用权限。因此在使用其他功能之前,请务必确保登录成功,否则可能导致功能异常或不可用。如您仅需使用 TRTC 音视频服务,可忽略此步骤。

时序图





登录操作

// 登录:userID 可自定义,userSig 参考步骤一生成获取
V2TIMManager.getInstance().login(userID, userSig, new V2TIMCallback() {
@Override
public void onSuccess() {
Log.i("imsdk", "success");
}
@Override
public void onError(int code, String desc) {
// 如果返回以下错误码,表示使用 UserSig 已过期,请您使用新签发的 UserSig 进行再次登录。
// 1. ERR_USER_SIG_EXPIRED(6206)
// 2. ERR_SVR_ACCOUNT_USERSIG_EXPIRED(70001)
// 注意:其他的错误码,请不要在这里调用登录接口,避免 IM SDK 登录进入死循环。
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});

登出操作

// 登出
V2TIMManager.getInstance().logout(new V2TIMCallback() {
@Override
public void onSuccess() {
Log.i("imsdk", "success");
}
@Override
public void onError(int code, String desc) {
Log.i("imsdk", "failure, code:" + code + ", desc:" + desc);
}
});
说明:
如果您的应用生命周期跟 IM SDK 生命周期一致,退出应用前可以不登出。若您只在进入特定界面后才使用 IM SDK,退出界面后不再使用,可以进行登出操作和对 IM SDK 进行反初始化。

步骤二:呼叫

时序图





发起呼叫

1. 主叫端本地画面预览(仅视频通话,语音通话忽略此步骤)
// 设置视频编码参数,决定远端用户看到的画面质量
TRTCCloudDef.TRTCVideoEncParam encParam = new TRTCCloudDef.TRTCVideoEncParam();
encParam.videoResolution = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_960_540;
encParam.videoFps = 15;
encParam.videoBitrate = 850;
encParam.videoResolutionMode = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_MODE_PORTRAIT;
mTRTCCloud.setVideoEncoderParam(encParam);

// 开启本地摄像头预览(可指定使用前置/后置摄像头进行视频采集)
mTRTCCloud.startLocalPreview(isFrontCamera, previewView);
注意:
您可根据业务需求自行设置视频编码参数 TRTCVideoEncParam,各档位最佳分辨率和码率搭配详见 分辨率码率参照表
enterRoom 之前调用以上接口,SDK 只会开启摄像头预览,并一直等到您调用 enterRoom 之后才开始本地视频推流。
2. 主叫端发送呼叫邀请信令
// 构造自定义数据
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("cmd", "av_call");
JSONObject msgJsonObject = new JSONObject();
msgJsonObject.put("callType", "videoCall"); // 指定通话类型(视频通话、语音通话)
msgJsonObject.put("roomId", generateRoomId()); // 指定 TRTC 房间号(主叫端可随机生成)
jsonObject.put("msg", msgJsonObject);
} catch (JSONException e) {
e.printStackTrace();
}
String data = jsonObject.toString();

// 发送呼叫邀请信令
V2TIMManager.getSignalingManager().invite(receiver, data, false, v2TIMOfflinePushInfo, timeout, new V2TIMCallback() {
@Override
public void onError(int code, String desc) {
// 发送呼叫邀请信令失败
// 提示呼叫失败,可以尝试重试
}

@Override
public void onSuccess() {
// 发送呼叫邀请信令成功
// 渲染呼叫页面,播放呼叫铃声
}
});
注意:
音视频通话场景中,通常需要在邀请信令中配置离线推送信息 v2TIMOfflinePushInfo,详情参见 离线推送消息
建议在邀请信令中设置合理的超时时间参数 timeout,单位为秒,SDK 会进行超时检测,从而实现呼叫超时自动挂断。
3. 被叫端收到呼叫邀请通知
// 被叫用户收到呼叫请求, inviteID 为该条请求 ID,inviter 为主叫用户 ID
V2TIMManager.getSignalingManager().addSignalingListener(new V2TIMSignalingListener() {
@Override
public void onReceiveNewInvitation(String inviteID, String inviter,
String groupId, List<String> inviteeList, String data) {
if (!data.isEmpty()) {
try {
JSONObject jsonObject = new JSONObject(data);
String command = jsonObject.getString("cmd");
JSONObject messageJsonObject = jsonObject.getJSONObject("msg");
if (command.equals("av_call")) {
String callType = messageJsonObject.getString("callType");
String roomId = messageJsonObject.getString("roomId");
// 渲染呼叫页面,播放呼叫铃声
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
});
注意:
主叫端发起呼叫请求、被叫端收到呼叫请求时,业务侧需要自行实现呼叫页面的渲染,以及呼叫铃声的播放。
4. 被叫端本地画面预览(仅视频通话,语音通话忽略此步骤)
if (callType.equals("videoCall")) {
// 设置视频编码参数,决定远端用户看到的画面质量
TRTCCloudDef.TRTCVideoEncParam encParam = new TRTCCloudDef.TRTCVideoEncParam();
encParam.videoResolution = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_960_540;
encParam.videoFps = 15;
encParam.videoBitrate = 850;
encParam.videoResolutionMode = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_MODE_PORTRAIT;
mTRTCCloud.setVideoEncoderParam(encParam);

// 开启本地摄像头预览(可指定使用前置/后置摄像头进行视频采集)
mTRTCCloud.startLocalPreview(isFrontCamera, previewView);
}

取消呼叫

1. 主叫端取消呼叫请求
V2TIMManager.getSignalingManager().cancel(inviteId, data, new V2TIMCallback() { @Override public void onError(int code, String desc) { // 取消呼叫请求失败
// 提示取消失败,可以尝试重试 } @Override public void onSuccess() { // 取消呼叫请求成功
// 销毁呼叫页面,停止播放呼叫铃声 } });
2. 被叫端收到取消通知
@Override public void onInvitationCancelled(String inviteID, String inviter, String data) {
// 销毁呼叫页面,停止播放呼叫铃声 }

呼叫超时

主叫端和被叫端均会收到超时通知,同时销毁呼叫页面并停止播放呼叫铃声。
@Override
public void onInvitationTimeout(String inviteID, List<String> inviteeList) {
// 提示呼叫超时,销毁呼叫页面,停止播放呼叫铃声
}

步骤三:接听

接听信令

1. 被叫端发送同意接听信令
V2TIMManager.getSignalingManager().accept(inviteId, data, new V2TIMCallback() { @Override public void onError(int code, String desc) { // 接听失败,提示异常或重试 } @Override public void onSuccess() { // 接听成功,渲染通话页面,停止播放呼叫铃声
if (callType.equals("videoCall")) {
// 开始视频通话
startVideoCall();
} else {
// 开始语音通话
startAudioCall();
} } });
2. 主叫端收到同意接听通知
@Override
public void onInviteeAccepted(String inviteID, String invitee, String data) {
if (callType.equals("videoCall")) {
// 开始视频通话
startVideoCall();
} else {
// 开始语音通话
startAudioCall();
}
}

语音通话

1. 主叫端和被叫端均进入同一个 TRTC 房间,开始语音通话。
private void startAudioCall() {
TRTCCloudDef.TRTCParams params = new TRTCCloudDef.TRTCParams(); params.sdkAppId = SDKAPPID; // TRTC应用标识, 在控制台获取 params.userSig = USERSIG; // TRTC鉴权凭证, 在服务端生成
params.strRoomId = roomId; // 房间号, 以字符串房间号为例 params.userId = userId; // 用户名, 建议和IM保持同步
mTRTCCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_SPEECH);
mTRTCCloud.enterRoom(params, TRTCCloudDef.TRTC_APP_SCENE_AUDIOCALL);
}
注意:
语音通话模式下,TRTC 进房场景需选用 TRTC_APP_SCENE_AUDIOCALL,同时不要指定进房角色 TRTCRoleType
开始音频采集 startLocalAudio 可同时设置音质参数,语音通话模式建议设置 TRTC_AUDIO_QUALITY_SPEECH
SDK 默认的自动订阅模式下,用户进入房间后,会立刻接收到该房间中的音频流,音频会自动解码播放,无需手动拉流。
2. 进房结果通知,标识通话状态。
// 标记是否正在通话中
boolean isOnCalling = false;

// 进房结果事件回调
@Override
public void onEnterRoom(long result) {
if (result > 0) {
// 进房成功,标识正在通话中
isOnCalling = true;
} else {
// 进房失败,提示通话异常
isOnCalling = false;
}
}

视频通话

1. 主叫端和被叫端均进入同一个 TRTC 房间,开始视频通话。
private void startVideoCall() {
TRTCCloudDef.TRTCParams params = new TRTCCloudDef.TRTCParams();
params.sdkAppId = SDKAPPID; // TRTC应用标识, 在控制台获取
params.userSig = USERSIG; // TRTC鉴权凭证, 在服务端生成
params.strRoomId = roomId; // 房间号, 以字符串房间号为例
params.userId = userId; // 用户名, 建议和IM保持同步
mTRTCCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_SPEECH);
mTRTCCloud.enterRoom(params, TRTCCloudDef.TRTC_APP_SCENE_VIDEOCALL);
}
注意:
视频通话模式下,TRTC 进房场景需选用 TRTC_APP_SCENE_VIDEOCALL,同时不要指定进房角色 TRTCRoleType
开始音频采集 startLocalAudio 可同时设置音质参数,视频通话模式建议设置 TRTC_AUDIO_QUALITY_SPEECH
SDK 默认的自动订阅模式下,音频会自动解码播放,视频需要手动调用 startRemoteView 拉取远端视频流渲染播放。
2. 进房结果通知,标识通话状态,拉取远端视频流。
// 标记是否正在通话中
boolean isOnCalling = false;

// 进房结果事件回调
@Override
public void onEnterRoom(long result) {
if (result > 0) {
// 进房成功,标识正在通话中
isOnCalling = true;
} else {
// 进房失败,提示通话异常
isOnCalling = false;
}
}

// 拉取远端视频流
@Override
public void onUserVideoAvailable(String userId, boolean available) {
// 远端用户发布/取消了主路视频画面
if (available) {
// 订阅远端用户的视频流,并绑定视频渲染控件
mTRTCCloud.startRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG, view);
} else {
// 停止订阅远端用户的视频流,并释放渲染控件
mTRTCCloud.stopRemoteView(userId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG);
}
}

步骤四:拒接

时序图





主动拒接

1. 被叫端发送拒绝接听信令
private void rejectInvite(String inviteId, String data, final V2TIMCallback callback) { V2TIMManager.getSignalingManager().reject(inviteId, data, new V2TIMCallback() { @Override public void onError(int code, String desc) { if (callback != null) { callback.onError(code, desc); } } @Override public void onSuccess() { if (callback != null) { callback.onSuccess(); } } }); }

JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("cmd", "av_call");
JSONObject msgJsonObject = new JSONObject();
msgJsonObject.put("callType", "videoCall"); // 指定通话类型(视频通话、语音通话)
msgJsonObject.put("reason", "active"); // 指定拒接类型(主动拒接、忙线拒接)
jsonObject.put("msg", msgJsonObject);
} catch (JSONException e) {
e.printStackTrace();
}

rejectInvite(inviteId, jsonObject.toString(), new V2TIMCallback() { @Override public void onError(int code, String desc) { // 拒接失败,提示异常或重试 } @Override public void onSuccess() { // 拒接成功,销毁呼叫页面,停止播放呼叫铃声 } });
2. 主叫端收到拒绝接听通知
@Override public void onInviteeRejected(String inviteID, String invitee, String data) {
if (!data.isEmpty()) {
try {
JSONObject jsonObject = new JSONObject(data);
String command = jsonObject.getString("cmd");
JSONObject messageJsonObject = jsonObject.getJSONObject("msg");
if (command.equals("av_call")) {
String reason = messageJsonObject.getString("reason");
if (reason.equals("active")) {
// 提示对方拒绝接听
} else if (reason.equals("busy")) {
// 提示对方忙线中
}
// 销毁呼叫页面,停止播放呼叫铃声
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}

忙线拒接

被叫端收到新的呼叫邀请,若判断本地通话状态为正在通话中,则自动忙线拒接。
@Override
public void onReceiveNewInvitation(String inviteID, String inviter,
String groupId, List<String> inviteeList, String data) {
if (!data.isEmpty()) {
try {
JSONObject jsonObject = new JSONObject(data);
String command = jsonObject.getString("cmd");
JSONObject messageJsonObject = jsonObject.getJSONObject("msg");
if (command.equals("av_call") && isOnCalling) {
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("cmd", "av_call");
JSONObject msgJsonObject = new JSONObject();
msgJsonObject.put("callType", "videoCall"); // 指定通话类型(视频通话、语音通话)
msgJsonObject.put("reason", "busy"); // 指定拒接类型(主动拒接、忙线拒接)
jsonObject.put("msg", msgJsonObject);
} catch (JSONException e) {
e.printStackTrace();
}
// 本地正在通话中,发送忙线拒接信令
rejectInvite(inviteId, jsonObject.toString(), new V2TIMCallback() {
@Override
public void onError(int code, String desc) {
// 忙线拒接失败
}
@Override
public void onSuccess() {
// 忙线拒接成功
}
});
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
注意:
主动拒接和忙线拒接都是使用 reject 信令实现的,但需注意通过信令的自定义数据 datareason 字段加以区分。

步骤五:挂断

时序图





挂断通话

1. 任一端退出房间,重置本地通话状态。
private void hangup() {
mTRTCCloud.stopLocalAudio();
mTRTCCloud.stopLocalPreview();
mTRTCCloud.exitRoom();
}

@Override
public void onExitRoom(int reason) {
// 已成功退出房间并挂断通话
isOnCalling = false;
}
2. 另一端收到远端退房通知,本地执行退房并重置通话状态。
@Override
public void onRemoteUserLeaveRoom(String userId, int reason) {
hangup();
}

@Override
public void onExitRoom(int reason) {
// 已成功退出房间并挂断通话
isOnCalling = false;
}

步骤六:功能控制

打开关闭麦克风

// 打开麦克风
mTRTCCloud.muteLocalAudio(false);
// 关闭麦克风
mTRTCCloud.muteLocalAudio(true);

打开关闭扬声器

// 打开扬声器
mTRTCCloud.muteAllRemoteAudio(false);
// 关闭扬声器
mTRTCCloud.muteAllRemoteAudio(true);

打开关闭摄像头

// 打开摄像头, 指定前置或后置摄像头及渲染控件
mTRTCCloud.startLocalPreview(isFrontCamera, videoView);
// 关闭摄像头
mTRTCCloud.stopLocalPreview();

听筒免提切换

// 切换听筒
mTRTCCloud.getDeviceManager().setAudioRoute(TXDeviceManager.TXAudioRoute.TXAudioRouteEarpiece);
// 切换免提
mTRTCCloud.getDeviceManager().setAudioRoute(TXDeviceManager.TXAudioRoute.TXAudioRouteSpeakerphone);

摄像头切换

// 判断当前是否为前置摄像头
boolean isFrontCamera = mTRTCCloud.getDeviceManager().isFrontCamera();
// 切换前置或后置摄像头, true: 切换为前置; false: 切换为后置
mTRTCCloud.getDeviceManager().switchCamera(!isFrontCamera);

高级功能

网络状态提示

音视频通话过程中,通常需要在对方网络状态较差时给予提示,从而会有通话卡顿的心理预期。
@Override
public void onNetworkQuality(TRTCCloudDef.TRTCQuality localQuality, ArrayList<TRTCCloudDef.TRTCQuality> remoteQuality) {
if (remoteQuality.size() > 0) {
switch (remoteQuality.get(0).quality) {
case TRTCCloudDef.TRTC_QUALITY_Excellent:
Log.i(TAG, "对方网络非常好");
break;
case TRTCCloudDef.TRTC_QUALITY_Good:
Log.i(TAG, "对方网络比较好");
break;
case TRTCCloudDef.TRTC_QUALITY_Poor:
Log.i(TAG, "对方网络一般");
break;
case TRTCCloudDef.TRTC_QUALITY_Bad:
Log.i(TAG, "对方网络较差");
break;
case TRTCCloudDef.TRTC_QUALITY_Vbad:
Log.i(TAG, "对方网络很差");
break;
case TRTCCloudDef.TRTC_QUALITY_Down:
Log.i(TAG, "对方网络极差");
break;
default:
Log.i(TAG, "未定义");
break;
}
}
}
注意:
localQuality 代表本地用户网络质量评估结果,其 userId 字段为空。
remoteQuality 代表远端用户网络质量评估结果,其结果受远端和本地共同影响。

通话时长统计

推荐使用 TRTC 远端用户进房时间作为统计通话时长的开始时间,本地用户退房时间作为统计通话时长的结束时间。
// 开始通话时间
long callStartTime = 0;
// 结束通话时间
long callFinishTime = 0;
// 通话持续时长(秒)
long callDuration = 0;

// 远端用户进房回调
@Override
public void onRemoteUserEnterRoom(String userId) {
callStartTime = System.currentTimeMillis();
}

// 本地用户退房回调
@Override
public void onExitRoom(int reason) {
callFinishTime = System.currentTimeMillis();
callDuration = (callFinishTime - callStartTime) / 1000;
}
注意:
若出现强杀、网络断开等异常结束情形,客户端可能无法统计到相关时间,可通过 服务端事件回调 监听进退房事件统计本次通话时长。

视频美颜特效

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

//copy资源文件到私有目录,只需要做一次
XmagicResParser.copyRes(getApplicationContext());
如果您的资源文件是从 网络动态下载 的,下载成功后,需要设置资源文件路径。
XmagicResParser.setResPath(下载的资源文件本地路径);
3. 设置第三方美颜的视频数据回调,将美颜 SDK 处理每帧数据结果传入 TRTC 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 是 TRTC 集成第三方美颜的 通用且重要步骤
场景化集成腾讯美颜特效指引详见 TRTC SDK 集成腾讯特效,独立集成腾讯美颜特效指引详见 独立集成腾讯特效

大小窗口切换

TRTC 中有很多需要操控视频画面的接口,这些接口都需要您指定视频渲染控件。在 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. 如果您业务涉及到切换显示区域的交互场景,可以使用 TRTC SDK 更新本地预览画面、更新远端用户视频渲染控件功能实现。
// 更新本地预览画面渲染控件
mTRTCCloud.updateLocalView(videoView);

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

离线推送消息

音视频通话场景通常需要用到离线推送消息,使得被叫用户 App 不在线时也能收到来电消息。离线推送集成指引详见 离线消息推送,下面我们重点介绍其中步骤七:发送离线推送消息、步骤八:解析离线推送消息 的实现方法。

发送离线推送消息

调用 invite 发送通话邀请时,您可通过 V2TIMOfflinePushInfo 设置离线推送参数。调用 V2TIMOfflinePushInfo 的 setExt 设置自定义 ext 数据,当用户收到离线推送启动 App 的时候,可以在单击通知跳转的回调中获取到 ext 字段,然后根据 ext 字段内容跳转到指定的 UI 界面。
import com.google.gson.Gson;
import com.tencent.imsdk.v2.V2TIMCallback;
import com.tencent.imsdk.v2.V2TIMManager;
import com.tencent.imsdk.v2.V2TIMOfflinePushInfo;
import com.tencent.qcloud.tuicore.TUILogin;
import com.tencent.qcloud.tuicore.push.OfflinePushExtConfigInfo;
import com.tencent.qcloud.tuicore.push.OfflinePushExtInfo;

import org.json.JSONException;
import org.json.JSONObject;

JSONObject contentObj = new JSONObject();
try {
contentObj.put("cmd", "av_call");
JSONObject contentDetailObj = new JSONObject();
contentDetailObj.put("callType", "videoCall");
contentDetailObj.put("roomId", generateRoomId());
contentObj.put("cmdInfo", contentDetailObj);
} catch (JSONException e) {
e.printStackTrace();
}
String data = contentObj.toString();

OfflinePushExtInfo offlinePushExtInfo = new OfflinePushExtInfo();

offlinePushExtInfo.getBusinessInfo().setSenderId(TUILogin.getLoginUser());
offlinePushExtInfo.getBusinessInfo().setSenderNickName(mNickname);
offlinePushExtInfo.getBusinessInfo().setFaceUrl(mFaceUrl);
offlinePushExtInfo.getBusinessInfo().setSendTime(System.currentTimeMillis() / 1000);
offlinePushExtInfo.getBusinessInfo().setDesc("You have a new call invitation");
offlinePushExtInfo.getBusinessInfo().setChatAction(OfflinePushExtInfo.REDIRECT_ACTION_CALL);
offlinePushExtInfo.getBusinessInfo().setCustomData(data.getBytes());

offlinePushExtInfo.getConfigInfo().setFCMPushType(OfflinePushExtConfigInfo.FCM_PUSH_TYPE_DATA);
offlinePushExtInfo.getConfigInfo().setFCMNotificationType(OfflinePushExtConfigInfo.FCM_NOTIFICATION_TYPE_TIMPUSH);

V2TIMOfflinePushInfo v2TIMOfflinePushInfo = new V2TIMOfflinePushInfo();
v2TIMOfflinePushInfo.setTitle(mNickname);
v2TIMOfflinePushInfo.setDesc("You have a new call invitation");
v2TIMOfflinePushInfo.setExt(new Gson().toJson(offlinePushExtInfo).getBytes());

v2TIMOfflinePushInfo.setAndroidOPPOChannelID("tuikit");
v2TIMOfflinePushInfo.setAndroidHuaWeiCategory("IM");
v2TIMOfflinePushInfo.setAndroidVIVOCategory("IM");

// 设置自定义铃声
v2TIMOfflinePushInfo.setIOSSound("phone_ringing.mp3");
v2TIMOfflinePushInfo.setAndroidSound("phone_ringing");

v2TIMOfflinePushInfo.setIOSInterruptionLevel("time-sensitive");
v2TIMOfflinePushInfo.enableIOSBackgroundNotification(false);
v2TIMOfflinePushInfo.setAndroidHonorImportance("NORMAL");
v2TIMOfflinePushInfo.setAndroidMeizuNotifyType(1);

V2TIMManager.getSignalingManager().invite(receiver, data, false, v2TIMOfflinePushInfo, timeout, new V2TIMCallback() {
@Override
public void onError(int code, String desc) {
// 发送呼叫邀请失败
}

@Override
public void onSuccess() {
// 发送呼叫邀请成功
}
});

解析离线推送消息

收到离线推送的通知栏消息,点击会自动跳转到您在之前配置的跳转界面,可以在界面启动的 onResume() 方法中,调用 getIntent().getExtras() 获取透传的离线推送参数再自定义跳转。具体可以参照 TUIKitDemo 的 handleOfflinePush() 方法。
private void handleOfflinePush() {
// 根据登录状态,判断是否需要重新登录 IM
// 1. 如果登录状态为 V2TIMManager.V2TIM_STATUS_LOGOUT,跳转到登录界面,重新登录 IM
if (V2TIMManager.getInstance().getLoginStatus() == V2TIMManager.V2TIM_STATUS_LOGOUT) {
Intent intent = new Intent(MainActivity.this, SplashActivity.class);
if (getIntent() != null) {
intent.putExtras(getIntent());
}
startActivity(intent);
finish();
return;
}

// 2. 否则,说明 App 只是处于退后台的状态,直接解析离线推送参数
final OfflineMessageBean bean = OfflineMessageDispatcher.parseOfflineMessage(getIntent());
if (bean != null) {
setIntent(null);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (manager != null) {
manager.cancelAll();
}

if (bean.action == OfflineMessageBean.REDIRECT_ACTION_CALL) {
Intent startActivityIntent = new Intent(context, MyCustomActivity.class);
startActivityIntent.putExtras(getIntent());
startActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(startActivityIntent);
}
}
}
注意:
FCM 单击通知栏的消息会默认跳转至应用的默认 Launcher 界面,您可以在界面启动的 onResume() 方法中,通过调用 getIntent().getExtras() 获取透传的离线推送参数再自定义跳转。

异常处理

TRTC 异常错误处理

TRTC SDK 遇到不可恢复的错误会在 onError 回调中抛出,详见 错误码表

UserSig 相关

UserSig 校验失败会导致进房失败,您可使用 UserSig 工具 进行校验。
枚举
取值
描述
ERR_TRTC_INVALID_USER_SIG
-3320
进房参数 UserSig 不正确,请检查 TRTCParams.userSig 是否为空。
ERR_TRTC_USER_SIG_CHECK_FAILED
-100018
UserSig 校验失败,请检查参数 TRTCParams.userSig 是否填写正确或已经过期。

进退房相关

进房失败请先检查进房参数是否正确,且进退房接口必须成对调用,即便进房失败也需要调用退房接口。
枚举
取值
描述
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 的房间。

设备相关

可监听设备相关错误,在出现相关错误时 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
麦克风正在被占用中,例如移动设备正在通话时,打开麦克风会失败。

收不到离线推送排查

1. 发送消息为自定义消息
自定义消息的离线推送和普通消息不太一样,自定义消息的内容我们无法解析,不能确定推送的内容,所以默认不推送,如果您有推送需求,需要您在 sendMessage 的时候设置 offlinePushInfodesc 字段,推送的时候会默认展示 desc 信息。
2. 设备通知栏设置影响
离线推送的直观表现就是通知栏提示,所以同其他通知一样受设备通知相关设置的影响,以华为为例:
“手机设置-通知-锁屏通知-隐藏或者不显示通知”,会影响锁屏状态下离线推送通知显示。
“手机设置-通知-更多通知设置-状态栏显示通知图标”,会影响状态栏下离线推送通知的图标显示。
“手机设置-通知-应用的通知管理-允许通知”,打开关闭会直接影响离线推送通知显示。
“手机设置-通知-应用的通知管理-通知铃声” 和 “手机设置-通知-应用的通知管理-静默通知”,会影响离线推送通知铃音的效果。
3. 按照流程接入完成,还是收不到离线推送
首先在 IM 控制台通过 离线测试工具 自测下是否可以正常推送。 推送异常情况,设备状态异常,需要检查下 IM 控制台配置各项参数是否正确,再者需要检查下代码初始化注册逻辑,包括厂商推送服务注册和 IM 设置离线推送配置相关逻辑是否正确设置。 推送异常情况,设备状态正常,需要看下是否需要正确填写 channel ID 或者后台服务是否正常。
离线推送依赖厂商能力,一些简单的字符可能会被厂商过滤不能透传推送。
如果离线推送消息出现推送不及时或者偶尔收不到的情况,需要看下厂商的推送限制。

跳转界面不成功

单击离线推送消息的通知栏,跳转到指定界面,原理是后台根据您在控制台配置的各个厂商的跳转方式和界面参数,根据厂商接口规则,传递给厂商服务器,单击时进行对应界面启动跳转。对应界面启动还依赖清单文件的配置,必须和控制台配置的相对应,才能正确启动和跳转。
1. 首先需要重点排查下控制台和清单文件相关配置是否对应且正确,可参见 TUIKitDemo 的配置,注意部分厂商提供接口方式存在差异。
2. 如果跳转到了配置的界面,需要再看下配置界面内离线消息的解析和界面重定向是否正常。

帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈