Skip to main content
Version: 1.x.x

Optimize for Low Bandwidth Networks - React Native

When participants join a call from a weak or unstable network, the available bandwidth may not be enough to support the call at its default media settings. VideoSDK provides several controls that you can use to adapt the experience to changing network conditions.

This guide covers recommended approaches for managing the media each participant sends and receives, including choosing the video quality to publish, controlling subscriptions, and keeping participants informed as conditions change. These options can help you reduce bandwidth usage and maintain a reliable call experience.

Before the Call​

Measure the participant's connection before media is published, then use the result to choose a starting configuration that suits the connection.

Test the Connection​

  • Run runPreCallTest() before the participant joins the meeting. It returns a quality score from 1 (BAD) to 5 (EXCELLENT) for both uplink and downlink, along with a factors array that identifies the conditions affecting the score. Use these results to choose a starting media configuration that is appropriate for the participant's connection.

  • See Precall Setup for the parameters accepted by runPreCallTest() and the complete result object.

A pre-call screen with a camera preview, microphone and camera toggles, front camera and phone microphone selectors, and a line reading starting at a lower video quality, above a join now button

tip

Evaluate uplink and downlink separately, and use the score as an overall measure of connection quality rather than as an indication of available bandwidth. A low score can result from factors such as high latency even when sufficient bandwidth is available. In that case, lowering video quality may not improve the connection.

Configure the Meeting​

Use the following meeting settings to control how participants publish and receive media. Select the settings based on the participant's network conditions and the requirements of your application.

Configure Published Media​

  • Use customCameraVideoTrack and customMicrophoneAudioTrack on MeetingProvider to pass media tracks configured for the participant's connection, based on your use case and the pre-call test results. If these are not provided, VideoSDK uses its default track configuration.
  • Create the camera track with createCameraVideoTrack() and the microphone track with createMicrophoneAudioTrack(). Both accept an encoderConfig, while the camera track also accepts facingMode, bitrateMode, maxLayer, and codec.
  • For constrained connections, consider bitrateMode: "bandwidth_optimized" for the camera to reduce bandwidth usage at the selected resolution. "balanced" is the default, and "high_quality" raises the bitrate for connections that can support it. See Optimize Video Tracks for the bitrate used by each video configuration and Optimize Audio Track for the available audio configurations.
  • Where you do not need a custom track, maxResolution on MeetingProvider offers a simpler control over the default camera track. "hd", the default, captures at h720p_w1280p, while "sd" captures at h540p_w960p. See Initialise Meeting.
  • Screen share is configured separately, through the encoderConfig passed to createScreenShareVideoTrack(). On a constrained connection, choose by the content being shared: a low frame rate keeps text and diagrams readable, while a lower resolution keeps moving content smooth.
  • You can also configure the video codec to suit your application's requirements. Codec support and configuration options vary by device, so see Video Codecs before changing the codec or using it with other video settings.
  • To enforce an upper limit on the amount a participant can send, set Maximum Send Bitrate Per Participant for the API key in the VideoSDK Dashboard. The limit applies to every session using that API key and overrides higher bitrate settings configured in your application. See Optimize Audio and Video Usage for details.

Keep Simulcast Enabled​

  • Keep multiStream enabled, which is the default. With multiStream: true, the camera publishes multiple video quality layers. VideoSDK can then deliver an appropriate layer to each participant based on the receiving conditions and requested video quality.
  • With multiStream: false, the camera publishes a single video layer at the configured resolution. All participants then receive that same published layer, even when their network conditions or video layouts differ.
  • multiStream is also what the controls in Optimize Incoming Video act on. Adaptive subscriptions, setViewPort(), and setQuality() all choose between published layers, so they have nothing to select from on a single-layer track.
  • maxLayer caps how many layers a track publishes, accepts 2 or 3, and defaults to 3. It never adds layers beyond what the capture resolution allows, so on a lower encoderConfig the two values can produce the same result.
  • See Simulcast (Adaptive Bitrate) for what each value publishes.

