全部產品
Search
文件中心

ApsaraVideo Live:通話中品質監測

更新時間:Mar 01, 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:使用者識別碼,如果為空白表示本機使用者上下行網路狀態。

  • 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) {}