为了让用户更好地感知网络质量,建议业务在通话前进行测速,通话中监测网络质量和连接情况,并提供相应的 UI 界面交互与提示。这样可以方便用户评估当前网络质量,并及时调整网络设置,以达到最佳通话效果。
通话前网络测速
普通用户很难去感知网络情况,建议在开播前进行测速,以便于评估当前网络质量,及时调整网络以达到通话的最优的效果。
测速的原理是 SDK 向服务器节点发送一批探测包,然后统计回包的质量,并将测速的结果通过回调接口进行通知,如下图所示:


通话前测速的优势:
测速结果将用于优化 SDK 的服务器选择策略,因此建议在用户首次通话前进行一次测速,以帮助我们选择最佳服务器。
如果测试结果非常不理想,您可以通过显眼的 UI 提示用户选择更好的网络环境。
startSpeedTest 测速
//启动网络测速的示例代码, 需要 sdkAppId 和 UserSig,(获取方式参考基本功能) // 这里以登录后开始测试为例 public void onLogin(String userId, String userSig) { TRTCCloudDef.TRTCSpeedTestParams params = new TRTCCloudDef.TRTCSpeedTestParams(); params.sdkAppId = GenerateTestUserSig.SDKAPPID; params.userId = mEtUserId.getText().toString(); params.userSig = GenerateTestUserSig.genTestUserSig(params.userId); params.expectedUpBandwidth = Integer.parseInt(expectUpBandwidthStr); params.expectedDownBandwidth = Integer.parseInt(expectDownBandwidthStr); // sdkAppID 为控制台中获取的实际应用的 AppID trtcCloud.startSpeedTest(params); } // 监听测速结果,继承 TRTCCloudListener 并实现如下方法 void onSpeedTestResult(TRTCCloudDef.TRTCSpeedTestResult result) { // 测速完成后,会回调出测速结果 }Android
// 启动网络测速的示例代码, 需要 sdkAppId 和 UserSig,(获取方式参考基本功能)// 这里以登录后开始测试为例- (void)onLogin:(NSString *)userId userSig:(NSString *)userSid{TRTCSpeedTestParams *params;// sdkAppID 为控制台中获取的实际应用的 AppIDparams.sdkAppID = sdkAppId;params.userID = userId;params.userSig = userSig;// 预期的上行带宽(kbps,取值范围: 10 ~ 5000,为 0 时不测试)params.expectedUpBandwidth = 5000;// 预期的下行带宽(kbps,取值范围: 10 ~ 5000,为 0 时不测试)params.expectedDownBandwidth = 5000;[trtcCloud startSpeedTest:params];}- (void)onSpeedTestResult:(TRTCSpeedTestResult *)result {// 测速完成后,返回测速结果}
// 启动网络测速的示例代码, 需要 sdkAppId 和 UserSig,(获取方式参考基本功能)// 这里以登录后开始测试为例void onLogin(const char* userId, const char* userSig){TRTCSpeedTestParams params;// sdkAppID 为控制台中获取的实际应用的 AppIDparams.sdkAppID = sdkAppId;params.userId = userid;param.userSig = userSig;// 预期的上行带宽(kbps,取值范围: 10 ~ 5000,为 0 时不测试)param.expectedUpBandwidth = 5000;// 预期的下行带宽(kbps,取值范围: 10 ~ 5000,为 0 时不测试)param.expectedDownBandwidth = 5000;trtcCloud->startSpeedTest(params);}// 监听测速结果void TRTCCloudCallbackImpl::onSpeedTestResult(const TRTCSpeedTestResult& result){// 测速完成后,会回调出测速结果}
字段 | 含义 | 含义说明 |
success | 是否成功 | 本次测试是否成功 |
errMsg | 错误信息 | 带宽测试的详细错误信息 |
ip | 服务器 IP | 测速服务器的 IP |
网络质量评分 | 通过评估算法测算出的网络质量,loss 越低,rtt 越小,得分也就越高 | |
upLostRate | 上行丢包率 | 范围是[0 - 1.0],例如0.3代表每向服务器发送10个数据包,可能有3个会在中途丢失 |
downLostRate | 下行丢包率 | 范围是[0 - 1.0],例如0.2代表从服务器每收取10个数据包,可能有2个会在中途丢失 |
rtt | 网络延时 | 代表 SDK 跟服务器一来一回之间所消耗的时间,这个值越小越好,正常数值在10ms - 100ms之间 |
availableUpBandwidth | 上行带宽 | 预测的上行带宽,单位为kbps, -1表示无效值 |
availableDownBandwidth | 下行带宽 | 预测的下行带宽,单位为kbps, -1表示无效值 |
工具测速
如果您不想通过调用接口的方式进行网络测速,TRTC 还提供了桌面端的网络测速工具程序,帮助您快速获取详细的网络质量信息,您可以根据自己平台选择以下程序进行下载。该程序提供了快速测试和持续测试两种测试选择:
Mac
Windows
指标 | 含义 |
WiFi Quality | Wi-Fi 信号质量 |
DNS RTT | 腾讯云的测速域名解析耗时 |
MTR | MTR 是一款网络测试工具,能探测客户端到 TRTC 节点的丢包率与延时,还可以查看路由中每一跳的具体信息 |
UDP Loss | 客户端到 TRTC 节点的 UDP 丢包率 |
UDP RTT | 客户端到 TRTC 节点的 UDP 延时 |
Local RTT | 客户端到本地网关的延时 |
Upload | 上行预估带宽 |
Download | 下行预估带宽 |
注意:
视频通话期间请勿测试,以免影响通话质量。
测速本身会消耗一定的流量,从而产生极少量额外的流量费用(基本可以忽略)。
通话中网络检测
为了让用户更清楚地感知到通话过程中可能出现的网络波动,建议在通话界面添加相应的 UI 提醒。特别是在网络状况较差时,通过提醒使用户能够感知到变化,并及时调整以改善网络情况。
onNetworkQuality 监听本地网络质量
onNetworkQuality 的回调事件,它会每两秒钟一次向您汇报当前的网络质量,其参数包括 localQuality 和 remoteQuality 两个部分:
localQuality:代表您当前的网络质量,分为 6 个等级,分别是 Excellent、Good、Poor、Bad、VeryBad 和 Down。
remoteQuality:代表远端用户的网络质量,这是一个数组,数组中的每个元素代表一个远端用户的网络质量。
Quality | 名称 | 说明 |
0 | Unknown | 未感知到 |
1 | Excellent | 当前网络非常好 |
2 | Good | 当前网络比较好 |
3 | Poor | 当前网络一般 |
4 | Bad | 当前网络较差,可能会出现明显的卡顿和通话延迟 |
5 | VeryBad | 当前网络很差,TRTC 只能勉强保持连接,但无法保证通讯质量 |
6 | Down | 当前网络不满足 TRTC 的最低要求,无法进行正常的音视频通话 |
通过对 onNetworkQuality 进行监听并在界面上做相应地提示即可:
// 监听 onNetworkQuality 回调并感知当前网络状态的变化 @Override public void onNetworkQuality(TRTCCloudDef.TRTCQuality localQuality, ArrayList<trtcclouddef.trtcquality> remoteQuality) { // Get your local network quality switch(localQuality) { case TRTCQuality_Unknown: Log.d(TAG, "SDK has not yet sensed the current network quality."); break; case TRTCQuality_Excellent: Log.d(TAG, "The current network is very good."); break; case TRTCQuality_Good: Log.d(TAG, "The current network is good."); break; case TRTCQuality_Poor: Log.d(TAG, "The current network quality barely meets the demand."); break; case TRTCQuality_Bad: Log.d(TAG, "The current network is poor, and there may be significant freezes and call delays."); break; case TRTCQuality_VeryBad: Log.d(TAG, "The current network is very poor, the communication quality cannot be guaranteed"); break; case TRTCQuality_Down: Log.d(TAG, "The current network does not meet the minimum requirements."); break; default: break; } // Get the network quality of remote users for (TRTCCloudDef.TRTCQuality info : arrayList) { Log.d(TAG, "remote user : = " + info.userId + ", quality = " + info.quality); } }
// 监听 onNetworkQuality 回调并感知当前网络状态的变化- (void)onNetworkQuality:(TRTCQualityInfo *)localQuality remoteQuality:(NSArray<trtcqualityinfo *=""> *)remoteQuality {// Get your local network qualityswitch(localQuality.quality) {case TRTCQuality_Unknown:NSLog(@"SDK has not yet sensed the current network quality.");break;case TRTCQuality_Excellent:NSLog(@"The current network is very good.");break;case TRTCQuality_Good:NSLog(@"The current network is good.");break;case TRTCQuality_Poor:NSLog(@"The current network quality barely meets the demand.");break;case TRTCQuality_Bad:NSLog(@"The current network is poor, and there may be significant freezes and call delays.");break;case TRTCQuality_VeryBad:NSLog(@"The current network is very poor, the communication quality cannot be guaranteed");break;case TRTCQuality_Down:NSLog(@"The current network does not meet the minimum requirements.");break;default:break;}// Get the network quality of remote usersfor (TRTCQualityInfo *info in arrayList) {NSLog(@"remote user : = %@, quality = %@", info.userId, @(info.quality));}}iOSMac
// 监听 onNetworkQuality 回调并感知当前网络状态的变化void onNetworkQuality(liteav::TRTCQualityInfo local_quality,liteav::TRTCQualityInfo* remote_quality, uint32_t remote_quality_count) {// Get your local network qualityswitch (local_quality.quality) {case TRTCQuality_Unknown:printf("SDK has not yet sensed the current network quality.");break;case TRTCQuality_Excellent:printf("The current network is very good.");break;case TRTCQuality_Good:printf("The current network is good.");break;case TRTCQuality_Poor:printf("The current network quality barely meets the demand.");break;case TRTCQuality_Bad:printf("The current network is poor, and there may be significant freezes and call delays.");break;case TRTCQuality_Vbad:printf("The current network is very poor, the communication quality cannot be guaranteed");break;case TRTCQuality_Down:printf("The current network does not meet the minimum requirements.");break;default:break;}// Get the network quality of remote usersfor (int i = 0; i < remote_quality_count; ++i) {printf("remote user : = %s, quality = %d", remote_quality[i].userId, remote_quality[i].quality);}}
onStatistics 监听完整网络质量
视频统计信息:视频的分辨率(resolution)、帧率(FPS)和比特率(bitrate)等信息。
音频统计信息:音频的采样率(samplerate)、声道(channel)和比特率(bitrate)等信息。
网络统计信息:SDK 和云端一次往返(SDK > Cloud > SDK)的网络耗时(rtt)、丢包率(loss)、上行流量(sentBytes)和下行流量(receivedBytes)等信息。
枚举类型 | 描述 |
appCpu | 当前应用的 CPU 使用率,单位 (%),Android 8.0 以上不支持。 |
downLoss | 从云端到 SDK 的下行丢包率,单位 (%)。 该数值越小越好,如果 downLoss 为 0%,则意味着下行链路的网络质量很好,从云端接收的数据包基本不发生丢失。 如果 downLoss 为 30%,则意味着云端向 SDK 传输的音视频数据包中,会有 30% 丢失在传输链路中。 |
gatewayRtt | 从 SDK 到本地路由器的往返时延,单位ms。该数值代表从 SDK 发送一个网络包到本地路由器网关,再从网关回送一个网络包到 SDK 的总计耗时,也就是一个网络包经历 “SDK > 网关 > SDK”的总耗时。 该数值越小越好:如果 gatewayRtt < 50ms,意味着较低的音视频通话延迟;如果 gatewayRtt > 200ms,则意味着较高的音视频通话延迟。 当网络类型为蜂窝网时,该值无效。 |
localArray | 本地的音视频统计信息。 由于本地可能有三路音视频流(即高清大画面,低清小画面,以及辅流画面),因此本地的音视频统计信息是一个数组。 |
receiveBytes | 总接收字节数(包含信令数据和音视频数据),单位为字节数(Bytes)。 |
remoteArray | 远端的音视频统计信息。 因为同时可能有多个远端用户,而且每个远端用户同时可能有多路音视频流(即高清大画面,低清小画面,以及辅流画面),因此远端的音视频统计信息是一个数组。 |
rtt | 从 SDK 到云端的往返延时,单位ms。该数值代表从 SDK 发送一个网络包到云端,再从云端回送一个网络包到 SDK 的总计耗时,也就是一个网络包经历 “SDK > 云端 > SDK” 的总耗时。 该数值越小越好:如果 rtt < 50ms,意味着较低的音视频通话延迟;如果 rtt > 200ms,则意味着较高的音视频通话延迟。 说明: rtt 代表 “SDK > 云端 > SDK” 的总耗时,所以不需要区分 upRtt 和 downRtt。 |
sendBytes | 总发送字节数(包含信令数据和音视频数据),单位为字节数(Bytes)。 |
systemCpu | 当前系统的 CPU 使用率,单位 (%),Android 8.0 以上不支持。 |
upLoss | 从 SDK 到云端的上行丢包率,单位 (%)。 该数值越小越好,如果 upLoss 为 0%,则意味着上行链路的网络质量很好,上传到云端的数据包基本不发生丢失。 如果 upLoss 为 30%,则意味着 SDK 向云端发送的音视频数据包中,会有 30% 丢失在传输链路中。 |
@Override public void onStatistics(TRTCStatistics statistics) { super.onStatistics(statistics); // appCpu使用率 Log.d(TAG, "appCpu:" + statistics.appCpu); // systemCpu使用率 Log.d(TAG, "systemCpu:" + statistics.systemCpu); // rtt SDK 到云端的往返延时 Log.d(TAG, "rtt:" + statistics.rtt); // upLoss 上行丢包率 Log.d(TAG, "upLoss:" + statistics.upLoss); // downLoss 下行丢包率 Log.d(TAG, "downLoss:" + statistics.downLoss); // gatewayRtt 网关到云端的往返延时 Log.d(TAG, "gatewayRtt:" + statistics.gatewayRtt); // sendBytes 发送字节数 Log.d(TAG, "sendBytes:" + statistics.sendBytes); // receiveBytes 接收字节数 Log.d(TAG, "receiveBytes:" + statistics.receiveBytes); if(statistics.localArray != null) { for (int i = 0; i < statistics.localArray.size(); i++) { // 本地视频宽度 Log.d(TAG, "localStatistics width:" + statistics.localArray.get(i).width); // 本地视频高度 Log.d(TAG, "localStatistics height:" + statistics.localArray.get(i).height); // 本地视频帧率 Log.d(TAG, "localStatistics frameRate:" + statistics.localArray.get(i).frameRate); // 本地视频码率 Log.d(TAG, "localStatistics videoBitrate:" + statistics.localArray.get(i).videoBitrate); // 本地音频码率 Log.d(TAG, "localStatistics audioBitrate:" + statistics.localArray.get(i).audioBitrate); // 本地音频设备采集状态(用于检测音频外设的健康度)0:采集设备状态正常;1:检测到长时间静音;2:检测到破音;3:检测到声音异常间断。 Log.d(TAG, "localStatistics audioCaptureState:" + statistics.localArray.get(i).audioCaptureState); // 本地音频采样率 Log.d(TAG, "localStatistics audioSampleRate:" + statistics.localArray.get(i).audioSampleRate); // 本地流类型 Log.d(TAG, "localStatistics streamType:" + statistics.localArray.get(i).streamType); } } if(statistics.remoteArray != null) { for (int i = 0; i < statistics.remoteArray.size(); i++) { // 远端用户userid Log.d(TAG, "remoteStatistics userId:" + statistics.remoteArray.get(i).userId); // 远端用户流类型 Log.d(TAG, "remoteStatistics streamType:" + statistics.remoteArray.get(i).streamType); // 远端视频宽度 Log.d(TAG, "remoteStatistics width:" + statistics.remoteArray.get(i).width); // 远端视频高度 Log.d(TAG, "remoteStatistics height:" + statistics.remoteArray.get(i).height); // 远端视频帧率 Log.d(TAG, "remoteStatistics frameRate:" + statistics.remoteArray.get(i).frameRate); // 远端视频码率 Log.d(TAG, "remoteStatistics videoBitrate:" + statistics.remoteArray.get(i).videoBitrate); // 远端视频卡顿率 单位 (%) 视频播放卡顿率(videoBlockRate) = 视频播放的累计卡顿时长(videoTotalBlockTime) / 视频播放的总时长。 Log.d(TAG, "remoteStatistics videoBlockRate:" + statistics.remoteArray.get(i).videoBlockRate); // 视频播放的累计卡顿时长,单位 ms Log.d(TAG, "remoteStatistics videoTotalBlockTime:" + statistics.remoteArray.get(i).videoTotalBlockTime); // 该路视频流的总丢包率 // videoPacketLoss 代表该路视频流历经 主播>云端>观众 这样一条完整的传输链路后,最终在观众端统计到的丢包率。 // videoPacketLoss 越小越好,丢包率为0即表示该路视频流的所有数据均已经完整地到达了观众端。 // 如果出现了 downLoss == 0 但 videoPacketLoss != 0 的情况,说明该路视频流在 云端>观众 这一段链路上没有出现丢包,但是在 主播>云端 这一段链路上出现了不可恢复的丢包。 Log.d(TAG, "remoteStatistics videoPacketLoss:" + statistics.remoteArray.get(i).videoPacketLoss); // 远端音频码率 Log.d(TAG, "remoteStatistics audioBitrate:" + statistics.remoteArray.get(i).audioBitrate); // 远端音频播放的卡顿率 Log.d(TAG, "remoteStatistics audioBlockRate:" + statistics.remoteArray.get(i).audioBlockRate); // 远端音频的采样率 Log.d(TAG, "remoteStatistics audioSampleRate:" + statistics.remoteArray.get(i).audioSampleRate); // 远端音频丢包率 Log.d(TAG, "remoteStatistics audioPacketLoss:" + statistics.remoteArray.get(i).audioPacketLoss); // 音频播放的累计卡顿时长 Log.d(TAG, "remoteStatistics audioTotalBlockTime:" + statistics.remoteArray.get(i).audioTotalBlockTime); // 播放延迟 为了避免网络抖动和网络包乱序导致的声音和画面卡顿,TRTC 会在播放端管理一个播放缓冲区,用于对接收到的网络数据包进行整理,该缓冲区的大小会根据当前的网络质量进行自适应调整,该缓冲区的大小折算成以毫秒为单位的时间长度,也就是 jitterBufferDelay。 Log.d(TAG, "remoteStatistics jitterBufferDelay:" + statistics.remoteArray.get(i).jitterBufferDelay); // 端到端延迟,单位 ms //point2PointDelay 代表 “主播=>云端=>观众” 的延迟,更准确地说,它代表了“采集=>编码=>网络传输=>接收=>缓冲=>解码=>播放” 全链路的延迟。 //point2PointDelay 需要本地和远端的 SDK 均为 8.5 及以上的版本才生效,若远端用户为 8.5 以前的版本,此数值会一直为0,代表无意义。 Log.d(TAG, "remoteStatistics point2PointDelay:" + statistics.remoteArray.get(i).point2PointDelay); } } }
- (void)onStatistics:(TRTCStatistics *)statistics {// appCpu使用率NSLog(@"appCpu: %u", statistics.appCpu);// systemCpu使用率NSLog(@"systemCpu: %u", statistics.systemCpu);// rtt SDK 到云端的往返延时NSLog(@"rtt: %d", statistics.rtt);// upLoss 上行丢包率NSLog(@"upLoss: %u", statistics.upLoss);// downLoss 下行丢包率NSLog(@"downLoss: %u", statistics.downLoss);// gatewayRtt 网关到云端的往返延时NSLog(@"gatewayRtt: %d", statistics.gatewayRtt);// sendBytes 发送字节数NSLog(@"sendBytes: %llu", (unsigned long long)statistics.sentBytes);// receiveBytes 接收字节数NSLog(@"receiveBytes: %llu", (unsigned long long)statistics.receivedBytes);if (statistics.localStatistics != nil) {for (int i = 0; i < statistics.localStatistics.count; i++) {// 本地视频宽度NSLog(@"localStatistics width: %d", statistics.localStatistics[i].width);// 本地视频高度NSLog(@"localStatistics height: %d", statistics.localStatistics[i].height);// 本地视频帧率NSLog(@"localStatistics frameRate: %u", statistics.localStatistics[i].frameRate);// 本地视频码率NSLog(@"localStatistics videoBitrate: %d", statistics.localStatistics[i].videoBitrate);// 本地音频码率NSLog(@"localStatistics audioBitrate: %d", statistics.localStatistics[i].audioBitrate);// 本地音频设备采集状态NSLog(@"localStatistics audioCaptureState: %d", statistics.localStatistics[i].audioCaptureState);// 本地音频采样率NSLog(@"localStatistics audioSampleRate: %d", statistics.localStatistics[i].audioSampleRate);// 本地流类型NSLog(@"localStatistics streamType: %ld", (long)statistics.localStatistics[i].streamType);}}if (statistics.remoteStatistics != nil) {for (int i = 0; i < statistics.remoteStatistics.count; i++) {// 远端用户useridNSLog(@"remoteStatistics userId: %@", statistics.remoteStatistics[i].userId);// 远端用户流类型NSLog(@"remoteStatistics streamType: %ld", (long)statistics.remoteStatistics[i].streamType);// 远端视频宽度NSLog(@"remoteStatistics width: %d", statistics.remoteStatistics[i].width);// 远端视频高度NSLog(@"remoteStatistics height: %d", statistics.remoteStatistics[i].height);// 远端视频帧率NSLog(@"remoteStatistics frameRate: %u", statistics.remoteStatistics[i].frameRate);// 远端视频码率NSLog(@"remoteStatistics videoBitrate: %d", statistics.remoteStatistics[i].videoBitrate);// 远端视频卡顿率NSLog(@"remoteStatistics videoBlockRate: %u", statistics.remoteStatistics[i].videoBlockRate);// 视频播放的累计卡顿时长NSLog(@"remoteStatistics videoTotalBlockTime: %d", statistics.remoteStatistics[i].videoTotalBlockTime);// 该路视频流的总丢包率NSLog(@"remoteStatistics videoPacketLoss: %u", statistics.remoteStatistics[i].videoPacketLoss);// 远端音频码率NSLog(@"remoteStatistics audioBitrate: %d", statistics.remoteStatistics[i].audioBitrate);// 远端音频播放的卡顿率NSLog(@"remoteStatistics audioBlockRate: %u", statistics.remoteStatistics[i].audioBlockRate);// 远端音频的采样率NSLog(@"remoteStatistics audioSampleRate: %d", statistics.remoteStatistics[i].audioSampleRate);// 远端音频丢包率NSLog(@"remoteStatistics audioPacketLoss: %u", statistics.remoteStatistics[i].audioPacketLoss);// 音频播放的累计卡顿时长NSLog(@"remoteStatistics audioTotalBlockTime: %d", statistics.remoteStatistics[i].audioTotalBlockTime);// 播放延迟NSLog(@"remoteStatistics jitterBufferDelay: %d", statistics.remoteStatistics[i].jitterBufferDelay);// 端到端延迟NSLog(@"remoteStatistics point2PointDelay: %d", statistics.remoteStatistics[i].point2PointDelay);}}}
void onStatistics(const TRTCStatistics& statistics) {// appCpu使用率printf("appCpu: %f\\n", statistics.appCpu);// systemCpu使用率printf("systemCpu: %f\\n", statistics.systemCpu);// rtt SDK 到云端的往返延时printf("rtt: %d\\n", statistics.rtt);// upLoss 上行丢包率printf("upLoss: %f\\n", statistics.upLoss);// downLoss 下行丢包率printf("downLoss: %f\\n", statistics.downLoss);// gatewayRtt 网关到云端的往返延时printf("gatewayRtt: %d\\n", statistics.gatewayRtt);// sentBytes 发送字节数printf("sentBytes: %lld\\n", statistics.sentBytes);// receiveBytes 接收字节数printf("receivedBytes: %lld\\n", statistics.receivedBytes);//for (const auto& localStat : statistics.localStatisticsArray) {if (statistics.localStatisticsArray != nullptr && statistics.localStatisticsArraySize != 0){for (int i = 0; i < statistics.localStatisticsArraySize; i++){// 本地视频宽度printf("localStatistics width: %d\\n", statistics.localStatisticsArray[i].width);// 本地视频高度printf("localStatistics height: %d\\n", statistics.localStatisticsArray[i].height);// 本地视频帧率printf("localStatistics frameRate: %f\\n", statistics.localStatisticsArray[i].frameRate);// 本地视频码率printf("localStatistics videoBitrate: %d\\n", statistics.localStatisticsArray[i].videoBitrate);// 本地音频码率printf("localStatistics audioBitrate: %d\\n", statistics.localStatisticsArray[i].audioBitrate);// 本地音频设备采集状态printf("localStatistics audioCaptureState: %d\\n", statistics.localStatisticsArray[i].audioCaptureState);// 本地音频采样率printf("localStatistics audioSampleRate: %d\\n", statistics.localStatisticsArray[i].audioSampleRate);// 本地流类型printf("localStatistics streamType: %d\\n", statistics.localStatisticsArray[i].streamType);}}//for (const auto& remoteStat : statistics.remoteStatisticsArray) {if (statistics.remoteStatisticsArray != nullptr && statistics.remoteStatisticsArraySize != 0){for (int i = 0; i < statistics.remoteStatisticsArraySize; i++){// 远端用户useridprintf("remoteStatistics userId: %s\\n", statistics.remoteStatisticsArray[i].userId);// 远端用户流类型printf("remoteStatistics streamType: %d\\n", statistics.remoteStatisticsArray[i].streamType);// 远端视频宽度printf("remoteStatistics width: %d\\n", statistics.remoteStatisticsArray[i].width);// 远端视频高度printf("remoteStatistics height: %d\\n", statistics.remoteStatisticsArray[i].height);// 远端视频帧率printf("remoteStatistics frameRate: %f\\n", statistics.remoteStatisticsArray[i].frameRate);// 远端视频码率printf("remoteStatistics videoBitrate: %d\\n", statistics.remoteStatisticsArray[i].videoBitrate);// 远端视频卡顿率printf("remoteStatistics videoBlockRate: %f\\n", statistics.remoteStatisticsArray[i].videoBlockRate);// 视频播放的累计卡顿时长printf("remoteStatistics videoTotalBlockTime: %d\\n", statistics.remoteStatisticsArray[i].videoTotalBlockTime);// 该路视频流的总丢包率printf("remoteStatistics videoPacketLoss: %f\\n", statistics.remoteStatisticsArray[i].videoPacketLoss);// 远端音频码率printf("remoteStatistics audioBitrate: %d\\n", statistics.remoteStatisticsArray[i].audioBitrate);// 远端音频播放的卡顿率printf("remoteStatistics audioBlockRate: %f\\n", statistics.remoteStatisticsArray[i].audioBlockRate);// 远端音频的采样率printf("remoteStatistics audioSampleRate: %d\\n", statistics.remoteStatisticsArray[i].audioSampleRate);// 远端音频丢包率printf("remoteStatistics audioPacketLoss: %f\\n", statistics.remoteStatisticsArray[i].audioPacketLoss);// 音频播放的累计卡顿时长printf("remoteStatistics audioTotalBlockTime: %d\\n", statistics.remoteStatisticsArray[i].audioTotalBlockTime);// 播放延迟printf("remoteStatistics jitterBufferDelay: %d\\n", statistics.remoteStatisticsArray[i].jitterBufferDelay);// 端到端延迟printf("remoteStatistics point2PointDelay: %d\\n", statistics.remoteStatisticsArray[i].point2PointDelay);}}}
通话中连接监测
用户在通话过程中,除了要感知网络变化,还需要了解当前 SDK 与后台的连接状态。这样用户可以更准确地判断当前网络是否已经调整完成,以及是否可以正常进行通话。


回调接口 | 接口说明 |
onConnectionLost | SDK 与云端的连接已经断开 |
onTryToReconnect | SDK 正在尝试重新连接到云端 |
onConnectionRecovery | SDK 与云端的连接已经恢复 |
断线重连逻辑
SDK 支持用户断线情况下自动重连(若持续30分钟都未重连成功,则自动退房并返回-3301错误码),连接过程中具体的连接状态和处理逻辑如下说明。下图展示了从用户 Userid1 加入频道,到连接中断,再到重新加入房间过程中,收到的监听回调事件:


具体说明:
T1:用户侧发起调用
enterRoom
接口发起进房请求。T2:用户 Userid1 收到
onEnterRoom
回调,Userid2 感知 Userid1 存在延迟,大约300ms后,Userid2 收到 onRemoteUserEnterRoom
回调。T3:Userid1 客户端因网络问题断网,SDK 会尝试重新加入房间。
T4:Userid1 如果连续8秒没有连接上服务端,Userid1 收到
onConnectionLost
断连回调。T5:Userid1 接着隔3秒没有连接上服务端,Userid1 收到
onTryToReconnect
重试回调。T6:Userid1 接着每隔24秒,收到
onTryToReconnect
重试回调。T7:Userid2 会在收到 Userid1 掉线通知90s后,SDK 判断远端用户 Userid1 掉线,Userid2 收到
onRemoteUserLeaveRoom
回调。T8:如果 Userid1 断连期间任意时刻重连成功,Userid1 收到
onConnectionRecovery
恢复回调。@Override public void onConnectionLost() { // 连接断开 Log.d(TAG, "onConnectionLost"); } @Override public void onTryToReconnect() { // 尝试重连 Log.d(TAG, "onTryToReconnect"); } @Override public void onConnectionRecovery() { // 重连成功 Log.d(TAG, "onConnectionRecovery"); }
- (void)onConnectionLost {// 连接断开NSLog(@"onConnectionLost");}- (void)onTryToReconnect {// 尝试重连NSLog(@"onTryToReconnect");}- (void)onConnectionRecovery {// 重连成功NSLog(@"onConnectionRecovery");}
void onConnectionLost() {// 连接断开printf("onConnectionLost\\n");}void onTryToReconnect() {// 尝试重连printf("onTryToReconnect\\n");}void onConnectionRecovery() {// 重连成功printf("onConnectionRecovery\\n");}