Control Media Consumption​

  • For large calls, consider autoConsume: false when the application needs explicit control over which streams each participant receives. With this setting, streams are consumed only when requested, allowing the application to subscribe only to the participants required by the interface.
  • Use consumeWebcamStreams(), consumeMicStreams(), stopConsumingWebcamStreams(), and stopConsumingMicStreams() on useParticipant as participants enter and leave the visible part of the interface.

Choose the Participant's Media Mode​

Use the mode setting on MeetingProvider to control whether a participant publishes and receives media.

ModeDescription
SEND_AND_RECVPublishes and receives media. This is the default.
RECV_ONLYReceives media without publishing. Consider this for participants who only need to watch or listen, or when upload bandwidth is limited.
SIGNALLING_ONLYDoes not publish or receive media. Consider this when the participant does not need real-time media.
  • Choose a mode that matches the participant's role and network conditions.
  • Use changeMode() to switch between modes during the meeting.

Keep the Default Transport Protocol​

  • MeetingProvider accepts a preferredProtocol of UDP_ONLY, UDP_OVER_TCP, or TCP_ONLY. The default is UDP_OVER_TCP, which prefers UDP and falls back to TCP where UDP is blocked.
  • Keep the default on weak networks. It already handles restrictive mobile networks that block UDP, without giving up UDP where it is available.
  • TCP_ONLY is worth reaching for only where UDP is known to be unavailable. Real-time media on a lossy connection is generally better served by UDP, because retransmissions and in-order delivery add delay to media that is no longer useful by the time it arrives.

Improve the In-Call Experience​

Network and media conditions can change during a meeting. Use VideoSDK events and statistics to keep participants informed when the connection is reconnecting, video quality changes, or media conditions affect their experience.

Keep Participants Informed​

Use call and media events to provide clear feedback when conditions change during the meeting. Depending on your application, this can be a toast, banner, status indicator, or other in-call message.

Four phone screens showing in-call states on a weak network: a warning that the participant's network is unstable, a reconnecting state while the connection is restored, a participant tile showing that video is unavailable while their video is interrupted, and a tile whose video has been paused

SignalWhat it tells youRecommended response
onQualityLimitationReports a limitation affecting the local participant's outgoing media. The type can be bandwidth, congestion, or cpu, and the state can be detected or resolved. See Monitoring Network Quality.When a limitation is detected, consider informing the participant. For bandwidth- or congestion-related limitations, you can also reduce the quality of the published video, for example by using a lighter camera track as described in Configure Published Media. A connection sitting near its limit can report detected and resolved in turn, so hold a short delay before showing or clearing the message rather than binding it directly to the event.
onMeetingStateChangedIndicates a change in the meeting connection state, including RECONNECTING when the connection is interrupted. See Meeting Connection Events.Show a clear reconnecting message or status indicator so the participant knows the connection is being restored.
onStreamStateChangedReports the state of a remote participant's video or screen-share stream as active, freeze-detected, freeze-resolved, stuck, or ended. Available on useStream. See Stream Events.Show feedback on the participant's tile while their stream is frozen or stuck, so the interruption is explained rather than appearing as a broken tile. Clear the feedback when the stream reports freeze-resolved or active.
onVideoQualityChangedIndicates that a participant has switched to a different simulcast layer. Available on useParticipant. See Simulcast (Adaptive Bitrate).When relevant to your interface, show a video-quality indicator or other feedback so the participant understands that the received video quality has changed.
onStreamPaused and onStreamResumedReport that adaptive subscriptions paused or resumed a stream from a participant. Available on useParticipant. See Scalability for Large Participant.Show feedback on the tile while its video is paused, so the participant understands the tile is intentionally quiet rather than broken.
getVideoStats() and getAudioStats()Provide statistics for a remote participant's incoming media, including values such as bitrate, rtt, jitter, and packetsLost. getShareStats() provides the same for a screen-share stream. See Understanding Call Quality.Use these statistics when your application needs to identify or display more detailed information about the quality of a remote stream.
getTransportStats()Reports the total bitrate being sent and received by the participant.Use this when your application needs additional information about overall media usage or to support custom quality-related UI.

Optimize Incoming Video​

