All Products
Search
Document Center

ApsaraVideo Live:In-Call Quality Monitoring

Last Updated:Mar 01, 2026

This topic explains how to monitor network, audio, and video quality during a call.

Function Introduction

During a call, the RTC SDK triggers callbacks related to call and live streaming quality. Use these callbacks to assess user interaction experience, troubleshoot issues, and optimize user experience. The SDK also triggers callbacks when a user’s network status changes, reporting the latest status.

Sample Code

In-call quality monitoring for Android: Android/ARTCExample/BasicUsage/src/main/java/com/aliyun/artc/api/basicusage/StreamMonitoring/StreamMonitoringActivity.java.

In-call quality monitoring for iOS: iOS/ARTCExample/BasicUsage/StreamMonitoring/StreamMonitoringVC.swift.

Prerequisites

Before you configure video settings, meet the following requirements:

Implementation

Note

This document uses Android as an example. Interface names may differ across platforms. For details, refer to the sample code.

Uplink and Downlink Network Quality Reports

When network quality changes, users can obtain uplink and downlink network quality information through the onNetworkQualityChanged callback.

Note

This feature is not supported on Linux.

  • uid: User ID. If empty, it represents the local user’s uplink and downlink network status.

  • upQuality: Uplink network status.

  • downQuality: Downlink network status.

The network quality type AliRtcNetworkQuality is defined as follows:

Enumeration Value

Description

AliRtcNetworkExcellent (0)

Excellent network quality.

AliRtcNetworkGood (1)

Good network quality.

AliRtcNetworkPoor (2)

Poor network quality.

AliRtcNetworkBad (3)

Bad network quality.

AliRtcNetworkVeryBad (4)

Very bad network quality.

AliRtcNetworkDisconnected (5)

Network disconnected.

AliRtcNetworkUnknow (6)

Unknown network quality.

Statistics Reports

The onAliRtcStats callback triggers every 2 seconds. It provides real-time feedback on statistics such as data transmission, CPU usage, call duration, and packet loss rate. For more information about metrics, see AliRtcStats.

Audio Quality Reports

Local Audio Stream Statistics

The onRtcLocalAudioStats callback provides real-time statistics for the local audio sender. This callback triggers every 2 seconds. Developers can use it to monitor audio quality, network conditions, and stream ingest status. Key metrics include the following:

  • sentSamplerate: Sample rate (Hz), such as 44100 or 48000. Indicates the sampling frequency of the current audio frame and affects audio clarity.

  • numChannel: Number of sound channels, such as 1 (mono) or 2 (stereo). Affects spatial perception and transmission overhead.

  • sentBitrate: Sent bitrate (kbps). Indicates the amount of encoded audio data sent per second. Higher bitrates improve audio quality but increase bandwidth usage.

For more metrics and descriptions, see AliRtcLocalAudioStats.

Audio Subscription Data Statistics

The onRtcRemoteAudioStats callback provides real-time statistics for remote users’ audio streams. This callback triggers every 2 seconds and delivers detailed data about audio quality, network conditions, and playback performance. Key metrics include the following:

  • quality:

    • 0: Unknown — Quality unknown.

    • 1: Excellent — Excellent quality.

    • 2: Good — Good quality with slightly lower bitrate.

    • 3: Poor: The user experience is degraded, but communication is not affected.

    • 4: Bad — Poor quality; communication is disrupted.

    • 5: Very Bad — Very poor quality; communication is nearly impossible.

    • 6: NetworkDisconnected — Network disconnected; no communication possible.

  • audioTotalFrozenRate: Audio playback stuttering rate since joining the channel.

  • network_transport_delay: Network latency from sender to receiver (ms).

For more metrics and descriptions, see AliRtcRemoteAudioStats.

Video Quality Reports

Local Video Stream Statistics

The onRtcLocalVideoStats callback provides real-time statistics for the local video sender. This callback triggers every 2 seconds. Developers can use it to monitor video encoding quality, network conditions, and stream ingest status. Key metrics include the following:

  • actualEncodeBitrate: Actual encoding bitrate used during encoding.

  • sentBitrate: Sent bitrate (kbps). Indicates the actual video bitrate sent over the network.

  • captureFps, encodeFps, sentFps: Frame rates for video capture, encoding, and sending.

  • avgQp: Average Quantization Parameter (QP) per second. Reflects video encoding quality. Lower QP values indicate lower compression.

