本文介绍如何在通话中监测网络、音频、视频的质量。
功能介绍
在通话过程中,RTC SDK会触发与通话和直播质量相关的回调。从这些回调中,可以了解用户的互动体验,进行问题排查和用户体验的优化。此外,当用户网络状态发生变化时,SDK也会触发相关回调,上报最新的状态。
示例代码
Android端通话中质量监测:Android/ARTCExample/BasicUsage/src/main/java/com/aliyun/artc/api/basicusage/StreamMonitoring/StreamMonitoringActivity.java。
iOS端通话中质量监测:iOS/ARTCExample/BasicUsage/StreamMonitoring/StreamMonitoringVC.swift。
前提条件
在设置视频配置之前,请确保达成以下条件:
功能实现
本文档以 Android 平台为例,不同平台接口命名可能不同,具体请参考代码示例。
上下行网络质量报告
当网络质量发生变化时,用户可以通过onNetworkQualityChanged回调获取上下行网络质量信息。
Linux 平台暂时不支持该功能。
uid:用户 ID,如果为空表示本地用户上下行网络状态。
upQuality:上行网络状态。
downQuality:下行网络状态。
网络质量类型AliRtcNetworkQuality如下:
枚举值 | 描述 |
AliRtcNetworkExcellent(0) | 网络极好。 |
AliRtcNetworkGood(1) | 网络好。 |
AliRtcNetworkPoor(2) | 网络不好。 |
AliRtcNetworkBad(3) | 网络差。 |
AliRtcNetworkVeryBad(4) | 网络极差。 |
AliRtcNetworkDisconnected(5) | 网络断开。 |
AliRtcNetworkUnknow(6) | 网络质量未知。 |
统计信息报告
onAliRtcStats回调每2秒触发一次,用于实时反馈通话过程中的各种统计数据,包括数据传输统计数据、CPU使用、通话时长、丢包率等。详细指标介绍请参考AliRtcStats。
音频质量报告
音频推流数据统计
onRtcLocalAudioStats回调主要用于实时反馈本地音频发送端的统计信息。该回调每 2s 触发一次,开发者可通过此接口监控音频质量、网络状况和推流状态等数据。返回的部分指标信息如下:
sentSamplerate:采样率(单位:Hz),例如44100、48000。表示当前音频帧的采样频率,影响音质清晰度。
numChannel:声道数,如1(单声道)、2(立体声)。决定音频空间感和传输开销。
sentBitrate:发送码率(单位:kbps),表示当前音频编码后每秒发送的数据量。码率越高音质越好,但带宽占用越大。
更多统计指标及其介绍请参考AliRtcLocalAudioStats。
音频订阅数据统计
onRtcRemoteAudioStats回调主要用于实时反馈远端用户的音频流的统计信息。该回调每隔 2s 触发一次,提供关于音频质量、网络状况和播放性能的详细数据。下面为部分指标信息:
quality:
0:Unknow,质量未知。
1:Excellent,质量极好。
2:Good,质量好,码率略低。
3:Poor,用户主观感受有瑕疵,但不影响沟通。
4:Bad,质量差,沟通不顺畅。
5:Very Bad,质量极差,基本不能沟通。
6:NetworkDisconnected,网络断开,完全无法沟通。
audioTotalFrozenRate:自加入频道音频播放卡顿率。
network_transport_delay:发送端到接收端的网络延时(ms)。
更多指标及详细介绍请参考AliRtcRemoteAudioStats。
视频质量报告
视频推流数据统计
onRtcLocalVideoStats回调主要用于实时反馈本地视频发送端的统计信息。该回调每 2s 触发一次,开发者可通过此接口监控视频编码质量、网络状况、推流状态等数据。返回的部分指标信息如下:
actualEncodeBitrate:实际编码码率,表示实际编码过程中使用的码率。
sentBitrate:发送码率(单位:kbps),表示实际通过网络发送的视频码率。
captureFps、encodeFps、sentFps:视频采集、编码、发送帧率。
avgQp:每秒平均QP(Quantization Parameter),反映视频编码质量。通常 QP 值越小,压缩率越低。
更多统计指标及其介绍请参考AliRtcLocalVideoStats。
视频订阅数据统计
onRtcRemoteVideoStats回调主要用于实时反馈远端用户的视频流的统计信息。该回调每隔 2s 触发一次,提供关于视频质量、网络状况和播放性能的详细数据。下面为部分指标信息:
width、height:视频分辨率。
decodeFps、renderFps:视频解码、渲染的帧率。
videoTotalFrozenRate:视频播放的累计卡顿率。
更多指标及详细介绍请参考AliRtcRemoteVideoStats。
Android
// 上下行网络质量
private AliRtcEngineEventListener mRtcEngineEventListener = new AliRtcEngineEventListener() {
// 网络质量变化
@Override
public void onNetworkQualityChanged(String uid, AliRtcEngine.AliRtcNetworkQuality upQuality, AliRtcEngine.AliRtcNetworkQuality downQuality){
super.onNetworkQualityChanged(uid, upQuality, downQuality);
// TODO: 添加业务逻辑
}
}
// 统计数据回调
private AliRtcEngineNotify mRtcEngineNotify = new AliRtcEngineNotify() {
@Override
public void onAliRtcStats(AliRtcEngine.AliRtcStats stats){
super.onAliRtcStats(stats);
mRtcStats = stats;
updateStatsDisplay();
}
@Override
public void onRtcLocalAudioStats(AliRtcEngine.AliRtcLocalAudioStats aliRtcStats){
super.onRtcLocalAudioStats(aliRtcStats);
mLocalAudioStats = aliRtcStats;
}
@Override
public void onRtcRemoteAudioStats(AliRtcEngine.AliRtcRemoteAudioStats aliRtcStats){
super.onRtcRemoteAudioStats(aliRtcStats);
mRemoteAudioStats = aliRtcStats;
}
@Override
public void onRtcLocalVideoStats(AliRtcEngine.AliRtcLocalVideoStats aliRtcLocalVideoStats){
super.onRtcLocalVideoStats(aliRtcLocalVideoStats);
mLocalVideoStats = aliRtcLocalVideoStats;
}
@Override
public void onRtcRemoteVideoStats(AliRtcEngine.AliRtcRemoteVideoStats aliRtcRemoteVideoStats){
super.onRtcRemoteVideoStats(aliRtcRemoteVideoStats);
mRemoteVideoStats = aliRtcRemoteVideoStats;
// 更新显示所有统计信息
updateStatsDisplay();
}
}iOS
extension StreamMonitoringMainVC: AliRtcEngineDelegate {
// MARK: Network Quality
func onNetworkQualityChanged(_ uid: String, up upQuality: AliRtcNetworkQuality, downNetworkQuality downQuality: AliRtcNetworkQuality) {
}
// MARK: Stream Monitoring Callback
func onRtcStats(_ stats: AliRtcStats) {
}
func onRtcLocalAudioStats(_ localAudioStats: AliRtcLocalAudioStats) {
}
func onRtcRemoteAudioStats(_ remoteAudioStats: AliRtcRemoteAudioStats) {
}
func onRtcLocalVideoStats(_ localVideoStats: AliRtcLocalVideoStats) {
}
func onRtcRemoteVideoStats(_ remoteVideoStats: AliRtcRemoteVideoStats) {
}
}Mac
- (void)onNetworkQualityChanged:(NSString *)uid upNetworkQuality:(AliRtcNetworkQuality)upQuality downNetworkQuality:(AliRtcNetworkQuality)downQuality{
NSLog(@"onNetworkQualityChanged callback!");
}
- (void)onRtcStats:(AliRtcStats)stats{
/* */
NSLog(@"onRtcStats callback!");
}
- (void)onRtcLocalAudioStats:(AliRtcLocalAudioStats *)localAudioStats {
NSLog(@"onRtcLocalAudioStats callback!");
}
- (void)onRtcRemoteAudioStats:(AliRtcRemoteAudioStats *)remoteAudioStats {
NSLog(@"onRtcRemoteAudioStats callback!");
}
- (void)onRtcLocalVideoStats:(AliRtcLocalVideoStats *)localVideoStats{
NSLog(@"onRtcLocalVideoStats callback!");
}
- (void)onRtcRemoteVideoStats:(AliRtcRemoteVideoStats *)remoteVideoStats{
NSLog(@"onRtcRemoteVideoStats callback!");
}
Windows
/* 本地视频状态 */
void CTutorialDlg::OnLocalVideoStats(const AliEngineLocalVideoStats& localVideoStats)
{
if (m_pRtcStatsDlg != nullptr) {
m_pRtcStatsDlg->setLocalVideoStats(localVideoStats);
}
}
/* 本地音频状态 */
void CTutorialDlg::OnLocalAudioStats(const AliEngineLocalAudioStats& localVideoStats)
{
if (nullptr != m_pRtcStatsDlg)
{
m_pRtcStatsDlg->setLocalAudioStats(localVideoStats);
}
}
/* 网络状态 */
void CTutorialDlg::OnNetworkQualityChanged(const char *uid,
AliEngineNetworkQuality upQuality,
AliEngineNetworkQuality downQuality)
{
// 处理网络质量变化
}
/* 远端音频状态 */
void CTutorialDlg::OnRemoteAudioStats(const AliEngineRemoteAudioStats& remoteAudioStats)
{
CString strText, strTemp;
switch (remoteAudioStats.track)
{
case AliEngineAudioTrackMic:
strText = _T("AudioTrackMic");
break;
default:
strText = _T("AudioTrackNo");
break;
}
strText += _T(": { ");
strTemp.Format(_T("quality: %d,"), remoteAudioStats.quality);
strText += strTemp;
strTemp.Format(_T("audio_loss_rate: %d,"), remoteAudioStats.audioLossRate);
strText += strTemp;
strTemp.Format(_T("rcvd_bitrate: %d,"), remoteAudioStats.rcvdBitrate);
strText += strTemp;
strTemp.Format(_T("total_frozen_times: %d,"), remoteAudioStats.totalFrozenTimes);
strText += strTemp;
strTemp.Format(_T("network_transport_delay: %d,"), remoteAudioStats.networkTransportDelay);
strText += strTemp;
strTemp.Format(_T("jitter_buffer_delay: %d }\n"), remoteAudioStats.jitterBufferDelay);
strText += strTemp;
CString strid = AliStringToCString(remoteAudioStats.userId);
/* 写到状态map中,UI通过定时器读取显示 */
m_MuteUserAudioStats.Lock();
m_UserAudioStatsMap[strid] = strText;
m_MuteUserAudioStats.Unlock();
}
/* 远端视频状态 */
void CTutorialDlg::OnRemoteVideoStats(const AliEngineRemoteVideoStats& remoteVideoStats)
{
CString strText, strTemp, strStats;
CString strid = AliStringToCString(remoteVideoStats.userId);
auto iter = m_UserVideoStatsMap.find(strid);
if (iter != m_UserVideoStatsMap.end())
{
strStats = iter->second;
}
switch (remoteVideoStats.track)
{
case AliEngineVideoTrackCamera:
strText = _T("TrackCamera");
break;
case AliEngineVideoTrackScreen:
strText = _T("TrackScreen");
break;
case AliEngineVideoTrackBoth:
strText = _T("TrackBoth");
break;
default:
strText = _T("TrackNo");
break;
}
delRepeatTrackStatic(strStats, strText);
strText += _T(": { ");
strTemp.Format(_T("Width: %d,"), remoteVideoStats.width);
strText += strTemp;
strTemp.Format(_T("Height: %d,"), remoteVideoStats.height);
strText += strTemp;
strTemp.Format(_T("Decode_fps: %d,"), remoteVideoStats.decodeFps);
strText += strTemp;
strTemp.Format(_T("Render_fps: %d,"), remoteVideoStats.renderFps);
strText += strTemp;
strTemp.Format(_T("Frozen_times: %d }\n"), remoteVideoStats.frozenTimes);
strText += strTemp;
strStats += strText;
/* 写到状态map中,UI通过定时器读取显示 */
m_MutexUserVideoStats.Lock();
m_UserVideoStatsMap[strid] = strStats;
m_MutexUserVideoStats.Unlock();
}
/* 引擎全局状态 */
void CTutorialDlg::OnStats(const AliEngineStats& stats)
{
AliEngineStats* pStats = new AliEngineStats;
if (pStats == nullptr)
{
return;
}
/* 全局状态也可以通过消息发送给UI模块显示 */
*pStats = stats;
PostMessage(MM_UPDATE_STATS, (WPARAM)pStats, 0);
}Linux
实现EngineEventHandlerInterface类中回调接口。
/**
* @brief 当前会话统计信息回调
* @param stats 会话统计信息
* @note SDK每两秒触发一次此统计信息回调
*/
virtual void OnStats(const AliRTCSdk::Linux::AliEngineStats& stats) {}
/**
* @brief 本地视频统计信息
* @param localVideoStats 本地视频统计信息
* @note SDK每两秒触发一次此统计信息回调
*/
virtual void OnLocalVideoStats(const AliRTCSdk::Linux::AliEngineLocalVideoStats& localVideoStats) {}
/**
* @brief 远端视频统计信息
* @param remoteVideoStats 远端视频统计信息
* @note SDK每两秒触发一次此统计信息回调
*/
virtual void OnRemoteVideoStats(const AliRTCSdk::Linux::AliEngineRemoteVideoStats& remoteVideoStats) {}
/**
* @brief 本地音频统计信息
* @param localAudioStats 本地视频统计信息
* @note SDK每两秒触发一次此统计信息回调
*/
virtual void OnLocalAudioStats(const AliRTCSdk::Linux::AliEngineLocalAudioStats& localAudioStats) {}
/**
* @brief 远端音频统计信息
* @param remoteAudioStats 远端视频统计信息
* @note SDK每两秒触发一次此统计信息回调
*/
virtual void OnRemoteAudioStats(const AliRTCSdk::Linux::AliEngineRemoteAudioStats& remoteAudioStats) {}