logo
On this page

Stream Quality Monitoring

2026-03-05

Overview

When using ZEGOCLOUD Express SDK for calls, users may experience poor network conditions. You can use the relevant callbacks to understand the current network quality and audio/video information changes during the call.

For example, in multi-person audio/video calls or multi-person singing scenarios, you may need to display user network quality in real-time. You can refer to this document to implement the corresponding functionality.

Sample Source Code

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

For related source code, please check files in the "lib\topics\StreamAdvanced\stream_monitoring" directory.

Prerequisites

Before monitoring call quality, make sure:

Basic Network Quality Report

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

The callback logic varies by version:

VersionCallback Logic
2.22.0 and aboveBased on the onNetworkQuality callback logic from version 2.14.0 to 2.21.1, it can also predict the network conditions of remote publishing users. If a remote publishing user's heartbeat is lost once, their network quality callback will be "unknown"; if the heartbeat is lost 3 times, their network quality callback will be "die".
2.14.0 to 2.21.1
  • You will receive your own network quality callback as long as you publish or play a stream.
  • You will receive other users' network quality callbacks only when you are playing their audio/video streams and they are in your room.
  • When "userID" is "" (empty string), it represents your own network quality; when "userID" is not "" (empty string), it represents other users in the room.
2.10.0 to 2.13.1
  • You must both publish and play streams 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 a stream for you to receive that user's network quality callback.
  • When "userID" is "" (empty string), it represents your own network quality; when "userID" is not "" (empty string), it represents other users in the room.
Warning
ZegoExpressEngine.onNetworkQuality = (String userID, ZegoStreamQualityLevel upstreamQuality, ZegoStreamQualityLevel downstreamQuality) {
    if (userID.isEmpty) {
        // Represents local user's (local side) network quality
        // ("My upstream network quality is $upstreamQuality");
        // ("My downstream network quality is $downstreamQuality");
    } else {
        // Represents other users' network quality in the room
        //("User $userID's upstream network quality is $upstreamQuality");
        //("User $userID's downstream network quality is $downstreamQuality");
    }

    /*
    ZegoStreamQualityLevel.Excellent, Excellent network quality
    ZegoStreamQualityLevel.Good, Good network quality
    ZegoStreamQualityLevel.Medium, Medium network quality
    ZegoStreamQualityLevel.Bad, Poor network quality
    ZegoStreamQualityLevel.Die, Network abnormal
    ZegoStreamQualityLevel.Unknown, Unknown network quality
    */
};

Advanced Quality Report

If the above basic network quality report cannot meet your needs, ZEGOCLOUD also provides more detailed publishing stream quality reports, playing stream quality reports, and other related information for your reference.

Publishing Stream Quality Report

The publishing stream quality report describes the quality of the process where users push audio/video to the ZEGOCLOUD server, including the frame rate of the audio/video stream during the capture and encoding stages, and the frame rate, bitrate, latency, and packet loss rate during transmission (sending).

You can register for onPublisherQualityUpdate to receive publishing stream quality callbacks. After successfully publishing a stream, this callback will be received every three seconds. You can use the quality parameter (ZegoPublishStreamQuality) to understand the health status of the published audio/video stream in real-time.

  • In most cases, you only need to pay attention to the "level" parameter of "quality" and use the "level" enumeration value to determine the overall quality of the published stream. For details, please refer to ZegoStreamQualityLevel.
  • If you want to pay attention to more detailed publishing stream quality parameters, please refer to ZegoPublishStreamQuality.
// Developers can monitor specific quality in this callback and report to the business server for monitoring, or monitor specific fields of the quality object to provide user-friendly prompts
ZegoExpressEngine.onPublisherQualityUpdate = (String streamID, ZegoPublishStreamQuality quality) {
    String networkQuality = "";
    // level represents the comprehensive score of publishing stream quality. In most cases, developers can refer to this score to display upstream network quality

    switch (quality.level) {
        case ZegoStreamQualityLevel.Excellent:
           networkQuality = "Excellent";
           break;
        case ZegoStreamQualityLevel.Good:
           networkQuality = "Good";
           break;
        case ZegoStreamQualityLevel.Medium:
           networkQuality = "Medium";
           break;
        case ZegoStreamQualityLevel.Bad:
           networkQuality = "Poor";
           break;
        case ZegoStreamQualityLevel.Die:
           networkQuality = "Failed";
           break;
        case ZegoStreamQualityLevel.Unknown:
           networkQuality = "Unknown";
           break;
        default:
           break;
    }
    //("Network quality is: $networkQuality");
};

Playing Stream Quality Report

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

You can register for onPlayerQualityUpdate to receive playing stream quality callbacks. After successfully playing a stream, this callback will be received every three seconds. You can use the quality parameter (ZegoPlayStreamQuality) to understand the health status of the played audio/video stream in real-time.

  • In most cases, you only need to pay attention to the "level" parameter of "quality" and use the "level" enumeration value to determine the overall quality of the played stream. For details, please refer to ZegoStreamQualityLevel.
  • If you want to pay attention to more detailed playing stream quality parameters, please refer to ZegoPlayStreamQuality.
