# In-call quality monitoring (/en/realtime-media/video/build/manage-connection-and-quality/in-call-quality-monitoring/unity)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

During a call, Video SDK triggers callbacks related to the video calling quality. These callbacks enable you to monitor your users' experience, troubleshoot issues, and optimize their overall experience

      
  
      
  
      
  
      
  
      
  
      
  
      
  
      
  
      
    ## Understand the tech [#understand-the-tech-8]

    After a user joins a channel, Video SDK triggers a series of callbacks every 2 seconds, reporting information such as uplink and downlink network quality, real-time interaction statistics, and statistics of local and remote audio and video streams.

    When there is a change in the audio or video state of a user, Video SDK triggers a callback to report the latest state and the reason for the change. The following figure shows the audio transmission process between app clients:

    **Audio transmission process**

    ![Audio transmission process](https://assets-docs.agora.io/images/video-sdk/in-call-quality.svg)

    To monitor the call quality, Agora provides the following call quality notifications:

    **Network quality**

    The network quality callback provides insight into the uplink and downlink last mile network quality for each participant in the channel. Last mile refers to the network from your device to Agora server. The [Network quality scores](#network-quality-score) are calculated based on factors such as sending or receiving bitrate, network packet loss rate, round-trip delay, and network jitter.

    **Statistics**

    The statistics callback, triggered every 2 seconds, reports key metrics such as call duration, the number of participants, system CPU usage, and app CPU usage.

    **Audio quality**

    Callbacks related to audio quality cover both local and remote audio streams. You monitor statistics and status changes, to gain insights into the quality of audio streams and any related reasons for status changes.

    **Video quality**

    Video quality callbacks provide information on both local and remote video streams. You receive statistics and status change notifications, that enable you to understand the quality of video streams and any related reasons for status changes.

    ## Prerequisites [#prerequisites-8]

    Ensure that you have implemented the [SDK quickstart](/en/realtime-media/video/get-started-sdk) in your project.

    ## Implement in-call quality monitoring [#implement-in-call-quality-monitoring-8]

    In `IRtcEngineEventHandler`, implement the following real-time interaction quality statistics callbacks and audio or video state monitoring callbacks to understand user interaction experience:

    * `OnNetworkQuality`: Reports uplink and downlink last mile network quality.
    * `OnRtcStats`: Reports real-time interaction statistics.
    * `OnLocalAudioStats`: Reports statistics for the sent audio stream.
    * `OnLocalAudioStateChanged`: Reports local audio stream state changes.
    * `OnRemoteAudioStats`: Reports statistics for the received remote audio stream.
    * `OnRemoteAudioStateChanged`: Reports remote audio stream state changes.
    * `OnLocalVideoStats`: Reports statistics for the sent video stream.
    * `OnLocalVideoStateChanged`: Reports local video stream state changes.
    * `OnRemoteVideoStats`: Reports statistics for the received remote video stream.
    * `OnRemoteVideoStateChanged`: Reports remote video stream state changes.

    In your game, add the following code:

    ```csharp
    // Event handler class to handle the events raised by Agora's RtcEngine instance
    internal class EventHandler : IRtcEngineEventHandler
    {
      public override void OnNetworkQuality(RtcConnection connection, uint remoteUid, int txQuality, int rxQuality)
      {
        Debug.Log($"OnNetworkQuality - Connection: {connection}, RemoteUid: {remoteUid}, TxQuality: {txQuality}, RxQuality: {rxQuality}");
      }

      public override void OnRtcStats(RtcConnection connection, RtcStats rtcStats)
      {
        string msg = $"{rtcStats.userCount} user(s)";
        msg += $"Packet loss rate: {rtcStats.rxPacketLossRate}";
        Debug.Log(msg);
      }

      public override void OnRemoteVideoStateChanged(RtcConnection connection, uint remoteUid, REMOTE_VIDEO_STATE state, REMOTE_VIDEO_STATE_REASON reason, int elapsed)
      {
        string msg = $"Remote video state changed: \n Uid = {remoteUid}\n NewState = {state}\n reason = {reason}\n elapsed = {elapsed}";
        Debug.Log(msg);
      }

      public override void OnRemoteVideoStats(RtcConnection connection, RemoteVideoStats stats)
      {
        string msg = $"Remote Video Stats: \n User id = {stats.uid}\n Received bitrate = {stats.receivedBitrate}\n Total frozen time = {stats.totalFrozenTime}";
        Debug.Log(msg);
      }

      public override void OnLocalAudioStats(RtcConnection connection, LocalAudioStats stats)
      {
        string msg = $"Local Audio Stats: \n Bytes sent = {stats.bytesSent}\n Bitrate = {stats.bitrate}\n NumChannels = {stats.numChannels}\n SentSampleRate = {stats.sentSampleRate}";
        Debug.Log(msg);
      }

      public override void OnLocalAudioStateChanged(RtcConnection connection, LOCAL_AUDIO_STREAM_STATE state, LOCAL_AUDIO_STREAM_ERROR error)
      {
        string msg = $"Local Audio State Changed: \n State = {state}\n Error = {error}";
        Debug.Log(msg);
      }

      public override void OnRemoteAudioStats(RtcConnection connection, RemoteAudioStats stats)
      {
        string msg = $"Remote Audio Stats: \n User id = {stats.uid}\n Received bitrate = {stats.receivedBitrate}\n Buffer size = {stats.bufferSize}\n Network transport delay = {stats.networkTransportDelay}";
        Debug.Log(msg);
      }

      public override void OnRemoteAudioStateChanged(RtcConnection connection, uint remoteUid, REMOTE_AUDIO_STATE state, REMOTE_AUDIO_STATE_REASON reason, int elapsed)
      {
        string msg = $"Remote Audio State Changed: \n RemoteUid = {remoteUid}\n State = {state}\n Reason = {reason}\n Elapsed = {elapsed}";
        Debug.Log(msg);
      }

      public override void OnLocalVideoStats(RtcConnection connection, LocalVideoStats stats)
      {
        string msg = $"Local Video Stats: \n Sent bitrate = {stats.sentBitrate}\n Sent frame rate = {stats.sentFrameRate}\n Encoder output frame rate = {stats.encoderOutputFrameRate}\n Capture frame rate = {stats.captureFrameRate}";
        Debug.Log(msg);
      }

      public override void OnLocalVideoStateChanged(VIDEO_SOURCE_TYPE source, LOCAL_VIDEO_STREAM_STATE state, LOCAL_VIDEO_STREAM_ERROR error)
      {
        string msg = $"Local Video State Changed: \n State = {state}\n Error = {error}";
        Debug.Log(msg);
      }
    }
    ```

    ## Reference [#reference-8]

    ### Network quality score [#network-quality-score-8]

    | Value | Enumeration         | Description                                                                    |
    | :---: | :------------------ | :----------------------------------------------------------------------------- |
    |   0   | `QUALITY_UNKNOWN`   | The quality is unknown or the user is not receiving a stream.                  |
    |   1   | `QUALITY_EXCELLENT` | The quality is excellent.                                                      |
    |   2   | `QUALITY_GOOD`      | The network quality is good, but the bitrate is slightly lower than excellent. |
    |   3   | `QUALITY_POOR`      | Users can feel the communication is slightly impaired.                         |
    |   4   | `QUALITY_BAD`       | Users cannot communicate smoothly.                                             |
    |   5   | `QUALITY_VBAD`      | The quality is so bad that users can barely communicate.                       |
    |   6   | `QUALITY_DOWN`      | The network is down, and users cannot communicate at all.                      |

    ### API reference [#api-reference-8]

    * [`onNetworkQuality`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onnetworkquality)

    * [`onRemoteAudioStateChanged`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onremoteaudiostatechanged)

    * [`onRemoteVideoStateChanged`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onremotevideostatechanged)

    * [`onLocalAudioStateChanged`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onlocalaudiostatechanged)

    * [`onLocalVideoStateChanged`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onlocalvideostatechanged)

    * [`onRemoteAudioStats`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onremoteaudiostats)

    * [`onRemoteVideoStats`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onremotevideostats)

    * [`onLocalAudioStats`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onlocalaudiostats)

    * [`onLocalVideoStats`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onlocalvideostats)

    * [`onRtcStats`](https://api-ref.agora.io/en/video-sdk/unity/4.x/API/class_irtcengineeventhandler.html#callback_irtcengineeventhandler_onrtcstats)

    
  
      
  
