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:
You have a valid Alibaba Cloud account and have created an ApsaraVideo Real-time Communication application. For more information, see Create Application. Obtain your App ID and App Key from the Application Management Console.
You have integrated the ARTC SDK into your project and implemented basic real-time audio and video features. For SDK integration, see SDK Download and Integration. For audio and video implementation, see Implement Audio and Video Calls.
Implementation
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.
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) {}