// Developers can monitor specific quality in this callback and report to the business server for monitoring, or monitor specific fields of the quality object to provide user-friendly prompts
ZegoExpressEngine.onPlayerQualityUpdate = (String streamID, ZegoPlayStreamQuality quality) {
    String networkQuality = "";
    // level represents the comprehensive score of playing stream quality. In most cases, developers can refer to this score to display downstream network quality

    switch (quality.level) {
        case ZegoStreamQualityLevel.Excellent:
           networkQuality = "Excellent";
           break;
        case ZegoStreamQualityLevel.Good:
           networkQuality = "Good";
           break;
        case ZegoStreamQualityLevel.Medium:
           networkQuality = "Medium";
           break;
        case ZegoStreamQualityLevel.Bad:
           networkQuality = "Poor";
           break;
        case ZegoStreamQualityLevel.Die:
           networkQuality = "Failed";
           break;
        case ZegoStreamQualityLevel.Unknown:
           networkQuality = "Unknown";
           break;
        default:
           break;
    }
    //("Network quality is: %s", networkQuality);
};

MOS Audio Quality Score

Starting from ZEGOCLOUD Express SDK version 2.16.0, the playing stream quality callback onPlayerQualityUpdate includes a new "mos" field, representing the score of the played stream's audio quality. Developers who are concerned about audio quality can use this field to understand the current audio quality status.

The "mos" field value ranges from [-1, 5], where -1 means unknown (for example, when scoring is not possible due to abnormal stream playing), and [0, 5] represents 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.0Excellent audio quality, clear and smooth, easy to hear.
3.5 - 4.0Good audio quality, occasional audio quality damage, but still clear and smooth, easy to hear.
3.0 - 3.5Fair audio quality, occasional freezes, requires some attention to hear clearly.
2.5 - 3.0Poor audio quality, frequent freezes, requires focused attention to hear clearly.
2.0 - 2.5Very poor audio quality, some semantic loss, difficult to communicate.
0 - 2.0Extremely poor audio quality, massive semantic loss, unable to communicate.
-1Unknown.

Other Information Monitoring

Publish/Play Stream Status Change Notification

Publishing Stream Status Callback

After successfully publishing a stream, you can use onPublisherStateUpdate to receive notifications of publishing stream status changes.

ZegoExpressEngine.onPublisherStateUpdate = (String streamID, ZegoPublisherState state, int errorCode, Map<String, dynamic> extendedData) {
    // When state is ZegoPublisherState.NoPublish and errorCode is non-zero, it indicates publishing stream failure, and no more retries will be made. At this point, you can display a publishing stream failure prompt on the interface
    // When state is ZegoPublisherState.PublishRequesting and errorCode is non-zero, it indicates retrying to publish stream. If the stream is not successfully published within the retry time, a publishing stream failure notification will be thrown
};

You can roughly judge the user's publishing 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 publishing stream status as follows:

Enumeration ValueDescription
ZegoPublisherState.NoPublishNot publishing stream status, in this state before publishing stream. If a steady-state abnormality occurs during the publishing process, such as incorrect AppID, AppSign, or Token, or another user is already publishing a stream with the same stream ID, the publishing will fail and enter the not publishing stream status.
ZegoPublisherState.PublishRequestingRequesting 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 retries and return to the requesting publish stream status.
ZegoPublisherState.PublishingPublishing stream status. Entering this status indicates that the stream has been successfully published and users can communicate normally.

The parameter "extendedData" is the extended information attached to the status update. If using ZEGOCLOUD's CDN content distribution network, after successfully publishing the stream, the content of this parameter includes keys "flv_url_list", "rtmp_url_list", "hls_url_list", corresponding to the playing URLs for FLV, RTMP, and HLS protocols respectively.

Playing Stream Status Change Callback

After successfully playing a stream, developers can use onPlayerStateUpdate to receive notifications of playing stream status changes.

ZegoExpressEngine.onPlayerStateUpdate = (String streamID, ZegoPlayerState state, int errorCode, Map<String, dynamic>extendedData) {
    // When state is ZegoPlayerState.NoPlay and errorCode is non-zero, it indicates playing stream failure, and no more retries will be made. At this point, you can display a playing stream failure prompt on the interface
    // When state is ZegoPlayerState.PlayRequesting and errorCode is non-zero, it indicates retrying to play stream. If the stream is not successfully played within the retry time, a playing stream failure notification will be thrown
};

Developers can roughly judge the user's playing 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 playing stream status as follows:

