通话中质量监测
在通话过程中,RTC SDK 会触发与通话和直播质量相关的回调。从这些回调中,你可以了解用户的互动体验,进行问题排查和优化用户体验。
本文介绍这些回调的详细信息以及回调的使用方法。
技术原理
用户加入频道后,SDK 会每隔 2 秒触发一系列回调,上报以下信息:实时互动统计信息、本地和远端音视频流的统计信息。
当用户的音频或视频状态发生变化时,SDK 会触发回调,上报最新的音频或视频状态,以及状态变化的原因。
统计信息报告
onRtcStats
回调每 2 秒触发一次,上报本地通话统计信息。你可以了解到通话时长、当前通话频道中的人数、当前系统的 CPU 使用率、当前 App 的 CPU 使用率等重要参数。
音频质量报告
音频质量报告包括本地音频流统计信息报告、本地音频流状态监控、远端音频流统计信息报告和远端音频流状态监控。
本地音频流统计信息报告
onLocalAudioStats
回调每 2 秒触发一次,上报本地设备发送音频流的统计信息。你可以了解到以下重要统计信息:
- 当前通话声道数
- 发送音频的采样率
- 发送音频码率的平均值
本地音频流状态监控
本地音频的状态发生改变时,如音频录制状态和音频编码状态发生改变,SDK 会触发 onLocalAudioStateChanged
回调上报当前本地音频状态。当本地音频出现故障时,你可以通过错误码排查问题。
远端音频流统计信息报告
下图展示 App 客户端之间的音频传输过程:
onRemoteAudioStats
回调每 2 秒触发一次,上报当前通话中每个远端用户(COMMUNICATION
场景)或远端主播(LIVE_BROADCASTING
场景)音频流的统计信息,包括远端发送的音频流质量(详见质量打分)、声道数等重要参数信息。
onRemoteAudioStats
侧重报告远端音频流的全链路音频质量,更贴近你的主观感受。即使网络发生丢包,但由于 FEC(Forward Error Correction)、重传恢复和带宽估计等抗丢包和拥塞控制技术,最终在接收端的音频丢帧率也可能不高,所以你感知到的音频质量较好。
远端音频流状态监控
远端用户(COMMUNICATION
场景)或远端主播(LIVE_BROADCASTING
场景)的音频流状态改变时,SDK 会触发 onRemoteAudioStateChanged
回调上报远端音频流当前状态和状态改变的原因。
视频质量报告
视频质量报告包括本地视频流统计信息报告、本地视频流状态监控、远端视频流统计信息报告和远端视频流状态监控。
本地视频流统计信息报告
onLocalVideoStats
回调上报本地设备发送视频流的统计信息,包括视频编码宽/高、目标编码率、发送视频的码率等重要参数信息。
如果你此前调用 enableDualStreamMode
方法开启了双流模式,则本回调描述本地设备发送的视频大流的统计信息。
本地视频流状态监控
本地视频的状态发生改变时,SDK 会触发 onLocalVideoStateChanged
回调上报当前本地视频状态。当本地视频出现故障时,你可以通过错误码排查问题。
远端视频流统计信息报告
下图展示 App 客户端之间的视频传输过程:
onRemoteVideoStats
回调上报当前通话中每个远端用户(COMMUNICATION
场景)或远端主播(LIVE_BROADCASTING
场景)视频流的统计信息。你可以了解到每个远端用户或远端主播的视频宽/高、接收视频的码率等重要参数信息。
远端视频流状态监控
远端用户(COMMUNICATION
场景)或远端主播(LIVE_BROADCASTING
场景)的视频流状态改变时,SDK 会触发 onRemoteVideoStateChanged
回调上报远端视频流当前状态和状态改变的原因。
前提条件
请确保你的项目中已实现了基本的实时音视频功能。详见实现音视频互动。
实现方法
在 IRtcEngineEventHandler
中,实现以下实时互动质量统计信息回调和音频或视频状态监控回调,从而了解用户互动体验:
onRtcStats
:报告实时互动统计信息。onLocalAudioStats
:报告发送的音频流的统计信息。onLocalAudioStateChanged
: 报告本地音频流状态监控。onRemoteAudioStats
:报告接收的远端音频流统计信息。onRemoteAudioStateChanged
:报告远端音频流状态监控。onLocalVideoStats
:报告发送的视频流统计信息。onLocalVideoStateChanged
:报告本地视频流状态监控。onRemoteVideoStats
:报告接收的远端视频流统计信息。onRemoteVideoStateChanged
:报告远端视频流状态监控。
LRESULT CAgoraReportInCallDlg::OnEIDRemoteVideoStateChanged(WPARAM wParam,
LPARAM lParam) {
PVideoStateStateChanged stateChanged = (PVideoStateStateChanged)wParam;
if (stateChanged) {
// 实现 onRemoteVideoStateChanged
CString strSateInfo;
switch (stateChanged->state) {
case REMOTE_VIDEO_STATE_STARTING:
strSateInfo = _T("REMOTE_VIDEO_STATE_STARTING");
break;
case REMOTE_VIDEO_STATE_STOPPED:
strSateInfo = _T("strSateInfo");
break;
case REMOTE_VIDEO_STATE_DECODING:
strSateInfo = _T("REMOTE_VIDEO_STATE_DECODING");
break;
case REMOTE_VIDEO_STATE_FAILED:
strSateInfo = _T("REMOTE_VIDEO_STATE_FAILED ");
break;
case REMOTE_VIDEO_STATE_FROZEN:
strSateInfo = _T("REMOTE_VIDEO_STATE_FROZEN ");
break;
}
CString strInfo;
strInfo.Format(_T("onRemoteVideoStateChanged: uid=%u, %s"),
stateChanged->uid, strSateInfo);
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
}
return 0;
}
// 实现 onRemoteVideoStats
LRESULT CAgoraReportInCallDlg::OnEIDRemoteVideoStats(WPARAM wParam,
LPARAM lParam) {
RemoteVideoStats *p = reinterpret_cast<RemoteVideoStats *>(wParam);
if (p) {
CString tmp;
tmp.Format(_T("%dms"), p->delay);
m_staVideoNetWorkDelayVal.SetWindowText(tmp);
tmp.Format(_T("%dKbps"), p->receivedBitrate);
m_staVideoRecvBitrateVal.SetWindowText(tmp);
delete p;
}
return TRUE;
}
// 实现 onRemoteAudioStats
LRESULT CAgoraReportInCallDlg::OnEIDRemoteAudioStats(WPARAM wParam,
LPARAM lParam) {
RemoteAudioStats *p = reinterpret_cast<RemoteAudioStats *>(wParam);
if (p) {
CString tmp;
tmp.Format(_T("%dms"), p->networkTransportDelay);
m_staAudioNetWorkDelayVal.SetWindowText(tmp);
tmp.Format(_T("%dKbps"), p->receivedBitrate);
m_staAudioRecvBitrateVal.SetWindowText(tmp);
delete p;
}
return TRUE;
}
// 实现 onRtcStats
LRESULT CAgoraReportInCallDlg::OnEIDRtcStats(WPARAM wParam, LPARAM lParam) {
RtcStats *p = reinterpret_cast<RtcStats *>(wParam);
if (p) {
CString tmp;
tmp.Format(_T("%dKbps/%dKbps"), p->txKBitRate, p->rxKBitRate);
m_staTotalBitrateVal.SetWindowText(tmp);
tmp.Format(_T("%.2fMB/%.2fMB"),
p->txBytes ? p->txBytes / 1024.0 / 1024 : 0,
p->rxBytes ? p->rxBytes / 1024.0 / 1024 : 0);
m_staTotalBytesVal.SetWindowText(tmp);
delete p;
}
return TRUE;
}
// 实现 onLocalVideoStats
LRESULT CAgoraReportInCallDlg::OnEIDLocalVideoStats(WPARAM wParam,
LPARAM lParam) {
LocalVideoStats *p = reinterpret_cast<LocalVideoStats *>(wParam);
if (p) {
CString tmp;
tmp.Format(_T("%d fps"), p->sentFrameRate);
m_staLocalVideoFPSVal.SetWindowText(tmp);
tmp.Format(_T("%d X %d"), p->encodedFrameWidth, p->encodedFrameHeight);
m_staLocalVideoResoultionVal.SetWindowText(tmp);
delete p;
}
return TRUE;
}
参考信息
API 参考
onRtcStats
onLocalAudioStats
onLocalAudioStateChanged
onRemoteAudioStats
onRemoteAudioStateChanged
onLocalVideoStats
onLocalVideoStateChanged
onRemoteVideoStats
onRemoteVideoStateChanged
质量打分
代码 | 描述 |
---|---|
QUALITY_UNKNOWN (0) | 网络质量未知。 |
QUALITY_EXCELLENT (1) | 网络质量极好。 |
QUALITY_GOOD (2) | 用户主观感觉和 QUALITY_EXCELLENT (1) 差不多,但码率可能略低于 QUALITY_EXCELLENT (1)。 |
QUALITY_POOR (3) | 用户主观感受有瑕疵但不影响沟通。 |
QUALITY_BAD (4) | 勉强能沟通但不顺畅。 |
QUALITY_VBAD (5) | 网络质量非常差,基本不能沟通。 |
QUALITY_DOWN (6) | 完全无法沟通。 |