logo
Video Call
On this page

Call Quality Monitoring

2026-03-05

Feature Overview

During calls using ZEGO Express SDK, users may sometimes experience poor network conditions. At this time, you can understand changes in current call network quality and audio/video information through relevant callbacks.

For example, when conducting multi-person audio and video calls or multi-person singing, if we need to display user network quality in real-time, we can refer to this document to implement corresponding functions.

Example Source Code

Please refer to Download Example Source Code to get the source code.

For related source code, please check the files in the "/ZegoExpressExample/AdvancedStreaming/src/main/java/im/zego/streammonitoring" directory.

Prerequisites

Before monitoring call quality, please ensure:

Basic Network Quality Report

You can receive upstream and downstream network quality for users (including yourself) by listening to the onNetworkQuality callback. This callback is received every two seconds. For network quality levels, please refer to ZegoStreamQualityLevel.

Different versions of onNetworkQuality have different callback logic:

VersionCallback Logic
2.22.0 and aboveBased on the onNetworkQuality interface callback logic of 2.14.0 ~ 2.21.1, it can also estimate the network situation of remote publishing users. If the remote publishing user's heartbeat is lost once, the callback network quality is unknown; if the remote publishing user's heartbeat is lost 3 times, the callback network quality is die.
2.14.0 ~ 2.21.1
  • As long as you publish or play stream, you can receive your own network quality callback.  
  • When you play audio and video streams published by other users and that user is in your Room, you will receive that user's network quality callback. 
  • When "userID" is "" (empty string), it represents this time is your own network quality; when "userID" is not "" (empty string), it represents a report from other users in the Room.
2.10.0 ~ 2.13.1
  • You must both publish and play stream to receive your own network quality callback. 
  • When you play a stream, the user publishing that stream must be in the same Room and also playing stream, for you to receive that user's network quality callback.
  • When "userID" is "" (empty string), it represents this time is your own network quality; when "userID" is not "" (empty string), it represents a report from other users in the Room.
Warning

onNetworkQuality is not applicable to live streaming scenarios using CDN. You can refer to Advanced Quality Report - Publish Stream Quality Report to monitor CDN publish stream quality.

public void setEngineEventHandler(){
        engine.setEventHandler(new IZegoEventHandler() {
            @Override
            public void onNetworkQuality(String userID, ZegoStreamQualityLevel upstreamQuality, ZegoStreamQualityLevel downstreamQuality) {
                super.onNetworkQuality(userID, upstreamQuality, downstreamQuality);
                if (userID == "") {
                    // Represents network quality of local user (local end)
                    //("My upstream network quality is %lu", (unsigned long)upstreamQuality);
                    //("My downstream network quality is %lu", (unsigned long)downstreamQuality);
                } else {
                    //Represents network quality of other users in the Room
                    //("User %s's upstream network quality is %lu", userID, (unsigned long)upstreamQuality);
                    //("User %s's downstream network quality is %lu", userID, (unsigned long)downstreamQuality);
                }

                /*
                ZegoStreamQualityLevel.EXCELLENT, Network quality excellent
                ZegoStreamQualityLevel.GOOD, Network quality good
                ZegoStreamQualityLevel.MEDIUM, Network quality normal
                ZegoStreamQualityLevel.BAD, Network quality poor
                ZegoStreamQualityLevel.DIE, Network abnormal
                ZegoStreamQualityLevel.UNKNOWN, Network quality unknown
                */

            }
        });

    }

Advanced Quality Report

If the above basic network quality report cannot meet your needs, ZEGO also provides more detailed publish stream quality reports, play stream quality reports, and other related information.

Publish Stream Quality Report

The publish stream quality report refers to the quality report describing the process of users publishing audio and video to the ZEGO server, including the frame rate of the audio and video stream during the capture and encoding stages, and the frame rate, bitrate, delay, and packet loss rate of the transmitted (sent) audio and video stream.

