In-Call Quality Monitoring

Updated at:
Copy as MD

Monitor network, audio, and video quality during a call to troubleshoot issues and optimize user experience.

Function Introduction

During a call, the RTC SDK triggers callbacks related to call and live streaming quality. You can use these callbacks to assess interaction quality, troubleshoot issues, and optimize user experience. The SDK also reports network status changes through dedicated callbacks.

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 begin, make sure the following requirements are met:

Implementation

Note

The following examples use Android. Interface names may differ across platforms. For details, refer to the sample code.

Uplink and Downlink Network Quality Reports

When network quality changes, the onNetworkQualityChanged callback reports uplink and downlink network quality.

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, providing real-time statistics on 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 triggers every 2 seconds, providing real-time statistics for the local audio sender, including 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 triggers every 2 seconds, providing real-time statistics for remote audio streams, including 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 triggers every 2 seconds, providing real-time statistics for the local video sender, including 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 triggers every 2 seconds, providing real-time statistics for remote video streams, including 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) {}