一、通话前网络测速
普通用户很难去感知网络情况,建议在开播前进行测速,以便于评估当前网络质量,及时调整网络以达到通话的最优的效果。
测速的原理是 SDK 向服务器节点发送一批探测包,然后统计回包的质量,并将测速的结果通过回调接口进行通知:
通话前测速的优势:
·测速结果将用于优化 SDK 的服务器选择策略,因此建议在用户首次通话前进行一次测速,以帮助我们选择最佳服务器。
·如果测试结果非常不理想,你可以通过显眼的 UI 提示用户选择更好的网络环境。
二、startSpeedTest 测速
AndroidiOSWindows//启动网络测速的示例代码, 需要 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 为控制台中获取的实际应用的 AppID params.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 为控制台中获取的实际应用的 AppID params.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){ // 测速完成后,会回调出测速结果}
测速的结果(TRTCSpeedTestResult)包含如下几个字段:
| 字段 | 含义 | 含义说明 |
| success | 是否成功 | 本次测试是否成功 |
| errMsg | 错误信息 | 带宽测试的详细错误信息 |
| ip | 服务器 IP | 测速服务器的 IP |
| quality | 网络质量评分 | 通过评估算法测算出的网络质量,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 进行监听并在界面上做相应地提示即可:
AndroidiOS&MacWindows// 监听 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 quality switch(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 users for (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 quality switch (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 users for (int i = 0; i < remote_quality_count; ++i) { printf("remote user : = %s, quality = %d", remote_quality[i].userId, remote_quality[i].quality); }}
六、onStatistics 监听完整网络质量
onStatistics 统计回调事件,它会每间隔2秒抛出一次,用于通知 SDK 内部音频、视频以及网络相关的专业技术指标,这些信息在 TRTCStatistics 均有罗列:
·视频统计信息:视频的分辨率(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% 丢失在传输链路中。 |
AndroidiOS&MacWindows@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++) { // 远端用户userid NSLog(@"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++) { // 远端用户userid printf("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 恢复回调。
AndroidiOS&MacWindows@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");}