Scalable Video
Scalable video is a feature for routed sessions that improves video quality in multi-party sessions by letting each subscriber receive a video quality that matches their current network conditions — independently of every other subscriber.
Without scalable video, the Media Router forwards the same video quality to every subscriber on a stream. When a subscriber's connection degrades, the router uses bandwidth estimation to tell the publisher to reduce its bitrate — which lowers the quality for all subscribers on that stream. With scalable video active, the router selects the most appropriate quality layer for each subscriber independently in real time, without affecting what other subscribers receive.
Scalable video requires a routed session (a session that uses the Media Router). It is not available in relayed sessions and would not be useful there, since streams travel directly between clients with no router in the path to select or switch quality layers. See The Media Router and media modes.
Scalable video is enabled by default. The application-level setting defaults to Auto, which means the Media Router activates scalable video automatically when there are more than two clients in a session and all other conditions are met. You can also set it to On (always active) or Off — see Application-Level Setting for details. For most applications, no code changes are needed.
Codecs and Scalability Models
How scalable video works depends on the codec the publisher negotiates. Understanding the differences helps you choose the right codec for your use case, such as when you need to optimize for publisher CPU usage, upstream bandwidth, or subscriber-side quality adaptation.
VP8: Simulcast
With VP8, scalable video is implemented through simulcast. The publisher encodes and transmits multiple completely separate bitstreams at different resolutions and frame rates — for example, 1080p, 540p, and 270p each with their own temporal (frame rate) layers. Each stream is self-contained and can be decoded on its own.
Because each stream is fully independent, the Media Router can instantly switch any subscriber to a different quality level with no re-encoding — providing strong resilience to changing network conditions.
Tradeoff: Encoding and uploading multiple streams requires more CPU and upstream bandwidth on the publisher side than sending a single quality. Lower-resolution layers are significantly cheaper to encode than the highest quality, but the total cost is still higher than a non-simulcast stream.
VP9: Scalable Video Coding (SVC)
With VP9, scalable video uses Scalable Video Coding (SVC). The publisher encodes a single bitstream that has multiple spatial (resolution) and temporal (frame rate) layers mathematically embedded within it. Each higher layer depends on the lower layers, so a decoder receiving only the base layer gets low quality, while one receiving all layers gets full quality — all from the same bitstream.
The publisher CPU cost for SVC encoding is higher than encoding a single stream — roughly comparable to VP8 simulcast — but SVC requires less upstream bandwidth because there is only one bitstream to upload instead of multiple independent streams. From the single encoded stream, the Media Router can extract and forward exactly the right subset of layers for each subscriber, providing efficient and flexible quality adaptation that is also more resilient to packet loss.
SVC requires the publisher endpoint and the Media Router to both support VP9 SVC. Firefox supports VP9 but not SVC — a Firefox publisher sends VP9 without layers, so the Media Router cannot adapt quality for that stream.
For a full explanation of SVC layers, scalability modes (L1T3, L2T3, L3T3), archiving behavior, and device support, see VP9 Scalable Video Coding for routed sessions.
Key Difference at a Glance
| Aspect | Simulcast (VP8) | SVC (VP9) |
|---|---|---|
| What the publisher sends | Multiple independent bitstreams at different resolutions and frame rates (distinct SSRCs/RIDs) | 1 bitstream with spatial and temporal layers embedded (single SSRC) |
| Spatial layers | Multi-stream: each resolution is a separate encoded stream | Embedded within a single bitstream |
| Temporal layers | Each simulcast stream can include temporal layers | Embedded within a single bitstream |
| Media Router role | Pick which stream to forward | Extract and forward the right layers |
| Firefox publisher support | Supported via RID-based encodings | VP9 yes; SVC not supported on Firefox |
Spatial layer resolution ratio: Each spatial step uses a 2:1 resolution ratio. An L3T3 stream has layers at 100%, 50%, and 25% of the source resolution (e.g., 1080p → 540p → 270p). This is why setPreferredResolution() can target substantially different resolutions.
H.264: Scalable Video Not Supported
The H.264 codec is fully supported in the Vonage Video API for publishing and subscribing, but scalable video is not available for H.264 streams. The Media Router cannot switch quality layers for H.264 streams, and scalable video settings have no effect when H.264 is negotiated. If your application requires scalable video, use VP8 or VP9 instead. See Video codecs.
Scalable Video Support
Scalable video is supported for VP8 simulcast and VP9 SVC. It is not supported for H.264 streams. It is only available in routed sessions.
The following clients support scalable video:
- Web SDK — Chrome, Firefox, Safari, Samsung Internet, WebView Android, WebView on iOS, and Chromium-based Edge. Note: Firefox supports VP8 simulcast but not VP9 SVC.
- Android SDK (on supported devices)
- iOS SDK (on supported devices)
- Windows SDK
- Linux SDK
- macOS SDK
- React Native SDK (on supported devices)
For VP9 SVC device and browser compatibility details, see VP9 Scalable Video Coding for routed sessions.
Note: By default, scalable video is disabled for screen-sharing streams and enabled for camera and custom video source streams. To enable scalable video for screen-sharing, see Scalable Screen-Sharing Streams.
How to Use Scalable Video
Application-Level Setting
Scalable video has three modes you can set per application in the Vonage Dashboard:
- Log in to your Vonage Video API Account.
- From the left-hand menu, select Applications.
- For an existing application, click the three-dot menu and select Edit. For a new application, access the capability section after clicking Create a new application.
- Toggle to enable the Video option.
- Scroll down and click Show advanced features.
- Under Scalable video, choose one of the following:
- Auto (recommended) — The Media Router enables scalable video when there are more than two clients in a session. Leave this selected unless you have a specific reason to override it.
- On — Scalable video is always enabled (in supported clients) for all sessions in this application.
- Off — Scalable video is disabled for all sessions in this application. Use this to lock the resolution and frame rate, or to reduce publisher CPU and bandwidth usage.
- Click Save changes.
Note: This setting controls VP8 simulcast behavior. VP9 always uses SVC when supported, and H.264 does not support scalable video — neither is affected by this setting.
Note: Streams require more upstream bandwidth from the publisher when scalable video is active, because additional quality layers are encoded and transmitted.
Scalable Screen-Sharing Streams
By default, scalable video is disabled for screen-sharing streams and enabled for camera and custom video source streams. Screen-sharing content typically changes less frequently than camera video, so the additional encoding cost of scalable video is usually unnecessary. However, enabling it can be beneficial in sessions where screen-share subscribers have varying network conditions. You can override the default per-publisher:
| SDK | Method / Property |
|---|---|
| Web SDK | scalableScreenshare option in OT.initPublisher() |
| Android SDK | PublisherKit.Builder.scalableScreenshare() |
| iOS SDK | OTPublisherKitSettings.scalableScreenshare |
| Windows SDK | Publisher.Builder.ScalableScreenshare |
| Linux SDK | otc_publisher_settings_set_scalable_screenshare() |
How the Content Hint Affects Screen-Sharing Layers
When scalable video is enabled for a VP8 screen-sharing stream in a routed session, the video content hint you set on the publisher determines how the publisher structures the simulcast layers it sends. This is why the number of streams (SSRCs) you observe for a screen-sharing publisher in a tool such as chrome://webrtc-internals depends on the content hint:
-
detailortext— The publisher optimizes for preserving fine detail and readability (text, line art, static content). It sends two streams at the same spatial resolution that differ only in frame rate: a full-resolution stream at the regular frame rate, and a second full-resolution stream at a lower frame rate. This keeps the shared content sharp and legible while still giving the Media Router a lower-bitrate layer to fall back to when a subscriber's network degrades — so degradation reduces the frame rate rather than blurring the detail. Two layers at the full resolution give a good balance between resilience during degradation and maintaining the original detailed resolution. -
motion— The publisher optimizes for smooth movement (for example, sharing video playback). It behaves like camera simulcast, sending multiple streams at different spatial resolutions, so the Media Router can drop a subscriber to a lower resolution to keep motion fluid under constrained network conditions.
Choose the content hint that matches your shared content: detail or text prioritizes keeping detail sharp (degrading frame rate first), while motion prioritizes smooth motion (degrading resolution first).
Note: This layer behavior applies to VP8 simulcast screen-sharing streams. Set the content hint via the videoContentHint option in OT.initPublisher() or the publisher.setVideoContentHint() method — see Video content hints.
Setting Subscriber Preferred Frame Rate and Resolution
When a stream is published with scalable video, subscribers can signal a preferred quality to the Media Router. The Media Router selects the closest available layer that fits the subscriber's actual network conditions.
Important: Calling setPreferredResolution() or setPreferredFrameRate() triggers a renegotiation with the Media Router. Doing so repeatedly or in rapid succession is costly — it consumes CPU and can degrade the overall stream quality. Set the preferred values once (or only when the subscriber's layout changes significantly), rather than adjusting them continuously.
Warning: These subscriber-side preferences assume the publisher is using the default scalability layer layout. If the publisher has overridden it with setTargetScalabilityMode(), the layer the Media Router forwards may not match the requested resolution or frame rate. See Interaction with subscriber preferred resolution and frame rate for details.
| SDK | Frame rate | Resolution |
|---|---|---|
| Web SDK | Subscriber.setPreferredFrameRate() — see subscribe-streams Web guide |
Subscriber.setPreferredResolution() — see subscribe-streams Web guide |
| Android SDK | SubscriberKit.setPreferredFrameRate() — see subscribe-streams Android guide |
SubscriberKit.setPreferredResolution() — see subscribe-streams Android guide |
| iOS SDK | OTSubscriberKit.preferredFrameRate — see subscribe-streams iOS guide |
OTSubscriberKit.preferredResolution — see subscribe-streams iOS guide |
| Windows SDK | Subscriber.PreferredFramerate — see subscribe-streams Windows guide |
Subscriber.PreferredResolution — see subscribe-streams Windows guide |
| Linux SDK | otc_subscriber_set_preferred_frame_rate() — see subscribe-streams Linux guide |
otc_subscriber_set_preferred_resolution() — see subscribe-streams Linux guide |
| React Native | preferredFrameRate property of OTSubscriber — see subscribe-streams React Native guide |
preferredResolution property of OTSubscriber — see subscribe-streams React Native guide |
How to Verify Scalable Video Is Working
There is no single "scalable video active" flag in the SDK, but you can confirm it is operating using the following approaches.
Video Inspector
The Video Inspector Tool in the Quality Metrics module displays the codec, resolution, and frame rate. Hover over any point on a plotted line to view the codec currently in use. When scalable video is enabled, the resolution and/or frame rate for one or more subscribers may adjust dynamically in response to changing network conditions.
WebRTC Stats
Each SDK exposes the underlying WebRTC stats report. On the publisher side, inspect RTCOutboundRtpStreamStats:
- With VP8 simulcast, you will see multiple
ssrcentries with differentframeWidth/frameHeightvalues — one per simulcast layer. - With VP9 SVC, you will see a single
ssrcwith ascalabilityModeproperty set (for example,L3T3).
Note: For a VP8 screen-sharing stream published with the detail or text content hint, the two ssrc entries have the same frameWidth/frameHeight and differ in frame rate instead of resolution. See How the Content Hint Affects Screen-Sharing Layers.
SDK methods to access the stats report:
- Web SDK —
Publisher.getRtcStatsReport()andSubscriber.getRtcStatsReport() - Linux — see Getting stream stats
Checklist: Conditions Required for Scalable Video to Be Active
If you are not observing adaptive quality behavior, verify the following:
- The session is routed (not relayed).
- Scalable video is not set to Off at the application level.
- The negotiated codec is VP8 or VP9 (not H.264).
- The publisher is running on a supported client.
Setting the target scalability mode
You can explicitly set the scalability mode for a publisher, controlling the number of spatial (resolution) and temporal (frame rate) layers that WebRTC encodes. This gives you fine-grained control over the trade-off between video quality adaptability and resource usage.
How it works
The target scalability mode is applied to the Media Router (publisher → Media Router) media path. It can be set or changed at any time — it does not require the media path negotiation to have completed, and you can update it on the fly during an active session while the encoder is already running. If you call the set method before the media path is established, the SDK holds the value and applies it once the codec negotiation completes. In Adaptive Media Routing (AMR) sessions, the mode is only applied to the Media Router (publisher → Media Router) media path, since scalability modes are not used when bypassing the Media Router in the media path.
What the target controls
The target scalability mode tells the encoder how many spatial (resolution) and temporal (frame rate) layers to produce. This directly determines what quality options the Media Router has available when forwarding video to each subscriber:
- More spatial layers — the Media Router can scale down the resolution for subscribers with limited bandwidth while keeping full resolution for others. For example,
L3T3provides three resolution tiers for the router to choose from. - More temporal layers — the Media Router can reduce the frame rate for constrained subscribers without reducing resolution. For example,
L1T3keeps full resolution at all times but gives the router three frame rate levels to choose from — this is well-suited for detail-critical content like slides or documents. - Fewer layers — reduces publisher CPU and bandwidth usage, but limits the Media Router's ability to adapt quality per subscriber.
When the target might not be fully applied
The target is a preference — the encoder applies it when conditions allow. The actual mode applied may differ from your target in these cases:
- Codec limitations — If the negotiated codec does not support the requested layers, the mode is adjusted. For example, setting
L2T3with VP8 results inL1T3being applied because VP8 does not support spatial scalability. See Codec-dependent fallback. - Resolution too low for the requested spatial layers — Each spatial layer step uses a 2:1 resolution ratio. If the publisher's capture resolution is too low to meaningfully subdivide (for example, publishing at 320×240 with
L3T3requested), the encoder may produce fewer spatial layers than requested because the lowest layer would be too small to be useful. - Hardware or resource constraints — On resource-constrained devices, the encoder might not produce all requested layers if doing so would exceed CPU or bandwidth budgets.
Checking the applied mode
The get method returns your target (what you set), not necessarily what was applied. To check the actual scalability layers being produced by the encoder, use the WebRTC stats report — specifically the RTCOutboundRtpStreamStats — via getRtcStatsReport(). For details on accessing stats across platforms, see How to Verify Scalable Video Is Working and Client Observability.
Valid scalability modes
The following modes are supported:
| Mode | Spatial layers | Temporal layers | Description |
|---|---|---|---|
L1T1 |
1 | 1 | Single resolution, single frame rate (no scalability) |
L1T2 |
1 | 2 | Single resolution, two frame rate levels |
L1T3 |
1 | 3 | Single resolution, three frame rate levels |
L2T1 |
2 | 1 | Two resolutions, single frame rate |
L2T2 |
2 | 2 | Two resolutions, two frame rate levels |
L2T3 |
2 | 3 | Two resolutions, three frame rate levels |
L3T1 |
3 | 1 | Three resolutions, single frame rate |
L3T2 |
3 | 2 | Three resolutions, two frame rate levels |
L3T3 |
3 | 3 | Three resolutions, three frame rate levels |
The format follows the W3C WebRTC SVC specification: L<spatial>T<temporal>, where the number after L is the count of spatial layers and the number after T is the count of temporal layers.
Any value outside this list is rejected with an error. If you have never called the set method (for example, setTargetScalabilityMode() on Web/Android or the targetScalabilityMode property on iOS/Windows), the corresponding get method returns an empty or null value (platform-dependent) — this is because the scalability mode is a target preference only, not an internally managed default. The SDK does not assume a default target on your behalf, so until you explicitly set one, there is no value to return.
Codec-dependent fallback
The mode that is actually applied depends on the codec negotiated on the Media Router media path:
- SVC-capable codecs (VP9) — the requested mode is applied as-is. These codecs support both spatial and temporal scalability layers natively.
- VP8 — VP8 supports temporal scalability only, with no spatial layers; spatial qualities are handled by publishing multiple streams at different resolutions (simulcast). The closest applicable mode is derived by preserving the temporal dimension and forcing spatial layers to 1. For example, if you set
L2T2and VP8 is negotiated,L1T2is applied. - H.264 — scalable video is not supported for H.264 in this API, so the fallback behavior described above does not apply to H.264 streams.
The get method always returns what you explicitly set (your intent), regardless of what was actually applied after codec-dependent fallback. For example, if you set L2T3 and VP8 is negotiated, the Media Router applies L1T3 (since VP8 has no spatial layers), but the get method still returns L2T3 — reflecting your original target, not the effective mode.
Interaction with subscriber preferred resolution and frame rate
The Media Router's layer-selection logic for Subscriber.setPreferredResolution() and Subscriber.setPreferredFrameRate() is built around the default scalability layer layout. When a publisher sets a non-default target scalability mode, the spatial and/or temporal layers actually produced by the encoder no longer match what the Media Router assumes is available, and the layer the router forwards to a subscriber may not correspond to the resolution or frame rate that subscriber requested.
Warning: When a publisher overrides the scalability mode with setTargetScalabilityMode() (or the equivalent property on iOS/Windows), subscribers calling setPreferredResolution() or setPreferredFrameRate() on that publisher's stream will not necessarily receive the requested resolution or frame rate. For example, requesting a lower preferred resolution with setPreferredResolution() may have no effect if the publisher's chosen mode does not expose a matching spatial layer, and a preferred frame rate may map to a different temporal layer than expected. If your application depends on subscriber-side preferred resolution/frame rate selection working predictably, leave the target scalability mode at its default.
Platform-specific API usage
The Publisher object includes setTargetScalabilityMode() and getTargetScalabilityMode() methods.
Setting the target scalability mode
Call setTargetScalabilityMode() on a Publisher object to set the target scalability mode. The method accepts a string identifying the desired mode:
publisher.setTargetScalabilityMode('L3T3');
This method may be called at any time — it does not require the media path negotiation to have completed. The mode will be applied once the Media Router media path is established.
The method throws an error if the requested mode is not valid (i.e. not one of L1T1, L1T2, L1T3, L2T1, L2T2, L2T3, L3T1, L3T2, L3T3):
try {
publisher.setTargetScalabilityMode('L3T3');
} catch (err) {
console.error('Invalid scalability mode:', err.message);
}
Getting the target scalability mode
Call getTargetScalabilityMode() to retrieve the value that was explicitly set. It returns undefined if the setter has never been called:
const mode = publisher.getTargetScalabilityMode();
console.log(mode); // 'L3T3' or undefined
Note: The getter returns the user's intent, not the mode actually applied. When a non-SVC codec (VP8 or H.264) is negotiated, the applied mode may differ (spatial layers are clamped to 1).
The PublisherKit class includes setTargetScalabilityMode() and getTargetScalabilityMode() methods.
Setting the target scalability mode
Call setTargetScalabilityMode() on a PublisherKit instance to set the target scalability mode:
publisher.setTargetScalabilityMode("L3T3");
This method may be called at any time — it does not require the media path negotiation to have completed. The mode will be applied once the Media Router media path is established.
The method throws an OpenTokException if the requested mode is not valid (i.e. not one of L1T1, L1T2, L1T3, L2T1, L2T2, L2T3, L3T1, L3T2, L3T3):
try {
publisher.setTargetScalabilityMode("L3T3");
} catch (OpenTokException e) {
Log.e(TAG, "Invalid scalability mode: " + e.getMessage());
}
Getting the target scalability mode
Call getTargetScalabilityMode() to retrieve the value that was explicitly set. It returns null if the setter has never been called:
String mode = publisher.getTargetScalabilityMode();
// "L3T3" or null
Note: The getter returns the user's intent, not the mode actually applied. When a non-SVC codec (VP8 or H.264) is negotiated, the applied mode may differ (spatial layers are clamped to 1).
The OTPublisherKit class includes the targetScalabilityMode property.
Setting the target scalability mode
Set the targetScalabilityMode property on an OTPublisherKit instance to set the target scalability mode:
try publisher.setTargetScalabilityMode("L3T3")
The setter may be called at any time — it does not require the media path negotiation to have completed. The mode will be applied once the Media Router media path is established.
The setter raises an OTError if the requested mode is not valid (i.e. not one of L1T1, L1T2, L1T3, L2T1, L2T2, L2T3, L3T1, L3T2, L3T3).
Getting the target scalability mode
Read the targetScalabilityMode property to retrieve the value that was explicitly set. It returns nil if the setter has never been called:
let mode = publisher.targetScalabilityMode
// "L3T3" or nil
Note: The getter returns the user's intent, not the mode actually applied. When a non-SVC codec (VP8 or H.264) is negotiated, the applied mode may differ (spatial layers are clamped to 1).
The OTPublisherKit class includes the targetScalabilityMode property.
Setting the target scalability mode
Set the targetScalabilityMode property on an OTPublisherKit instance to set the target scalability mode:
NSError *error = nil;
[publisher setTargetScalabilityMode:@"L3T3" error:&error];
The setter may be called at any time — it does not require the media path negotiation to have completed. The mode will be applied once the Media Router media path is established.
The setter raises an OTError if the requested mode is not valid (i.e. not one of L1T1, L1T2, L1T3, L2T1, L2T2, L2T3, L3T1, L3T2, L3T3).
Getting the target scalability mode
Read the targetScalabilityMode property to retrieve the value that was explicitly set. It returns nil if the setter has never been called:
NSString *mode = publisher.targetScalabilityMode;
// @"L3T3" or nil
Note: The getter returns the user's intent, not the mode actually applied. When a non-SVC codec (VP8 or H.264) is negotiated, the applied mode may differ (spatial layers are clamped to 1).
The Publisher class includes the TargetScalabilityMode property.
Setting the target scalability mode
Set the TargetScalabilityMode property on a Publisher instance to set the target scalability mode:
publisher.TargetScalabilityMode = "L3T3";
The setter may be called at any time — it does not require the media path negotiation to have completed. The mode will be applied once the Media Router media path is established.
The setter throws an OpenTokException if the requested mode is not valid (i.e. not one of L1T1, L1T2, L1T3, L2T1, L2T2, L2T3, L3T1, L3T2, L3T3):
try
{
publisher.TargetScalabilityMode = "L3T3";
}
catch (OpenTokException e)
{
Console.WriteLine("Invalid scalability mode: " + e.Message);
}
Getting the target scalability mode
Read the TargetScalabilityMode property to retrieve the value that was explicitly set. It returns null if the setter has never been called:
string mode = publisher.TargetScalabilityMode;
// "L3T3" or null
Note: The getter returns the user's intent, not the mode actually applied. When a non-SVC codec (VP8 or H.264) is negotiated, the applied mode may differ (spatial layers are clamped to 1).
The C SDK includes otc_publisher_set_target_scalability_mode() and otc_publisher_get_target_scalability_mode() functions.
Setting the target scalability mode
Call otc_publisher_set_target_scalability_mode() to set the target scalability mode for a publisher:
otc_status status = otc_publisher_set_target_scalability_mode(publisher, "L3T3");
if (status != OTC_SUCCESS) {
printf("Failed to set scalability mode\n");
}
This method may be called at any time — it does not require the media path negotiation to have completed. The mode will be applied once the Media Router media path is established.
The function returns OTC_INVALID_PARAM if:
- The publisher pointer is
NULL - The scalability mode string is
NULL - The requested mode is not valid (i.e. not one of
L1T1,L1T2,L1T3,L2T1,L2T2,L2T3,L3T1,L3T2,L3T3)
Getting the target scalability mode
Call otc_publisher_get_target_scalability_mode() to retrieve the value that was explicitly set. It returns NULL if the setter has never been called:
const char* mode = otc_publisher_get_target_scalability_mode(publisher);
if (mode != NULL) {
printf("Target scalability mode: %s\n", mode);
}
Note: The getter returns the user's intent, not the mode actually applied. When a non-SVC codec (VP8 or H.264) is negotiated, the applied mode may differ (spatial layers are clamped to 1).
Common Misconceptions and FAQs
Is Scalable Video the Same as VP9 SVC?
No. "Scalable video" is the name of the Vonage Video API feature. The technical mechanism depends on the codec:
- VP8 implements scalable video through simulcast — the publisher sends multiple independent streams.
- VP9 implements scalable video through SVC — the publisher sends one stream with embedded layers.
Both mechanisms let the Media Router adapt the quality each subscriber receives. When documentation mentions "scalable video" without specifying a codec, it refers to the feature as a whole.
Does Setting a Preferred Codec Force Scalable Video On?
No. Selecting a preferred codec (in the dashboard or via the preferredVideoCodecs publisher option) is independent of scalable video. Whether scalable video activates depends on all of the following:
- The scalable video application setting (On / Off / Auto).
- The session being routed.
- The negotiated codec supporting scalable video (VP8 or VP9 — not H.264).
- The publisher's client or browser supporting scalable video.
Why Is Subscriber.setPreferredResolution() Not Adapting Quality?
The subscriber-side setPreferredResolution() and setPreferredFrameRate() are hints to the Media Router, not direct commands to the publisher's encoder. The Media Router can only act on these hints when it is actively selecting between scalable video layers for that subscriber. If any of the following conditions is true, there are no layers to select from and the hints have no effect:
- Scalable video is disabled at the application level or on the publisher stream.
- The negotiated codec is H.264 (scalable video is unsupported).
- The publisher's client does not produce scalable layers (for example, a Firefox publisher sending VP9 without SVC layers).
- The publisher has a CPU or bandwidth constraint and is not sending higher layers for the router to select from.
- The publisher has set a non-default target scalability mode via
setTargetScalabilityMode()— the Media Router's layer selection is built around the default layout, so subscriber preferences may not map to the layer the subscriber expects. - The session is relayed — in a relayed session, the Media Router does not forward streams, so layer selection cannot occur. Note that in relayed (P2P) sessions, the publisher's encoder adapts its output directly based on the subscriber's network conditions, but this is not the same as scalable video layer selection.
Note: In relayed sessions, some SDKs may still accept these API calls without error, but the Media Router is not involved in stream forwarding so the preferences cannot be honored for quality-layer selection.
Note: Publisher-side resolution and frame rate APIs (where available in certain SDKs) behave differently — they directly control the encoded stream's resolution and frame rate, not a Media Router quality hint.
Can I Mix Scalable and Non-Scalable Publishers in the Same Session?
Yes. Scalable video is determined per stream, not per session. A session can simultaneously have publishers using scalable video and publishers that are not (for example, H.264 publishers, or clients publishing with scalable video disabled). The Media Router handles each stream independently.
Does Scalable Video Affect Archiving?
For VP8 simulcast, the archiver records using the highest available quality layer.
For VP9 SVC, individual archives store each participant's stream as VP9 SVC-encoded WebM. Composed archives are always transcoded to H.264/AAC MP4 regardless of the session codec.
Playback of VP9 SVC WebM files may not work in all media players. For playback instructions and transcoding commands, see Notes on Archiving in the VP9 guide.
For more information, see this support article.