You can receive publish stream quality callbacks by registering onPublisherQualityUpdate. This callback is received every three seconds after successful publishing. You can understand the health status of the published audio and video stream in real-time based on the quality (ZegoPublishStreamQuality) parameter.

  • In most cases, you only need to pay attention to the "level" parameter of "quality" to judge the comprehensive quality of the published stream based on the "level" enumeration value. For details, please refer to ZegoStreamQualityLevel.
  • If you want to pay attention to more detailed publish stream quality parameters, you can refer to ZegoPublishStreamQuality.
    engine.setEventHandler(new IZegoEventHandler() {

            // Developers can monitor specific quality in this callback and report to the business server for monitoring, or monitor a certain field of the quality object to give user-friendly prompts
            @Override
            public void onPublisherQualityUpdate(String streamID, ZegoPublishStreamQuality quality) {
                String networkQuality = "";
                // level represents the comprehensive score of publish stream quality. In most cases, developers can refer to this score to display upstream network quality

                switch (quality.level) {
                    case EXCELLENT:
                        networkQuality = "Excellent";
                        break;
                    case GOOD:
                        networkQuality = "Good";
                        break;
                    case MEDIUM:
                        networkQuality = "Medium";
                        break;
                    case BAD:
                        networkQuality = "Poor";
                        break;
                    case DIE:
                        networkQuality = "Failed";
                        break;
                    case UNKNOWN:
                        networkQuality = "Unknown";
                        break;
                    default:
                        break;
                }
                //("Network quality is: %s", networkQuality);
            }
        });

Play Stream Quality Report

The play stream quality report refers to the quality report of the process of users playing audio and video streams, including the frame rate, bitrate, delay, and packet loss rate of the received audio and video stream, the frame rate of the audio and video stream during the decoding stage, and the frame rate, freeze rate, and overall audio and video quality during the rendering stage.

You can receive play stream quality callbacks by registering onPlayerQualityUpdate. This callback is received every three seconds after successful playing. Developers can understand the health status of the played audio and video stream in real-time based on the quality (ZegoPlayStreamQuality) parameter.

  • In most cases, you only need to pay attention to the "level" parameter of "quality" to judge the comprehensive quality of the played stream based on the "level" enumeration value. For details, please refer to ZegoStreamQualityLevel.
  • If you want to pay attention to more detailed play stream quality parameters, you can refer to ZegoPlayStreamQuality.
    engine.setEventHandler(new IZegoEventHandler() {
            // Developers can monitor specific quality in this callback and report to the business server for monitoring, or monitor a certain field of the quality object to give user-friendly prompts
            @Override
            public void onPlayerQualityUpdate(String streamID, ZegoPlayStreamQuality quality) {
                String networkQuality = "";
                // level represents the comprehensive score of play stream quality. In most cases, developers can refer to this score to display downstream network quality

                switch (quality.level) {
                    case EXCELLENT:
                        networkQuality = "Excellent";
                        break;
                    case GOOD:
                        networkQuality = "Good";
                        break;
                    case MEDIUM:
                        networkQuality = "Medium";
                        break;
                    case BAD:
                        networkQuality = "Poor";
                        break;
                    case DIE:
                        networkQuality = "Failed";
                        break;
                    case UNKNOWN:
                        networkQuality = "Unknown";
                        break;
                    default:
                        break;
                }
                //("Network quality is: %s", networkQuality);
            }
        });
}

MOS Audio Quality Score

Starting from ZEGO Express SDK version 2.16.0, the play stream quality callback onPlayerQualityUpdate adds a "mos" field, representing the score of play stream audio quality. When developers are concerned about audio quality, they can understand the current audio quality status through this field.

The value range of the mos field is [-1, 5], where -1 means unknown (e.g., unable to score when play stream is abnormal), and [0, 5] means the normal scoring range. The subjective audio quality perception corresponding to real-time audio MOS scoring is as follows:

MOS ValueEvaluation Criteria
4.0 ~ 5.0Audio quality is very good, clear and smooth, can be heard clearly.
3.5 ~ 4.0Audio quality is good, occasional audio quality damage, but still clear and smooth, can be heard clearly.
3.0 ~ 3.5Audio quality is medium, occasional stuttering, requires some attention to hear clearly.
2.5 ~ 3.0Audio quality is poor, frequent stuttering, requires concentrated attention to hear clearly.
2.0 ~ 2.5Audio quality is very poor, some semantic loss, difficult to communicate.
0 ~ 2.0Audio quality is extremely poor, massive semantic loss, unable to communicate.
-1Unknown.

Other Information Monitoring

Publish/Play Stream Status Change Notification

Publish Stream Status Callback

