网络质量监控

最近更新时间:2024-10-31 17:10:41

我的收藏
为了让用户更好地感知网络质量,建议业务在通话前进行测速,通话中监测网络质量和连接情况,并提供相应的 UI 界面交互与提示。这样可以方便用户评估当前网络质量,并及时调整网络设置,以达到最佳通话效果。

通话前网络测速

普通用户很难去感知网络情况,建议在开播前进行测速,以便于评估当前网络质量,及时调整网络以达到通话的最优的效果。
测速的原理是 SDK 向服务器节点发送一批探测包,然后统计回包的质量,并将测速的结果通过回调接口进行通知,如下图所示:



通话前测速的优势:
测速结果将用于优化 SDK 的服务器选择策略,因此建议在用户首次通话前进行一次测速,以帮助我们选择最佳服务器。
如果测试结果非常不理想,您可以通过显眼的 UI 提示用户选择更好的网络环境。

startSpeedTest 测速

Android
iOS
Windows
//启动网络测速的示例代码, 需要 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 进行监听并在界面上做相应地提示即可:
Android
iOS&Mac
Windows
// 监听 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% 丢失在传输链路中。
Android
iOS&Mac
Windows
@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 恢复回调。
Android
iOS&Mac
Windows
@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");
}