全部产品
Search
文档中心

视频直播:通话中质量监测

更新时间:Feb 28, 2026

本文介绍如何在通话中监测网络、音频、视频的质量。

功能介绍

在通话过程中,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) {}