Skip to main content

How do I troubleshoot slow first-frame rendering of remote video when using the Agora Video SDK?

When using the Agora Video SDK for real-time Video Calling, Interactive Live Streaming, or Broadcast Streaming, some developers observe that the remote video takes too long to display after joining a channel. This delay can negatively impact user experience, especially in scenarios like live broadcasts where quick video display is critical.

Why delays occur

The delay in the first frame rendering is usually due to delays across multiple stages, including:

  • Setting the remote rendering view too late
  • Not subscribing to the remote video stream in time after joining the channel

Solution

Video SDK provides a mechanism to help developers identify which stage of the video rendering pipeline is time-consuming. The onVideoRenderingTracingResult callback returns the duration of each stage. By default, durations are calculated from the time joinChannel is called. To customize the start time, call startMediaRenderingTracing.

Step 1: Use startMediaRenderingTracing before joining

To align rendering metrics with actual user experience, call startMediaRenderingTracing immediately when the user triggers a join action (such as tapping Join or switching channels). This ensures the collected data reflects real-world latency.

The following table explains the fields returned in tracingInfo from the onVideoRenderingTracingResult callback:

FieldDescriptionReference RangeRecommendations
elapsedTime
  • Decoding event: Time from startMediaRenderingTracing to onFirstRemoteVideoDecoded.
  • Rendering event: Time from startMediaRenderingTracing to onFirstRemoteVideoFrame.
< 1000 ms for bothIf the difference between decoding and rendering is >200 ms, ensure the remote view is set promptly. If only decoding events are reported, check whether:
  • The remote view is unset
  • An external renderer is used
start2JoinChannelTime from startMediaRenderingTracing to joinChannel.< 10 msIf too large, ensure joinChannel is called promptly after user action.
join2JoinSuccessTime from joinChannel to onJoinChannelSuccess.50–500 msIf high, check network conditions. Consider calling preloadChannel to speed up joining.
joinSuccess2RemoteJoinedTime from onJoinChannelSuccess to onUserJoined.≤50 msHigh values may occur if:
  • Host joins after the viewer
  • Streams are not subscribed by default
remoteJoined2SetViewTime from onUserJoined to setupRemoteVideo.0–20 msSet the remote view immediately in onUserJoined. For optimal performance, pre-set the view before joining.
remoteJoined2UnmuteVideoTime from onUserJoined to subscribing to the video stream.0 msIf high, check if subscription is delayed. Subscribing during join keeps this value at 0.
remoteJoined2PacketReceivedTime from onUserJoined to receiving the first video packet.0–50 msIf high, check:
  • Whether video stream subscription is delayed
  • Whether the host publishes the stream promptly

Step 2: Use default behavior (without startMediaRenderingTracing)

If you don't call startMediaRenderingTracing, the SDK uses the time joinChannel is called as the starting point for measuring rendering performance.

With this approach, most fields in the onVideoRenderingTracingResult callback have the same meaning as when tracing is started before joining. However, two fields have different interpretations, as shown below:

FieldDescriptionReference RangeRecommendations
elapsedTime
  • Decoding event: Time from joinChannel to onFirstRemoteVideoDecoded.
  • Rendering event: Time from joinChannel to onFirstRemoteVideoFrame.
< 1000 ms for bothIf the difference between decoding and rendering is >200 ms, check whether the remote view is set on time. If only decoding events are received, possible causes include:
  • Remote view not set
  • Use of an external renderer
start2JoinChannelTime from startMediaRenderingTracing to joinChannel.0 msThis value is always 0 because no tracing was started before calling joinChannel.

Step 3: Pre-join the channel, then subscribe on demand

If your app requires extremely low latency for the first frame, pre-join the channel without subscribing to the stream. When the user triggers the join action, call startMediaRenderingTracing and subscribe to the remote stream.

For implementation details, see Optimize first-frame rendering.

The following table describes each field for this scenario:

FieldDescriptionReference RangeRecommendations
elapsedTime
  • Decoding event: Time from startMediaRenderingTracing to onFirstRemoteVideoDecoded.
  • Rendering event: Time from startMediaRenderingTracing to onFirstRemoteVideoFrame.
< 500 ms for bothIf the rendering time greatly exceeds decoding time, check whether the remote view was set in time.
start2JoinChannelTime from joinChannel to startMediaRenderingTracing.NegativeSince the channel was joined before tracing started, this value is negative and can be ignored.
join2JoinSuccessTime from joinChannel to onJoinChannelSuccess.50–500 msThis value is irrelevant because the user has already joined the channel.
joinSuccess2RemoteJoinedTime from startMediaRenderingTracing to onUserJoined.0 msIf greater than 0, it may indicate frequent channel switching. This value is not meaningful if onUserJoined is triggered after tracing starts.
remoteJoined2SetViewTime from startMediaRenderingTracing to setupRemoteVideo.0 msIf greater than 0, check whether the view was set promptly. This value may be invalid if the view was set before calling startMediaRenderingTracing.
remoteJoined2UnmuteVideoTime from startMediaRenderingTracing to subscribing to the stream.0 msIf this value is large, check for delays between calling startMediaRenderingTracing and subscribing to the remote stream.
remoteJoined2PacketReceivedTime from startMediaRenderingTracing to receiving the first video packet.~100 msIf high, verify that the remote host is publishing video promptly after joining.

API Reference

Android

iOS

macOS

Windows