Incoming video is usually the largest part of a participant's bandwidth usage, and a participant only benefits from the quality their layout can actually display. Video is rendered with RTCView, which does not adjust the requested quality on its own, so match the incoming video to each tile yourself using the controls below.

Three phone screens showing how incoming video matches each tile: one participant filling the screen and receiving high quality, a grid of small tiles each receiving low quality, and a grid where the tiles scrolled below the visible area are paused

RecommendationHow
Enable adaptive subscriptionsUse enableAdaptiveSubscription() to let VideoSDK prioritise streams for you. Under bandwidth constraints it pauses muted participants' video first, then the least dominant speakers, and keeps pinned participants at the highest quality. See Scalability for Large Participant.
Report each tile's sizeCall setViewPort() with the dimensions of the view rendering the participant, and again when the layout changes. VideoSDK then selects the layer that suits the display area, so a thumbnail does not receive a full-resolution stream.
Control quality yourselfWhere you need a manual override, use setQuality() with "low", "med", or "high" to request a specific layer for a participant. See Selecting the Video Quality Layer.
Pause streams that are not visibleUse pause() on useStream while a participant is outside the visible area and resume() when they return. This matters most in paginated or scrolling layouts, where tiles leave the screen but keep receiving video. See Grid with Pagination.
Pause a whole media kindUse pauseAllStreams() with "video", "audio", or "share" to stop receiving one kind entirely, and resumeAllStreams() to restore it. Consider this for an audio-only fallback when a connection cannot sustain video.
Reduce screen-share qualityUse setScreenShareQuality("low") when bandwidth is limited. Screen-share quality is managed separately from camera video.
Prioritise the participants that matterUse pin() with "CAM", "SHARE", or "SHARE_AND_CAM" to mark the participants your layout gives prominence to, and unpin() when that changes. Pinned participants are kept at the highest quality while adaptive subscriptions reduce the rest.

Optimize Data Transfers​

Call media is not the only traffic using the participant's connection. Image captures and application updates also consume bandwidth, so keeping these transfers efficient can help preserve bandwidth for the call, especially on weak networks.

Capture Images Efficiently​

  • captureImage() captures a frame from the local participant's camera at its capture resolution. It can only be called on the local participant and requires the camera to be enabled, so the image always comes from the camera itself rather than from a received video layer.
  • For use cases such as document capture or identity verification, this means the image quality does not depend on the published video quality. There is no need to raise the published quality in order to obtain a higher-resolution image.
  • Pass height and width when a smaller image is sufficient. If these are not provided, the dimensions of the local camera stream are used.

To obtain an image from another participant, request it over pubSub and have that device capture and transfer it:

RecommendationHow
Request the capture from the camera ownerPublish a request on a pubSub topic, then capture the image locally on the device that receives it. See Image Capturer.
Transfer the image through temporary storageUpload the Base64 image with uploadBase64File() on useFile, share the returned URL over pubSub, and retrieve it with fetchBase64File(). This keeps a large payload off the pubSub channel, which other in-call updates share. See File Sharing.

Optimize Message Delivery​

Chat, reactions, application state, and other updates can also use bandwidth during a call. Choose the delivery options based on how important the data is and how quickly it becomes outdated.

Use caseRecommendation
Cursor positions, typing indicators, or other frequently changing updatesSet realtimeOverflow: "drop" on usePubSub() so the newest updates are delivered and stale ones are discarded rather than queued.
Updates that should survive a brief delaySet realtimeOverflow: "queue" with maxQueue, so a subscriber that falls behind can catch up. Anything lost beyond the queue is reported through onMessageDrop.
Persisted messages delivered when a participant joinsSet oldMessageLimit on usePubSub() when the full message history is not required, and newMessageLimit to cap how fast live messages are delivered.
Updates intended for specific participantsSet sendOnly when publishing so the message is delivered only to the participant IDs that need it, rather than to everyone in the call.
Messages that do not need to reach late joinersLeave persist disabled when publishing, which is the default, so the message is not stored and replayed to participants who join later.

See Overview for the full set of publish and subscribe options.

API Reference​

The API references for all the methods and events utilized in this guide are provided below.

Got a Question? Ask us on discord