After successful publishing, you can receive notifications of publish stream status changes through onPublisherStateUpdate.

engine.setEventHandler(new IZegoEventHandler() {
    @Override
    public void onPublisherStateUpdate(String streamID, ZegoPublisherState state, int errorCode, JSONObject extendedData) {
        super.onPublisherStateUpdate(streamID, state, errorCode, extendedData);
        // When state is ZEGO_PUBLISHER_STATE_NO_PUBLISH, and errcode is non-zero, it indicates publish stream failed, and no retry will be performed. At this time, a publish stream failure prompt can be displayed on the interface;
        // When state is ZEGO_PUBLISHER_STATE_PUBLISH_REQUESTING, and errcode is non-zero, it indicates retrying publish stream. At this time, if publish stream is not successful within the retry time, a publish stream failure notification will be thrown.
    }
}

You can roughly judge the user's publish stream network situation based on whether the "state" parameter in the callback is in "requesting publish stream status". The values of the "state" parameter correspond to the user's publish stream status as follows:

Enumeration ValueDescription
ZEGO_PUBLISHER_STATE_NO_PUBLISHNo publish stream status, in this state before publishing. If a steady-state exception occurs during the publish stream process, such as incorrect AppID, AppSign, or Token, or if other users are already publishing, publishing a stream with the same stream ID will fail, it will enter the no publish stream status.
ZEGO_PUBLISHER_STATE_PUBLISH_REQUESTINGRequesting publish stream status. After the publish stream operation is successfully executed, it will enter the requesting publish stream status. Usually, this status is used for UI interface display. If an interruption occurs due to poor network quality, the SDK will perform internal retry and also return to the requesting publish stream status.
ZEGO_PUBLISHER_STATE_PUBLISHINGPublishing stream status. Entering this status indicates that publish stream has been successful and users can communicate normally.

The parameter "extendedData" is extended information attached to the status update. If using ZEGO's CDN content distribution network, after successful publishing, the keys of the parameter's content are "flv_url_list", "rtmp_url_list", "hls_url_list", corresponding to the play URLs of flv, rtmp, and hls protocols respectively.

Play Stream Status Change Callback

After successful playing, developers can receive notifications of play stream status changes through onPlayerStateUpdate.

engine.setEventHandler(new IZegoEventHandler() {
    @Override
    public void onPlayerStateUpdate(String streamID, ZegoPlayerState state, int errorCode, JSONObject extendedData) {
        super.onPlayerStateUpdate(streamID, state, errorCode, extendedData);
        // When state is ZEGO_PLAYER_STATE_NO_PLAY, and errcode is non-zero, it indicates play stream failed, and no retry will be performed. At this time, a play stream failure prompt can be displayed on the interface;
        // When state is ZEGO_PLAYER_STATE_PLAY_REQUESTING, and errcode is non-zero, it indicates retrying play stream. At this time, if play stream is not successful within the retry time, a play stream failure notification will be thrown.
    }
}

Developers can roughly judge the user's play stream network situation based on whether the "state" parameter is in "requesting play stream status". The values of the "state" parameter correspond to the user's play stream status as follows:

Enumeration ValueDescription
ZEGO_PLAYER_STATE_NO_PLAYNo play stream status, in this state before playing. If a steady-state exception occurs during the play stream process, such as incorrect AppID, AppSign, or Token, it will enter the no play stream status.
ZEGO_PLAYER_STATE_PLAY_REQUESTINGRequesting play stream status. After the play stream operation is successfully executed, it will enter the requesting play stream status. Usually, this status is used for application interface display. If an interruption occurs due to poor network quality, the SDK will perform internal retry and also return to the requesting play stream status.
ZEGO_PLAYER_STATE_PLAYINGPlaying stream status. Entering this status indicates that play stream has been successful and users can communicate normally.

Notification of Receiving Audio/Video First Frame

Publish Stream End Audio Capture First Frame Callback

You can receive audio first frame callbacks by registering onPublisherCapturedAudioFirstFrame. After the publish stream interface is called successfully, this callback will be received when the SDK captures the first frame of audio data.

Notes

In the case of not publishing, after calling the publish stream interface, that is, when the engine of the audio and video module inside the SDK starts, the SDK will capture the local device's audio data and receive this callback. Developers can judge whether the SDK actually captures audio data based on this callback. If this callback is not received, it indicates that the audio capture device is occupied or abnormal.

engine.setEventHandler(new IZegoEventHandler() {
    @Override
    public void onPublisherCapturedAudioFirstFrame() {
        super.onPublisherCapturedAudioFirstFrame();
    }
}

Publish Stream End Video Capture First Frame Callback

You can receive video first frame callbacks by registering onPublisherCapturedVideoFirstFrame. After the publish stream interface is called successfully, this callback will be received when the SDK captures the first frame of video data.

Notes

In the case of not publishing or not previewing, after calling the publish stream or preview interface, that is, when the engine of the audio and video module inside the SDK starts, the SDK will capture the local device's video data and receive this callback. You can judge whether the SDK actually captures video data based on this callback. If this callback is not received, it indicates that the video capture device is occupied or abnormal.

engine.setEventHandler(new IZegoEventHandler() {
    @Override
    public void onPublisherCapturedVideoFirstFrame(ZegoPublishChannel channel) {
    super.onPublisherCapturedVideoFirstFrame(channel);
    }
}

Play Stream End Audio Receive First Frame Callback

Developers can listen to play stream end audio receive first frame callbacks by registering onPlayerRecvAudioFirstFrame. After the play stream interface is called successfully, this callback will be received when the SDK plays the first frame of audio data.

engine.setEventHandler(new IZegoEventHandler() {
    @Override
    public void onPlayerRecvAudioFirstFrame(String streamID) {
        super.onPlayerRecvAudioFirstFrame(streamID);
        AppLogger.getInstance().receiveCallback("onPlayerRecvAudioFirstFrame streamID:%s",streamID);
    }
}

Play Stream End Video Receive First Frame Callback

You can listen to play stream end receive video first frame callbacks by registering onPlayerRecvVideoFirstFrame. After the play stream interface is called successfully, this callback will be received when the SDK plays the first frame of video data.

engine.setEventHandler(new IZegoEventHandler() {
    @Override
    public void onPlayerRecvVideoFirstFrame(String streamID) {
        super.onPlayerRecvVideoFirstFrame(streamID);
    }
}

Play Stream End Rendered Video First Frame Callback

You can listen to play stream end rendered video first frame callbacks by registering onPlayerRenderVideoFirstFrame. After the play stream interface is called successfully, this callback will be received when the SDK plays and renders the first frame of video data.

Notes

You can use this callback to calculate first frame delay or update the UI components of the playing stream.

engine.setEventHandler(new IZegoEventHandler() {
    @Override
    public void onPlayerRenderVideoFirstFrame(String streamID){
        super.onPlayerRenderVideoFirstFrame(streamID);
    }
}

Video Resolution Change Callback

Capture Video Resolution Change Callback

You can listen to capture video size change callbacks by registering onPublisherVideoSizeChanged. After successful publishing, if the video capture resolution changes during publishing, this callback will be received.

Notes

When not publishing or not previewing, for the first publishing or first preview, that is, when the engine of the audio and video module inside the SDK starts, the SDK will capture the local device's video data, and the capture resolution will change at this time.

You can use this callback to remove the cover of the local preview UI and other similar operations. You can also dynamically adjust the proportion of the preview view based on the resolution of this callback.

engine.setEventHandler(new IZegoEventHandler() {
    @Override
    public void onPublisherVideoSizeChanged(int width, int height, ZegoPublishChannel channel) {
        super.onPublisherVideoSizeChanged(width, height, channel);
    }
}

Play Stream Resolution Change Notification

You can receive play stream resolution change notifications by registering onPlayerVideoSizeChanged. After successful playing, if the video resolution changes during playing, this callback will be received, and users can adjust the display based on the final resolution of the stream.

Notes
  • If the played stream only has audio data, this callback will not be received.
  • If the publish stream end triggers the SDK's internal traffic control due to network problems, it may dynamically reduce the encoding resolution of the publish stream end. At this time, this callback will also be received.
  • This callback is triggered when the played audio and video stream is actually rendered to the set UI play interface. Developers can use this callback notification to update or switch the UI components that actually play the stream.
engine.setEventHandler(new IZegoEventHandler() {
    @Override
    public void onPlayerVideoSizeChanged(String streamID, int width, int height) {
    }
}

Previous

Pre-Call Network/Device Detection

Next

Network Speed Test