Enumeration ValueDescription
ZegoPlayerState.NoPlayNot playing stream status, in this state before playing stream. If a steady-state abnormality occurs during the playing process, such as incorrect AppID, AppSign, or Token, it will enter the not playing stream status.
ZegoPlayerState.PlayRequestingRequesting 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 retries and return to the requesting play stream status.
ZegoPlayerState.PlayingPlaying stream status. Entering this status indicates that the stream has been successfully played and users can communicate normally.

Notification of Receiving Audio/Video First Frame

Publishing Side Audio Capture First Frame Callback

You can register for onPublisherCapturedAudioFirstFrame to receive the audio first frame callback. After successfully calling the publish stream interface, this callback will be received when the SDK captures the first frame of audio data.

Note
  • When not publishing or previewing, on first publish or first preview (when the SDK's internal audio/video module engine starts), the SDK will capture the local device's audio data, and this callback will be received. Developers can use this callback to determine whether the SDK has actually captured audio data. If this callback is not received, it means the audio capture device is occupied or abnormal.
  • Web does not currently support the onPublisherCapturedAudioFirstFrame interface.
ZegoExpressEngine.onPublisherCapturedAudioFirstFrame = () {
};

Publishing Side Video Capture First Frame Callback

You can register for onPublisherCapturedVideoFirstFrame to receive the video first frame callback. After successfully calling the publish stream interface, this callback will be received when the SDK captures the first frame of video data.

Note
  • If the published stream only has audio data, this callback will not be received.
  • When not publishing or previewing, on first publish or first preview (when the SDK's internal audio/video module engine starts), the SDK will capture the local device's video data, and this callback will be received. You can use this callback to determine whether the SDK has actually captured video data. If this callback is not received, it means the video capture device is occupied or abnormal.
  • Web does not currently support the onPublisherCapturedAudioFirstFrame interface.
ZegoExpressEngine.onPublisherCapturedVideoFirstFrame = (ZegoPublishChannel channel) {
};

Playing Side Audio Receive First Frame Callback

Developers can register for onPlayerRecvAudioFirstFrame to monitor the playing side's audio receive first frame callback. After successfully calling the play stream interface, this callback will be received when the SDK plays the first frame of audio data.

Warning
  • Web does not currently support the onPlayerRecvAudioFirstFrame interface.
ZegoExpressEngine.onPlayerRecvAudioFirstFrame = (String streamID) {
    print("onPlayerRecvAudioFirstFrame streamID:$streamID");
};

Playing Side Video Receive First Frame Callback

You can register for onPlayerRecvVideoFirstFrame to monitor the playing side's video receive first frame callback. After successfully calling the play stream interface, this callback will be received when the SDK plays the first frame of video data.

Note
  • If the played stream only has audio data, this callback will not be received.
  • Web does not currently support the onPlayerRecvVideoFirstFrame interface.
ZegoExpressEngine.onPlayerRecvVideoFirstFrame = (String streamID) {
};

Playing Side Rendered Video First Frame Callback

You can register for onPlayerRenderVideoFirstFrame to monitor the playing side's rendered video first frame callback. After successfully calling the play stream interface, this callback will be received when the SDK plays and renders the first frame of video data.

Note
  • If the played stream only has audio data, this callback will not be received.
  • You can use this callback to track first frame rendering time or update the playing stream's UI components.
  • Web does not currently support the onPlayerRenderVideoFirstFrame interface.
ZegoExpressEngine.onPlayerRenderVideoFirstFrame = (String streamID) {
};

Video Resolution Change Callback

Capture Video Resolution Change Callback

You can register for onPublisherVideoSizeChanged to monitor the capture video size change callback. After successfully publishing a stream, if the video capture resolution changes during publishing, this callback will be received.

Note
  • If the published stream only has audio data, this callback will not be received.
  • When not publishing or previewing, on first publish or first preview (when the SDK's internal audio/video module engine starts), the SDK will capture the local device's video data, at which point the capture resolution will change.
  • You can use this callback to remove UI overlay masks for local preview, or dynamically adjust preview view proportions based on the callback's resolution.
ZegoExpressEngine.onPublisherVideoSizeChanged = (int width, int height, ZegoPublishChannel channel) {
};

Playing Resolution Change Notification

You can register for onPlayerVideoSizeChanged to receive playing resolution change notifications. After successfully playing a stream, if the video resolution changes during playing, this callback will be received. Users can adjust the display based on the stream's final resolution.

Note
  • If the played stream only has audio data, this callback will not be received.
  • If the publishing side triggers the SDK's internal traffic control due to network issues, it may dynamically reduce the encoding resolution of the publishing side, in which case this callback will also be received.
  • This callback is triggered when the played audio/video stream is actually rendered to the set UI playing interface. Developers can use this callback notification to update or switch the UI components that actually play the stream.
ZegoExpressEngine.onPlayerVideoSizeChanged = (String streamID, int width, int height) {
};

Previous

Pre-call Detection

Next

Network Speed Test