For more metrics and descriptions, see AliRtcLocalVideoStats.

Video Subscriptions Data Statistics

The onRtcRemoteVideoStats callback provides real-time statistics for remote users’ video streams. This callback triggers every 2 seconds and delivers detailed data about video quality, network conditions, and playback performance. Key metrics include the following:

  • width, height: Video resolution.

  • decodeFps, renderFps: Frame rates for video decoding and rendering.

  • videoTotalFrozenRate: Cumulative video playback stuttering rate.

For more metrics and descriptions, see AliRtcRemoteVideoStats.

Android

// Uplink and downlink network quality
private AliRtcEngineEventListener mRtcEngineEventListener = new AliRtcEngineEventListener() {
    // Network quality change
    @Override
    public void onNetworkQualityChanged(String uid, AliRtcEngine.AliRtcNetworkQuality upQuality, AliRtcEngine.AliRtcNetworkQuality downQuality){
        super.onNetworkQualityChanged(uid, upQuality, downQuality);
        // TODO: Add business logic
    }
}
// Statistics callback
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;
        // Update display for all statistics
        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

/* Local video status */
void CTutorialDlg::OnLocalVideoStats(const AliEngineLocalVideoStats& localVideoStats)
{
	if (m_pRtcStatsDlg != nullptr) {
		m_pRtcStatsDlg->setLocalVideoStats(localVideoStats);
	}
}

/* Local audio status */
void CTutorialDlg::OnLocalAudioStats(const AliEngineLocalAudioStats& localVideoStats)
{
	if (nullptr != m_pRtcStatsDlg)
	{
		m_pRtcStatsDlg->setLocalAudioStats(localVideoStats);
	}
}

/* Network status */
void CTutorialDlg::OnNetworkQualityChanged(const char *uid,
	AliEngineNetworkQuality upQuality,
	AliEngineNetworkQuality downQuality)
{
    // Handle network quality change
}


/* Remote audio status */
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);

    /* Write to status map. UI reads and displays it via timer. */
	m_MuteUserAudioStats.Lock();
	m_UserAudioStatsMap[strid] = strText;
	m_MuteUserAudioStats.Unlock();
}

/* Remote video status */
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;

    /* Write to status map. UI reads and displays it via timer. */
	m_MutexUserVideoStats.Lock();
	m_UserVideoStatsMap[strid] = strStats;
	m_MutexUserVideoStats.Unlock();
}


/* Engine global status */
void CTutorialDlg::OnStats(const AliEngineStats& stats)
{
	AliEngineStats* pStats = new AliEngineStats;
	if (pStats == nullptr)
	{
		return;
	}

    /* Global status can also be sent to the UI module for display. */
	*pStats = stats;
	PostMessage(MM_UPDATE_STATS, (WPARAM)pStats, 0);
}

Linux

Implement the callback interfaces in the EngineEventHandlerInterface class.

/**
 * @brief Session statistics callback
 * @param stats Session statistics
 * @note SDK triggers this callback every two seconds.
 */
virtual void OnStats(const AliRTCSdk::Linux::AliEngineStats& stats) {}

/**
 * @brief Local video statistics
 * @param localVideoStats Local video statistics
 * @note SDK triggers this callback every two seconds.
 */
virtual void OnLocalVideoStats(const AliRTCSdk::Linux::AliEngineLocalVideoStats& localVideoStats) {}

/**
 * @brief Remote video statistics
 * @param remoteVideoStats Remote video statistics
 * @note SDK triggers this callback every two seconds.
 */
virtual void OnRemoteVideoStats(const AliRTCSdk::Linux::AliEngineRemoteVideoStats& remoteVideoStats) {}

/**
 * @brief Local audio statistics
 * @param localAudioStats Local audio statistics
 * @note SDK triggers this callback every two seconds.
 */
virtual void OnLocalAudioStats(const AliRTCSdk::Linux::AliEngineLocalAudioStats& localAudioStats) {}

/**
 * @brief Remote audio statistics
 * @param remoteAudioStats Remote audio statistics
 * @note SDK triggers this callback every two seconds.
 */
virtual void OnRemoteAudioStats(const AliRTCSdk::Linux::AliEngineRemoteAudioStats& remoteAudioStats) {}