Service quality
You can use service quality callbacks to notify users about video, audio, and screen sharing quality during a session and unstable network conditions. You can also use these to measure or show latency, FPS, and resolution. Zoom Video SDK optimizes the FPS and resolution to the current device and network capabilities to optimize a good, smooth experience.
Additionally, you can build network strength indicators for your users or a real-time statistics dashboard of video, audio, and screen share quality. This is similar to how Zoom Meetings shares diagnostic information.
If you want near real time analytics to quickly diagnose and resolve network disruptions, consider purchasing a Quality of Service Subscription (QSS) plan to use our QSS API and webhooks for Video SDK.
Network status
The ZoomVideoSDKNetworkStatus enum includes values representing the network status.
ZoomVideoSDKNetworkStatus_None,
ZoomVideoSDKNetworkStatus_Bad,
ZoomVideoSDKNetworkStatus_Normal,
ZoomVideoSDKNetworkStatus_Good
To get a network status change callback, there must be at least two users in the session with their video on. You can use the network status returned from the callback to create an icon to show the current network quality.
func onUserVideoNetworkStatusChanged(_ status: ZoomVideoSDKNetworkStatus, user: ZoomVideoSDKUser) {
if status == .bad {
//Do something
}
}
- (void)onUserVideoNetworkStatusChanged:(ZoomVideoSDKNetworkStatus)status user:(ZoomVideoSDKUser *)user {
if (status == ZoomVideoSDKNetworkStatus_Bad) {
//Do something
}
}
QOS statistics
The onQOSStatisticsReceived callback provides real-time quality-of-service metrics for audio, video, and screen sharing streams. It fires for both send and receive directions, identified by the direction property on the statistics object.
The base ZoomVideoSDKQOSStatistics object contains fields common to all stream types and directions. Depending on the value of direction, cast the object to ZoomVideoSDKQOSSendStatistics or ZoomVideoSDKQOSRecvStatistics to specifically access send or receive fields.
func onQOSStatisticsReceived(_ statistics: ZoomVideoSDKQOSStatistics, user: ZoomVideoSDKUser) {
// Common fields available on all statistics
statistics.direction // .send or .receive
statistics.statisticsType // .audio, .video, or .share
statistics.codecName // e.g. "h264", "av1", "opus" — valid only during this callback
statistics.timestamp
statistics.rtt
statistics.jitter
statistics.width
statistics.height
statistics.fps
statistics.bps
statistics.packetsLost
statistics.packetsSent
statistics.networkLevel
statistics.avgLoss // per-thousand, e.g. 100 = 10%
statistics.maxLoss // per-thousand
statistics.bandwidth
if statistics.direction == .send, let sendStats = statistics as? ZoomVideoSDKQOSSendStatistics {
sendStats.frameWidthInput
sendStats.frameHeightInput
sendStats.frameRateInput
sendStats.bytesSent
sendStats.packetsSent
sendStats.totalPacketSendDelay
sendStats.totalEncodeTime
sendStats.framesEncoded
} else if statistics.direction == .receive, let recvStats = statistics as? ZoomVideoSDKQOSRecvStatistics {
recvStats.bytesReceived
recvStats.packetsReceived
recvStats.estimatedPlayoutTimestamp
recvStats.totalDecodeTime
recvStats.framesDecoded
recvStats.jitterBufferDelay
recvStats.jitterBufferEmittedCount
}
}
- (void)onQOSStatisticsReceived:(ZoomVideoSDKQOSStatistics *)statistics user:(ZoomVideoSDKUser *)user {
// Common fields available on all statistics
statistics.direction; // ZoomVideoSDKStatisticsDirection_Send or _Receive
statistics.statisticsType; // Audio, Video, or Share
statistics.codecName; // e.g. "h264", "av1", "opus" — valid only during this callback
statistics.timestamp;
statistics.rtt;
statistics.jitter;
statistics.width;
statistics.height;
statistics.fps;
statistics.bps;
statistics.packetsLost;
statistics.packetsSent;
statistics.networkLevel;
statistics.avgLoss; // per-thousand, e.g. 100 = 10%
statistics.maxLoss; // per-thousand
statistics.bandwidth;
if (statistics.direction == ZoomVideoSDKStatisticsDirection_Send) {
ZoomVideoSDKQOSSendStatistics *sendStats = (ZoomVideoSDKQOSSendStatistics *)statistics;
sendStats.frameWidthInput;
sendStats.frameHeightInput;
sendStats.frameRateInput;
sendStats.bytesSent;
sendStats.packetsSent;
sendStats.totalPacketSendDelay;
sendStats.totalEncodeTime;
sendStats.framesEncoded;
} else if (statistics.direction == ZoomVideoSDKStatisticsDirection_Receive) {
ZoomVideoSDKQOSRecvStatistics *recvStats = (ZoomVideoSDKQOSRecvStatistics *)statistics;
recvStats.bytesReceived;
recvStats.packetsReceived;
recvStats.estimatedPlayoutTimestamp;
recvStats.totalDecodeTime;
recvStats.framesDecoded;
recvStats.jitterBufferDelay;
recvStats.jitterBufferEmittedCount;
}
}
Video quality
When statisticsType is .video, the callback provides video-specific metrics such as frame dimensions, frame rate, and bitrate. To receive video statistics, there must be at least two users in the session with their video on.
Audio quality
When statisticsType is .audio, the callback provides audio-specific metrics such as codec, jitter, and packet loss. The width, height, and fps fields are not applicable for audio. To receive audio statistics, there must be at least two users actively sending audio to the session.
Screen sharing quality
When statisticsType is .share, the callback provides screen sharing metrics such as frame dimensions, frame rate, and bitrate. To receive share statistics, there must be at least two users in the session with at least one sharing their screen.