i want to know which video quality is best to tran...
# ask-ai
w
i want to know which video quality is best to transfer if i have 500+ participants joined and simulcast is on
t
Thanks for reaching out, Jubin. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I’m responding to your question about which video quality to publish when you have 500+ participants and simulcast is enabled. Short answer: • Publish a moderate main quality (e.g., 720p) with two lower simulcast layers (360p and 180p). Enable simulcast + adaptive stream so subscribers get only the layer matching their bandwidth/display. • Use Dynacast (dynamic broadcast) or publisher-side layer suppression so unused layers aren’t sent to everyone. • Prefer H.264 for broad browser support unless you need SVC from VP9/AV1 (those have limited browser support). Recommended configuration (practical): 1. Capture/publish target: 720p (1280x720) as the highest layer. 2. Simulcast layers: 720p / 360p / 180p. 3. Bitrate targets (approximate per layer): ◦ 720p: 1.0–2.5 Mbps (adjust down if many publishers) ◦ 360p: 300–800 kbps ◦ 180p: 80–250 kbps 1. Enable adaptive stream on subscribers and visibility detection on clients so thumbnail/invisible tracks receive lower/no data. 2. Enable publisher optimizations (stop heavy processing, reduce frame rate) for low-powered devices. If you expect many simultaneous active cameras (many people publishing video), consider: • Lowering the top layer to 480p or reducing target bitrate for 720p. • Making most participants audio-only by default and enabling video on demand. • Using a broadcast/streaming workflow (SFU→CDN) for one-to-many scenarios instead of every participant subscribing to all streams. If you want, tell me: • Are most participants publishing video or mostly viewers? • Typical viewer device types (desktop vs mobile) and network quality expectations? I can refine the layer sizes and bitrate targets based on those details. Sources: Bringing Zoom's end-to-end optimizations to WebRTC | LiveKit | An introduction to WebRTC Simulcast | LiveKit | How we built a globally distributed mesh network to scale WebRTC | LiveKit | Configuring the Client SDK for Optimal Video Quality | client-sdk-android | livekit/livekit_composite | DeepWiki, with interactive elements
w
if i send 4k or 1080p quality to all user because they only see my video for 5 to 6 hours then what happen
t
Thanks — you’re asking what happens if you publish 4K or 1080p and every participant receives that high-quality video for 5–6 hours. Short answer: it will work technically, but it creates large and avoidable costs and performance problems for both you and your viewers. Key impacts • Bandwidth: Sending 1080p or 4K to hundreds of participants multiplies your outbound bandwidth. Example: a single 1080p stream at 2 Mbps × 500 viewers = ~1 Gbps sustained; 4K at higher bitrate is much larger. This drives up server/network costs and risks link saturation. • CPU and encoding load: Higher resolutions increase encoding CPU use on the publisher side and server-side processing if you transcode or manage layers—risking dropped frames or higher latency. • Viewer bandwidth & experience: Many viewers (mobile/limited networks) will struggle with sustained high bitrates leading to rebuffering, quality drops, or failed play. • Costs and scaling: Higher egress and processing = much higher cloud bills and may require bigger SFU/relay capacity. • Battery and device heat: Mobile viewers will consume more battery and may overheat if decoding high-bitrate 4K for hours. What to do instead (recommended) • Use simulcast + adaptive stream so each participant receives only the layer matching their display and bandwidth. • Publish a reasonable top layer (720p or 1080p if needed) rather than 4K; keep simulcast layers (e.g., 720/360/180). • Use Dynacast / layer suppression so unused layers aren’t sent to everyone. • Default many participants to audio-only; enable video on demand for those who need it. • Lower publisher frame rate (e.g., 15–30 fps) for long sessions to reduce bitrate. • Reduce bitrate targets for top layer if you must support many simultaneous viewers. If you want, tell me: • Are all 500+ participants actively viewing your video continuously, or are most passive viewers? • Do you need 4K for content detail (e.g., art/medical demos), or is 1080/720 acceptable? Give those details and I’ll recommend exact layer sizes and bitrate targets. , with interactive elements
w
which codec is usually used for mobile and desktop app fro flutter
t
Responding to your question about which codec to use for Flutter mobile and desktop apps. Short recommendation: • Use H.264 as the primary codec for both mobile (iOS/Android) and desktop when you need broad compatibility and hardware acceleration. • Use VP8 as a common fallback if H.264 isn’t available. • Use VP9 (or AV1) only when you control client support and want better compression—these have higher CPU cost and limited browser/device support. Why: • H.264 is widely supported and hardware-accelerated on most phones and desktops, so it’s power- and CPU-efficient. • VP8 is universally supported by WebRTC and a safe fallback. • VP9/AV1 give better quality-per-bit but have limited device/browser support and higher encoding/decoding cost. How to set it in Flutter: • Specify preferred codecs in Room/Publish options (e.g., prefer
h264
, then
vp8
) so negotiation picks the best supported codec on each client. If you tell me target platforms (iOS/Android/web/desktop) and whether you need maximum compatibility or best compression, I’ll suggest an exact codec priority list and sample config. Sources: WebRTC Codecs Guide | LiveKit | client-sdk-flutter | livekit/livekit_composite | DeepWiki | examples/other | livekit/livekit_composite | DeepWiki | Configuring the Client SDK for Optimal Video Quality, with interactive elements
w
Describe settings for sending high quality video to 1000 participants
t
Thanks — you're asking for settings to send high-quality video to 1,000 participants. Before I give concrete settings, I need one clarifying question so I recommend the right architecture and bitrate targets: Do you plan to have every participant subscribe directly (1:many via SFU) or will you use a broadcast/streaming workflow (SFU → CDN / RTMP) for one-to-many delivery? Tell me that and also: • Target resolution/frame rate you consider "high quality" (e.g., 1080p@30, 720p@30, 4K) • Expected viewer devices (mostly desktop, mobile, or mixed) • Whether most participants will also publish video or are viewers only With those details I’ll give exact codec, simulcast/Dynacast settings, layer resolutions & bitrate targets, and scaling recommendations. , with interactive elements
w
Target resolution would be 1080 mixed also publish video
t
Thanks — responding to your request for detailed settings to send 1080p video to ~1000 mixed-device participants where many also publish video. Summary recommendation (architecture first) • Use a broadcast/streaming workflow (SFU → CDN/relay) instead of every participant subscribing to every publisher. Sending 1080p from many publishers to 1,000 direct subscribers will create unsustainable egress, CPU, and client load. • For interactive participants (small subset who need real-time two‑way), keep them on the SFU. For large passive audience, route a single mixed/selected 1080p stream (or per-speaker streams) through a CDN/RTMP for one‑to‑many. Codec and negotiation • Primary codec: H.264 (hardware-accelerated on most mobile/desktop). • Fallback: VP8. Use VP9/AV1 only if you control client support and accept higher CPU cost. • Set preferred codecs in your Flutter Room/Publish options so negotiation chooses H.264 where available. Simulcast / layers / Dynacast • Enable simulcast on publishers and adaptive stream on subscribers. • Use three simulcast layers: 1080p / 720p / 360p (or 1080p/540p/270p if you need smaller steps). • Enable Dynacast (layer suppression) so unused layers aren’t sent to everyone. Suggested capture and layer resolutions (example) 1. Top layer: 1080p (1920×1080) — for viewers on large screens with sufficient bandwidth. 2. Mid layer: 720p (1280×720). 3. Low layer: 360p (640×360). Bitrate & framerate targets (per published track) • 1080p: 2.5–4.0 Mbps @ 24–30 fps (use lower end if many simultaneous publishers). • 720p: 1.0–2.0 Mbps @ 24–30 fps. • 360p: 150–500 kbps @ 15–24 fps. • For long sessions or many publishers, consider 15–24 fps for 1080/720 to reduce load. Publisher defaults & client behavior • Set publisher defaults to prefer H.264 and the above simulcast layers. • Lower publisher CPU load: enable hardware encoding where available, reduce frame rate when device constrained, and disable heavy effects (background blur) for long sessions. • Use visibility detection and adaptive stream so clients only receive visible tracks at needed resolution. Scaling & deployment recommendations • Offload 1:many viewers to a CDN via SFU → CDN/RTMP. Deliver a single high-quality stream to the CDN rather than 1,000 individual SFU streams. • If many participants publish video simultaneously, limit the number of active camera streams per room (e.g., spotlight N active publishers) and make others audio-only by default. • Autoscale SFU capacity and ensure ample egress bandwidth (estimate sustained egress for each outgoing stream you serve to many viewers). • Monitor CPU, memory, and network metrics and throttle top-layer bitrates if needed. Additional practical optimizations • Use adaptive bitrate/congestion control and DTX for audio. • Consider server-side mixing or selective forwarding for small-group interaction; use CDN for mass distribution. • Test with representative device/network mixes (mobile on 4G, low-bandwidth desktops) and adjust bitrate targets accordingly. • For recording/archiving, transcode once for the CDN rather than recording many high-res tracks. If you want, I can: • Provide exact RoomOptions/PublishOptions sample config for Flutter with
videoCodecs
, simulcast layer settings, and bitrate limits. • Estimate bandwidth and server sizing given X simultaneous active publishers and Y viewers on CDN — tell me how many active cameras you expect concurrently and whether you control a CDN provider., with interactive elements
w
give code for codec h264
t
Responding to your request for Flutter code to publish video using H.264. Example Flutter (LiveKit) snippet — sets H.264 as preferred codec, enables simulcast, and configures layer presets and bitrates:
Copy code
import 'package:livekit_client/livekit_client.dart';

Future<void> publishWithH264(Room room) async {
  // Ensure room was connected already

  // Create camera track at 1080p capture
  final videoTrack = await LocalVideoTrack.createCameraTrack(
    options: const CameraCaptureOptions(
      resolution: VideoResolution.high1080p,
    ),
  );

  // Set room-level options (adaptive stream + dynacast)
  // (If you set these when connecting, skip this)
  room.setOptions(const RoomOptions(
    adaptiveStream: true,
    dynacast: true,
  ));

  // Publish with H.264, simulcast layers, and bitrate/framerate limits
  await room.localParticipant.publishTrack(
    videoTrack,
    publishOptions: const TrackPublishOptions(
      // Prefer H.264; fallback to vp8 if needed
      videoCodecs: ['h264', 'vp8'],
      simulcast: true,
      // These fields are SDK-typed examples; adapt names if your SDK version uses different fields
      videoSimulcastLayers: [
        VideoSimulcastLayerPreset.p1080, // top layer 1080p
        VideoSimulcastLayerPreset.p720,  // mid layer 720p
        VideoSimulcastLayerPreset.p360,  // low layer 360p
      ],
      videoEncoding: VideoEncoding(
        maxBitrate: 2500000, // ~2.5 Mbps for 1080p
        maxFramerate: 30,
      ),
    ),
  );
}
Notes: • H.264 is listed first in
videoCodecs
so it’s negotiated when available. • Keep
adaptiveStream
and
dynacast
enabled so subscribers only receive needed layers. • Adjust
maxBitrate
/ framerate lower if many simultaneous publishers or constrained devices. • If your LiveKit Flutter SDK version uses different enum/field names, replace
VideoSimulcastLayerPreset
/
VideoEncoding
with the equivalent types from your SDK. Tell me your SDK version if you want a copy tailored to your exact LiveKit Flutter package API. Sources: client-sdk-flutter | livekit/livekit_composite | DeepWiki | A tale of two protocols: comparing WebRTC against HLS for live streaming | ... | Bringing Zoom&#39;s end-to-end optimizations to WebRTC | LiveKit | Configuring the Client SDK for Optimal Video Quality | WebRTC Codecs Guide | LiveKit, with interactive elements
w
what benifit to define vp8 as fallback if it send h264 but some did not support then vp8 support?
t
Good question — here's why listing VP8 as a fallback is useful. • Compatibility: Some clients/devices or browser builds may not support H.264 or may have bugs with it. If H.264 fails during negotiation, having VP8 as a fallback ensures the call still connects. • Graceful degradation: If H.264 isn’t available, VP8 lets the session continue without forcing users to reconfigure or lose the stream. • No client-side switching required: Codec negotiation happens automatically during the WebRTC handshake. If the remote side can’t decode H.264, the peers will agree on VP8 instead. • Broad WebRTC support: VP8 is universally supported by WebRTC stacks, so it’s a safe fallback for older or niche clients. • Developer control: Listing codecs in priority order (e.g.,
['h264','vp8']
) lets you prefer hardware-accelerated H.264 when possible while still guaranteeing connectivity via software-backed VP8 when necessary. In short: VP8 as a fallback preserves compatibility and uptime — you get H.264’s efficiency where supported and VP8’s universal reach where it isn’t. , with interactive elements
w
if i send using h264 some user did not has h264 is it can see video by using vp8??
t
Short answer: No—both sides must agree on a common codec. If you only offer H.264 and a subscriber cannot decode H.264, that subscriber will not see the video. What to do: • Publish with a codec priority list like
['h264','vp8']
. During WebRTC negotiation, peers will pick the first mutually supported codec. • That way, clients that support H.264 will use it, and clients that don’t will automatically fall back to VP8 and still receive the video. If you want, I can show the exact Room/PublishOptions snippet for your Flutter SDK version to ensure proper codec ordering. , with interactive elements
w
i set h264 codec and fallback vp8. 500 users are watching my video. suppose 450 users' device support h264 and rest support vp8. what will happen now
t
Thanks — answering what happens when you publish with
['h264','vp8']
and 450 viewers support H.264 while 50 only support VP8. • Negotiation: During WebRTC SDP negotiation the server/clients pick a common codec per peer. For each subscriber, the SFU will send the stream in the highest-priority codec that both sides support. • Result: 450 viewers will receive H.264; the 50 that don’t support H.264 will receive VP8. All viewers can see the video (no manual switching needed). • How it’s delivered: The SFU manages sending the appropriate encoded stream to each subscriber. You only need to publish once with codec priority; the SFU handles per-subscriber negotiation and routing. • Resource/quality impact: ◦ Encoding: If you publish only H.264, the SFU may need to transcode to VP8 for the 50 non-H.264 viewers (transcoding increases CPU and costs). If your SFU supports per-subscriber re-encoding or codec transcodes, expect higher server CPU/latency. ◦ Bandwidth: Subscribers get the bitrate for their chosen codec/layer; overall egress is similar, but server CPU and possibly memory increase if transcoding is required. • Recommendation: ◦ Prefer negotiating codecs so publishers can send H.264 and the SFU will avoid transcoding when possible. ◦ If you expect many mixed-codec viewers and want to avoid server transcoding, consider publishing both codecs (if your pipeline allows) or ensure your SFU/hosting plan can handle transcoding load. ◦ Monitor SFU CPU if you see many VP8-fallback subscribers. If you want, tell me whether your SFU is configured to transcode or if you control server capacity, and I’ll recommend concrete options to minimize CPU/egress impact. , with interactive elements
w
in flutter? are u saying that a device can send multiple streams encoded with differnt codec?
t
Good question — here’s a concise clarification for Flutter + LiveKit. • A single published video track is encoded with one codec. You provide a codec priority list (e.g.,
['h264','vp8']
) for negotiation, but that list does not make the publisher send multiple codecs simultaneously. • During WebRTC negotiation, each subscriber and the SFU pick a mutually supported codec. If the subscriber supports H.264, they receive H.264. If they only support VP8, the SFU and peer must agree on VP8. • Two ways to handle mixed-support clients: ◦ The SFU transcodes from H.264 → VP8 for those subscribers (this costs CPU and adds latency). ◦ Or the publisher explicitly publishes a second track encoded as VP8 (rare and more complex). • Practical default: publish once with
['h264','vp8']
, let negotiation choose H.264 where possible, and ensure your SFU can transcode or plan for VP8-only clients. If you want, I can show the exact Flutter publish snippet that sets codec priority and explain how to detect whether transcoding occurred. , with interactive elements
w
yes
t
Great — below is a concise Flutter example showing how to publish a single video track preferring H.264 with VP8 fallback, plus how to check which codec was negotiated for a published track. This clarifies that a published track uses one codec; the SFU will transcode if a subscriber requires a different codec. 1) Publish with H.264 preferred, VP8 fallback
Copy code
import 'package:livekit_client/livekit_client.dart';

Future<void> publishPreferredH264(Room room) async {
  // create camera track (1080p example)
  final videoTrack = await LocalVideoTrack.createCameraTrack(
    options: const CameraCaptureOptions(
      resolution: VideoResolution.high1080p,
    ),
  );

  // ensure adaptive/dynacast enabled if desired
  room.setOptions(const RoomOptions(adaptiveStream: true, dynacast: true));

  // publish with codec priority: h264 first, then vp8
  await room.localParticipant.publishTrack(
    videoTrack,
    publishOptions: const TrackPublishOptions(
      videoCodecs: ['h264', 'vp8'],
      simulcast: true,
      videoEncoding: VideoEncoding(maxBitrate: 2500000, maxFramerate: 30),
    ),
  );
}
2) Check the negotiated codec for a published track
Copy code
// after publishing, inspect the published track's stats/codec
void printPublishedCodec(LocalTrackPublication? pub) {
  if (pub == null) return;
  final codec = pub.codec; // SDK exposes negotiated codec on publication (name may vary by version)
  print('Published track negotiated codec: $codec');
}
Notes • A single published track is encoded in one codec. Listing
['h264','vp8']
tells WebRTC to prefer H.264 but fall back to VP8 if the remote side cannot do H.264. • The SFU may transcode server-side to deliver the stream in VP8 to clients that lack H.264 support; transcoding costs CPU and adds latency. • If you need to avoid server transcoding, you would need to publish a second track encoded as VP8 (more complex) or ensure subscriber devices support H.264. • If you share your LiveKit Flutter SDK version, I can tailor the snippet to exact API names (some field/property names differ between versions)., with interactive elements
w
if i dont define codec which will use as main and fallback
t
Responding to your question: if you don't define codecs, which codec is used as main and fallback? • Default behavior: VP8 is used as the primary codec in most clients when you don't set a preference. • Negotiation: During the WebRTC handshake, peers pick the first mutually supported codec. If a client supports H.264 and VP8, the implementation may still prefer VP8 by default unless you explicitly set a codec priority. • Result: Some clients may end up using H.264 if VP8 isn’t available, or VP8 if H.264 isn’t available — the final codec is whatever both sides support. • Note on transcoding: If a publisher uses one codec and a subscriber only supports another, the SFU must transcode to deliver the stream, which increases CPU and latency. If you want deterministic behavior, set codec priority explicitly (e.g.,
['h264','vp8']
) when connecting/publishing. Sources: WebRTC Codecs Guide | LiveKit | Client SDKs | livekit/livekit_composite | DeepWiki | Yeah, but can it run DOOM? | LiveKit | Bringing Zoom&#39;s end-to-end optimizations to WebRTC | LiveKit | An introduction to WebRTC Simulcast | LiveKit, with interactive elements
w
for some mobile user they got black screen instead of video other got the video what would be the issue
t
Sorry you’re seeing that — I can help narrow it down. A few quick questions so I can diagnose: • Which mobile platforms/OS versions show the black screen (iOS, Android, specific versions)? • Which LiveKit Flutter SDK version and underlying
flutter_webrtc
version are you using? • Is this only when publishing with H.264 (or another codec), or does it happen with VP8 too? • Are
adaptiveStream
and
dynacast
enabled? Are you using simulcast? • Do affected users see audio only (no video), a frozen/black frame, or a failed track entirely? • Any console/device logs or LiveKit stats (packet loss, decode errors) available? In the meantime, try these targeted checks: • Verify codec support: ensure the device/browser supports the negotiated codec (H.264) and hardware acceleration where possible. • Check track state: confirm the Local/Remote track is `muted`/`enabled` and not
stopped
. • Inspect video element: confirm the video element is attached, visible, and not CSS-hidden or zero-sized (visibility detection can pause rendering). • Confirm simulcast/adaptive behavior: disabled/incorrect layer selection can send only a high layer that the device can’t decode — try forcing a lower layer for the affected client. • Look at network/bitrate: extreme packet loss or very low bandwidth can produce black frames — check WebRTC stats for frame decode errors and incoming bitrate. • Check platform-specific issues: some Android devices and older iOS versions have H.264 hardware decode bugs — test forcing VP8 for an affected device. • Review logs: inspect client console and device logs for decoder errors (e.g., “unable to decode frame” or codec negotiation failures). If you share the platform/SDK version and any logs or WebRTC stats for an affected client, I’ll point to the most likely root cause and exact fixes (e.g., force VP8 for that device, change simulcast layers, or enable hardware encode/decode flags). , with interactive elements
w
Android 15
Copy code
livekit_client: ^2.6.0
publish with vp8 both adaptive stream and dynacast enable only audio is seen
t
Thanks — I’m responding to your Android 15 + LiveKit Flutter 2.6.0 issue where VP8 publishes produce audio only (black screen) while others see video. Try these steps in order and tell me results/logs: 1) Force H.264 publish (quick test)
Copy code
await room.localParticipant.publishTrack(
  videoTrack,
  publishOptions: const TrackPublishOptions(
    videoCodecs: ['h264','vp8'],
    simulcast: true,
  ),
);
If video shows for affected users, VP8 encoding/decoding on Android 15 is the culprit. 2) Temporarily disable adaptiveStream & dynacast • Reconnect with RoomOptions(adaptiveStream: false, dynacast: false) • If video returns, visibility/layer suppression is pausing the stream for those clients. 3) Check video track & subscription state • Confirm encoder is producing frames (local): videoTrack.onFrame or video stats show framesEncoded > 0. • Confirm remote participants see a subscribed video track (onTrackSubscribed events). 4) Collect WebRTC stats • Inspect incoming/outgoing video stats for an affected client: ◦ bytesSent/bytesReceived ◦ framesEncoded/framesDecoded ◦ decodeErrors Share those numbers if you can. 5) Verify flutter_webrtc compatibility • Ensure your flutter_webrtc version is up-to-date and built for Android 15. Older builds can fail on new Android releases. 6) Test on multiple Android 15 devices • Note device make/model. If it’s OEM-specific (e.g., Pixel vs Samsung), that suggests a vendor codec bug. 7) Workarounds if VP8 is failing • Prefer H.264 for Android 15 users. • If you must support VP8-only viewers, ensure SFU-side transcoding is enabled (expect higher CPU/latency) or publish an additional VP8 track (complex). If you can, share: • One affected device model + exact Android build • Relevant WebRTC stats (framesEncoded/Decoded, bytesSent/Received, decodeErrors) • Any client logs showing codec negotiation or decoder errors I’ll use those to pinpoint the root cause and give the exact fix. Sources: Client SDKs | livekit/livekit_composite | DeepWiki | Configuring the Client SDK for Optimal Video Quality | client-sdk-flutter | livekit/livekit_composite | DeepWiki | Bringing Zoom&#39;s end-to-end optimizations to WebRTC | LiveKit | examples/other | livekit/livekit_composite | DeepWiki, with interactive elements
w
same video sometime video show sometime show black screen i think when users 150+ then it see black screen to some users
t
Thanks — I hear that video intermittently shows as a black screen for some users once you have ~150+ participants. I’ll help narrow this down and give targeted steps. Quick diagnostic questions • Are affected users mostly Android 15 only, or mixed devices? any common device models? • Are the black-screen users mostly remote viewers (not publishing) or also publishers? • Is the SFU/server self-hosted or LiveKit Cloud? Any autoscaling in use? • Can you attach WebRTC stats for an affected viewer (framesDecoded, bytesReceived, decodeErrors, packetLoss)? Immediate checks and actions (try in this order) 1. Force H.264 for a test group ◦ Temporarily publish with
['h264','vp8']
. If black screens disappear, VP8 encoding/decoding or device-specific VP8 bugs are likely. 1. Disable adaptiveStream & dynacast briefly ◦ Reconnect with
RoomOptions(adaptiveStream: false, dynacast: false)
. If video appears, layer suppression or visibility logic is cutting layers for some viewers. 1. Reduce top-layer bitrate / framerate ◦ Lower 1080p → 720p or reduce 30fps → 15–24fps for many publishers to reduce encoder load and packet loss. 1. Limit active video publishers ◦ Spotlight / allow only N active camera streams (others audio-only) to reduce SFU load and client decoding demand. 1. Check SFU CPU / network / egress ◦ High SFU CPU or saturated network causes dropped frames or failed transcoding. If self-hosted, monitor CPU, memory, and NIC utilization and scale out. 1. Inspect WebRTC stats on affected clients ◦ Look for framesDecoded==0, decodeErrors>0, very low bytesReceived, or high packetLoss. Share these values if you want help interpreting them. 1. Test simulcast layer selection ◦ Force subscribers to select a lower simulcast layer (if your SDK allows) to check if only high-layer delivery fails. Longer-term mitigations • Use H.264 as primary codec for Android-heavy audiences to avoid VP8 device bugs. • Offload large audiences to SFU→CDN (one high-quality stream to CDN) to avoid 1:many egress and client overload. • Enable autoscaling for SFU nodes and ensure sufficient egress bandwidth. • Implement adaptive UI: auto-disable remote video thumbnails or reduce resolution when participant count grows. If you can provide: • an example WebRTC stats snapshot from an affected viewer, • one or two affected device models, • whether your SFU is self-hosted or LiveKit Cloud, I’ll analyze the stats and point to the most likely root cause and exact configuration changes to fix it. , with interactive elements
w
pls understand our scenario: we may have 500 to 1000 viewers plus 1 presenter. users can see only prenter's video. user will subscribe only that single video and rest will remain unsubscribe. the presenter can see max 100 users' video in a grid. we are using pagination. subscribing to only the videos which are currently in the grid and rest will remian unsubscribe.
t
Thanks — I understand your scenario: 1 presenter sent to 500–1000 viewers (viewers subscribe only to the presenter), and the presenter subscribes to a paginated grid of up to 100 participant videos (only current page subscribed). Below are concise, actionable recommendations (codec, simulcast/layers, bitrates, scaling, and fixes for intermittent black screens). Architecture & workflow • Use SFU for interactive part (presenter + presenters’ subscribers). For the 500–1000 passive viewers, stream the presenter's high-quality output to a CDN (SFU → CDN/RTMP) for one-to-many delivery to reduce SFU egress and client load. • Keep presenter publishing once (simulcast) and let SFU/CDN handle distribution. Viewers subscribe only to presenter’s track (no needless subscriptions). Codec • Primary: H.264 (hardware-accelerated on most mobile/desktop). Fallback: VP8. • Publish with codec priority: ['h264','vp8'] so H.264 is used where available and VP8 as fallback. • If many mixed-support viewers force server transcoding, prefer H.264 to minimize client decode problems on mobile. Simulcast, adaptiveStream, dynacast • Enable simulcast on the presenter and adaptiveStream + dynacast on room: ◦ Simulcast layers: 1080p / 720p / 360p (or 1080/540/270 if you prefer smaller mid-step). ◦ adaptiveStream ensures each viewer gets only the layer matching their viewport/bandwidth. ◦ dynacast prevents sending unused layers to everyone. • Viewers subscribe only to the presenter’s optimal layer automatically via adaptiveStream. Bitrate & framerate guidance (presenter track) • 1080p: 2.5–4 Mbps @ 24–30 fps (use 2.5 Mbps if many viewers to lower egress). • 720p: 1.0–2.0 Mbps @ 24–30 fps. • 360p: 150–500 kbps @ 15–24 fps. • For long streams or constrained networks, prefer 15–24 fps to save bandwidth. Presenter subscribing to grid (100 tiles) • Use pagination and only subscribe to visible tiles (you already do this). • For the presenter UI, request lower-resolution layers for thumbnails; request higher layer only for active speakers. • Limit concurrent decoded streams to what the presenter's device can handle (many devices struggle above ~25–40 simultaneous decodes). If presenter must see 100, reduce each tile to very low resolution (180–240p) and low framerate (10–15 fps), or use server-side mixed/preview composites. Scaling & server considerations • Offload mass viewers to a CDN. Deliver a single high-quality stream to the CDN rather than 500–1000 individual SFU sends. • Ensure SFU autoscaling and sufficient egress capacity if you keep many direct viewers. • Monitor SFU CPU, memory, and network; transcoding (H.264↔️VP8) is CPU-intensive—plan capacity accordingly. • Consider generating a server-side composite for presenter’s grid (if presenter device can’t decode 100 streams). Black-screen intermittent issues (especially Android 15 / VP8) • Symptoms you described (intermittent black screens at scale) often point to: ◦ Device codec limitations or VP8 hardware/software encoder bugs. ◦ SFU overload, packet loss, or layer suppression due to adaptiveStream/dynacast. ◦ Client-side decoding saturation (too many concurrent decodes). Actionable fixes: 1. Force H.264 for presenters and Android-heavy viewers as a test (H.264 tends to be more stable on Android). 2. Lower top-layer bitrate and framerate to reduce packet loss under load. 3. Keep adaptiveStream/dynacast enabled but test disabling them briefly to isolate layer suppression issues. 4. Ensure viewers only subscribe to the single presenter track (avoid accidental subscriptions). 5. Monitor WebRTC stats on affected clients: framesDecoded, bytesReceived, decodeErrors, packetLoss. Share these if you want help diagnosing specific failures. 6. Update flutter_webrtc and LiveKit SDK to latest versions built for Android 15. Flutter publish example (concept) • Publish presenter with H.264 priority, simulcast, and bitrate limits:
Copy code
await room.localParticipant.publishTrack(
  videoTrack,
  publishOptions: const TrackPublishOptions(
    videoCodecs: ['h264','vp8'],
    simulcast: true,
    videoSimulcastLayers: [
      VideoSimulcastLayerPreset.p1080,
      VideoSimulcastLayerPreset.p720,
      VideoSimulcastLayerPreset.p360,
    ],
    videoEncoding: VideoEncoding(maxBitrate: 2500000, maxFramerate: 24),
  ),
);
room.setOptions(const RoomOptions(adaptiveStream: true, dynacast: true));
(Adjust API names to your SDK version if needed.) Operational checklist before large event • Verify presenter publishes stable 1080p at target bitrate on representative networks. • Load-test SFU with expected viewer counts or verify CDN pipeline. • Confirm affected Android devices work with H.264 and updated flutter_webrtc. • Limit active camera publishers; keep others audio-only by default. • Prepare fallback: reduce bitrate or switch presenter to 720p if SFU/CDN shows stress. If you want, I can: • Provide an exact Flutter code snippet tailored to your LiveKit SDK version for publishing and subscriber control. • Estimate SFU/CDN capacity given expected viewer count and bitrate if you tell me whether you’ll use CDN and how many simultaneous active publishers you expect., with interactive elements
w
what about a normal user simulcast layers: where user video will be seen in a 100 items grid and pinned/unpinned some times
t
Thanks — responding to where a normal user's simulcast layers should map when their video appears in a 100‑item grid and is sometimes pinned/unpinned. Recommended strategy (user publishes once with simulcast; subscribers request layers based on view size/state) 1) Simulcast layers (publisher) • Three layers: high / medium / low ◦ High: 1080p (1920×1080) — for pinned / spotlight view ◦ Medium: 720p (1280×720) — for single large tile or speaker view ◦ Low: 180–360p (320×180 or 640×360) — for grid thumbnails • Keep publisher capture at the highest needed (e.g., 1080p) and let simulcast create the lower layers. 2) Bitrate & framerate targets (per layer) • High (1080p): 2.5–4 Mbps @ 24–30 fps (use lower end if many publishers) • Medium (720p): 1.0–2 Mbps @ 24–30 fps • Low (360p or 180p): 80–400 kbps @ 10–15 fps • For a 100‑tile grid, use low resolution + low fps to limit decode and CPU load on the viewer. 3) Subscriber behavior (presenter/viewer who displays the grid) • Subscribe only to visible tiles (you already do). • For grid tiles: request the low simulcast layer. • When a tile is pinned/unpinned: ◦ On pin: request high (or medium) layer for that user. ◦ On unpin: downgrade back to low. • Use adaptiveStream so the SFU/SDK automatically selects layers by viewport size; explicitly override layer selection for pin/unpin transitions for faster, deterministic behavior. 4) UI & UX rules • Debounce rapid pin/unpin actions (e.g., 300–500 ms) to avoid frequent layer switches. • Preload higher layer when user long-presses or hovers (optional) to reduce perceived lag. • For pinned users, allow a short grace period where both high and mid layers are requested then drop the unused one via dynacast. 5) Presenter/grid device constraints • Be conservative with decoded streams: many devices can decode dozens but not hundreds of simultaneous videos. For a 100‑tile grid: ◦ Keep tiles at 180–240p and 10–15 fps. ◦ Consider server-side compositing if the presenter device cannot decode 100 streams smoothly. • Limit active video publishers; others remain audio-only or send very low-resolution video. 6) Simulcast + Dynacast + AdaptiveStream settings • Enable all three: ◦ Simulcast: publish multiple layers. ◦ Dynacast: stop sending unused layers to reduce upstream. ◦ AdaptiveStream: subscriber-side automatic layer selection by size/visibility. • Explicitly select layer on pin/unpin to override adaptive heuristics when needed. 7) Fail-safes for black-screen/quality issues • If many viewers see black frames at scale: ◦ Lower top-layer bitrate/fps. ◦ Force H.264 for Android-heavy audiences. ◦ Ensure SFU has headroom (CPU/egress) and autoscaling. ◦ Monitor framesDecoded, decodeErrors, packetLoss for affected clients. If you want, I can produce: • Exact LiveKit/Flutter PublishOptions and subscriber-selection code for pin/unpin handling tailored to your SDK version. Which SDK version are you using?, with interactive elements
w
can we send less or more than 3 layers in simulcast
t
Yes — you can publish fewer or more than the default three simulcast layers. Summary and trade-offs • Fewer layers (e.g., 1–2): lower CPU/encoder overhead and less upstream bandwidth, but less flexibility for subscribers to adapt to different view sizes and networks. • More layers (4+): finer-grained adaptation (better quality matching) at the cost of higher encoder complexity and more upstream bandwidth. • Typical sweet spot: 2–3 layers for most apps (e.g., thumbnail, tile, spotlight). Use 4+ only if you have strict quality targets and can absorb extra CPU/bandwidth. How to configure in Flutter (concept) • The LiveKit Flutter SDK lets you enable simulcast and specify the simulcast layers or presets when publishing. API names vary by SDK version; below are two example patterns you can adapt. 1) Two-layer example (high + low)
Copy code
await room.localParticipant.publishTrack(
  videoTrack,
  publishOptions: const TrackPublishOptions(
    simulcast: true,
    videoCodecs: ['h264','vp8'],
    videoSimulcastLayers: [
      VideoSimulcastLayerPreset.p720, // high
      VideoSimulcastLayerPreset.p180, // low / thumbnail
    ],
    videoEncoding: VideoEncoding(maxBitrate: 1500000, maxFramerate: 24),
  ),
);
2) Four-layer example (very fine granularity)
Copy code
await room.localParticipant.publishTrack(
  videoTrack,
  publishOptions: const TrackPublishOptions(
    simulcast: true,
    videoCodecs: ['h264','vp8'],
    videoSimulcastLayers: [
      VideoSimulcastLayerPreset.p2160, // 4K/top (if needed)
      VideoSimulcastLayerPreset.p1080, // high
      VideoSimulcastLayerPreset.p720,  // medium
      VideoSimulcastLayerPreset.p360,  // low / thumbnail
    ],
    videoEncoding: VideoEncoding(maxBitrate: 5000000, maxFramerate: 30),
  ),
);
Practical guidance • Start with 2–3 layers; measure bandwidth/CPU and viewer quality. Add layers only if you need smoother transitions or more size-specific layers. • Ensure
dynacast
is enabled so unused layers aren’t sent to everyone. • Keep low/resolution layers very small (low bitrate, low fps) for large grids to reduce decode load. • Test device compatibility (some devices/OSes have encoder limits that affect many layers). If you tell me your LiveKit Flutter SDK version, I’ll give the exact field/enum names for that release and a recommended 2‑ or 3‑layer config tuned to your presenter/grid scenario. , with interactive elements
w
which codec is best for mobile and desktop
t
Short answer: H.264 is the best general choice for both mobile and desktop. Why: • Broad device/browser support and widespread hardware acceleration → lower CPU, better battery life, and more reliable playback on phones and desktops. • Reduces decoding issues on many Android/iOS devices compared with VP8/VP9. • Good balance of quality and compatibility for large audiences. Fallbacks / when to choose others: • Use VP8 as a fallback when H.264 isn’t available (universal WebRTC support). • Use VP9 (or AV1) only if you control the client fleet and need better compression; these have higher CPU cost and limited support (especially on iOS/Safari). Practical tip: • Publish with codec priority:
['h264','vp8']
so H.264 is preferred and VP8 used when necessary., with interactive elements
w
simulcast on or off
t
Short answer: Turn simulcast ON. Why (brief): • Simulcast lets the publisher send multiple resolution layers so each viewer receives only the layer that fits their viewport and bandwidth — critical when you have large grids and mixed devices. • It reduces overall bandwidth and improves reliability because thumbnails/grid tiles get low-res layers while pinned/spotlight users get high-res. • Combined with adaptiveStream + dynacast, it prevents sending unused layers and minimizes client/server load. Trade-offs: • Small upstream CPU and bandwidth overhead on the publisher for extra layers. • Slightly more complexity in handling layer selection on the subscriber side. • More layers mean more encoder work; keep layers to 2–3 in most cases. Practical recommendation for your scenario (presenter → 500–1000 viewers, paginated 100‑tile grids): • Enable simulcast + adaptiveStream + dynacast. • Use 3 layers: High (1080p), Mid (720p), Low (360p or 180p for 100-tile grid). • Prefer H.264 with VP8 fallback. • Keep presenter top-layer bitrate moderate (≈2.5 Mbps) and reduce fps (15–24) if needed. • For grid tiles, explicitly request the low layer; on pin, switch to mid/high. • If publisher/device resources are constrained, reduce to 2 layers (high + low) to lower encoder cost. Example Flutter concept (adjust to SDK version):
Copy code
room.setOptions(const RoomOptions(adaptiveStream: true, dynacast: true));

await room.localParticipant.publishTrack(
  videoTrack,
  publishOptions: const TrackPublishOptions(
    videoCodecs: ['h264','vp8'],
    simulcast: true,
    videoSimulcastLayers: [
      VideoSimulcastLayerPreset.p1080,
      VideoSimulcastLayerPreset.p720,
      VideoSimulcastLayerPreset.p360,
    ],
    videoEncoding: VideoEncoding(maxBitrate: 2500000, maxFramerate: 24),
  ),
);
If you want, I can give the exact API names for your LiveKit Flutter SDK version and a tuned 2‑ or 3‑layer config for presenter and grid tiles. , with interactive elements
w
is this true?? Can H264 be used with Simulcast? Yes, but support is limited and inconsistent. CodecSimulcast SupportReliabilityVP8✅ Full⭐⭐⭐⭐⭐H264⚠️ Partial⭐⭐⭐VP9❌ Not typical (uses SVC instead)⭐⭐⭐⭐ Most WebRTC systems prefer VP8 for simulcast. Why H264 Simulcast is Problematic 1️⃣ Hardware encoder limitation Most phones have only one H264 hardware encoder instance. Simulcast requires multiple encoders simultaneously: Example layers: LayerResolutionLow180pMid360pHigh720p For H264 this means:
Copy code
3 encoders running simultaneously
Many devices cannot do that. 2️⃣ Safari / iOS restrictions Apple supports H264 strongly, but simulcast behavior varies across devices and Safari versions. 3️⃣ LiveKit recommendation LiveKit documentation and WebRTC practice recommend:
Copy code
VP8 + simulcast
instead of
Copy code
H264 + simulcast
because it is more stable across platforms. What Happens if You Enable It? Example config:
Copy code
VideoPublishOptions(
  videoCodec: 'h264',
  simulcast: true,
)
Possible results: ResultReasonOnly 1 layer sentdevice encoder limitSimulcast disabled automaticallyWebRTC fallbackCodec switches to VP8negotiation fallback This might be why you saw VP8 in stats earlier. Best Configurations (Production) Zoom / LiveKit style meetings
Copy code
VideoPublishOptions(
  videoCodec: 'vp8',
  simulcast: true,
)
Best for: • large meetings • adaptive quality • bandwidth control Mobile optimized
Copy code
VideoPublishOptions(
  videoCodec: 'h264',
  simulcast: false,
)
Best for: • 1-to-1 calls • low CPU Recommended for Your App Since you mentioned earlier: • 100 videos simultaneously • Zoom-like meeting • LiveKit Use:
Copy code
VideoPublishOptions(
  videoCodec: 'vp8',
  simulcast: true,
)
This allows LiveKit to send 3 layers: LayerResolutionBitrateLow180p~150 kbpsMedium360p~400 kbpsHigh720p~1.5 Mbps Subscribers only receive the layer they need.
t
Yes — your summary is correct. Key points: • H.264 can be used with simulcast, but support is limited and inconsistent across devices. Many mobile devices expose only one H.264 hardware encoder instance, so producing multiple simultaneous H.264 layers may fail or fall back to a single layer. • VP8 is the most reliable choice for simulcast. It’s widely supported across browsers/devices and stable for multi-layer simulcast in large meetings. • VP9/AV1 use SVC (not traditional simulcast) and have limited cross-platform support (especially Safari/iOS), so they’re not a drop‑in simulcast replacement. • Practical guidance: ◦ For large grids and adaptive delivery: use VP8 + simulcast (2–3 layers) with adaptiveStream + dynacast. ◦ For mobile-optimized 1:1 or when hardware acceleration is paramount: prefer H.264 but avoid H.264 simulcast on devices that can’t encode multiple layers. ◦ If you enable H.264 simulcast, expect device-dependent behavior (sometimes only one layer sent or fallback to VP8), and monitor for increased server transcoding if subscribers require a different codec. If you want, I can: • Provide the exact LiveKit/Flutter code to prefer VP8 simulcast or to prefer H.264 (single-layer) depending on device detection. • Help you detect device encoder capabilities at runtime and choose codec/layers automatically. Which SDK version are you on? Sources: WebRTC Codecs Guide | LiveKit | Configuring the Client SDK for Optimal Video Quality | An introduction to WebRTC Simulcast | LiveKit | Bringing Zoom&#39;s end-to-end optimizations to WebRTC | LiveKit | client-sdk-android | livekit/livekit_composite | DeepWiki, with interactive elements
w
if i set quality 1080 and disable simulcast then what will happen in grid view of 100 participants
t
If you publish a single 1080p stream and disable simulcast, here’s what will happen in a 100‑participant grid and why it’s problematic: Immediate technical impacts • Bandwidth: Each subscriber receives the full 1080p stream they subscribe to. Even if tiles are small, viewers still download 1080p bitrates → huge downstream bandwidth per viewer. • Decoding load: Clients must decode full‑resolution frames even for small thumbnails. Decoding 100 simultaneous 1080p streams (or even many at once) will overwhelm most devices. • CPU & battery: Publisher encoding at 1080p continuously increases CPU and battery use. Clients’ decoding CPU and battery drain rise dramatically. • Latency and packet loss: Higher bitrate increases chance of packet loss and congestion, causing freezes/black frames across clients. • Server/SFU egress: SFU egress is high if many viewers subscribe directly (or if you relay many 1080p streams), increasing costs and risk of saturation. • UX: Grid will be choppy, likely produce black/frozen tiles, slow interactions, and poor experience on mobile/low‑bandwidth clients. Practical user-visible outcomes • Thumbnails look identical to full quality (wasteful). • Many clients (especially mobile) will show freezes, black screens, or drop frames. • Presenter devices and SFU may hit resource limits, causing wider outages. Recommended alternatives • Enable simulcast (or SVC): publish multiple layers (e.g., 1080/720/360 or 1080/720/180) so grid subscribers receive only a low layer. • Use adaptiveStream + dynacast so subscribers automatically get the appropriate layer and unused layers aren’t sent. • For a 100‑tile grid, request the low layer (180–360p @ low fps) for thumbnails; switch to higher layer on pin/spotlight. • Offload mass passive viewers to CDN (SFU→CDN) to avoid huge SFU egress. • Reduce frame rate for grids (10–15 fps) and keep thumbnails very low bitrate to limit decoding load. Bottom line: Disabling simulcast and sending 1080p to grid subscribers will create severe bandwidth, CPU, and UX problems. Keep simulcast on and serve low‑res layers for grid thumbnails; only use 1080p for pinned/spotlight views. , with interactive elements
w
if i enable adaptive stream then
t
Enabling adaptiveStream — what it does and what you should expect for a 100‑participant grid. What adaptiveStream does • Automatically lets the SFU choose which simulcast layer each subscriber receives based on the element’s size and visibility. • Causes invisible or very small video elements (thumbnails) to receive lower or no video layers, saving bandwidth and CPU. • Works with dynacast so unused layers can be suppressed upstream (publisher won’t send layers nobody needs). Effects in a 100‑tile grid • Bandwidth: Big savings — each viewer downloads only the low/resolution layer for thumbnails instead of full 1080p. Aggregate egress and client download drop drastically. • Decoding/CPU: Viewer devices decode lower‑res streams (much less CPU/battery cost). Presenter decoding load for subscribed tiles is reduced if you request low layers for the grid. • Quality: Thumbnails will be low resolution by design; pinned/spotlighted tiles will automatically upgrade to higher layers (or you can explicitly request higher layer on pin). • Latency/robustness: Fewer bytes in-flight reduces packet loss and improves overall stability at scale. • Edge cases: If adaptiveStream + dynacast and visibility logic think a tile is invisible (layout/scaling bugs), clients may get no video — ensure thumbnails are actually visible and have nonzero size. Practical recommendations for your 100‑tile grid • Keep simulcast ON and use 2–3 layers (e.g., 1080 / 720 / 180–360 for thumbnails). • Keep adaptiveStream + dynacast enabled. • For grid tiles, explicitly request the low layer; on pin, request medium/high layer. • Debounce layer switches on rapid pin/unpin to avoid flapping. • Use low fps (10–15) and low bitrate for thumbnail layer to reduce decode load when showing 100 tiles. • Monitor WebRTC stats (framesDecoded, bytesReceived, packetLoss) and SFU metrics to tune layer bitrates and framerate. Flutter example (concept)
Copy code
// enable at room connect or set options
room.setOptions(const RoomOptions(
  adaptiveStream: true,
  dynacast: true,
));

// publish with simulcast and layers
await room.localParticipant.publishTrack(
  videoTrack,
  publishOptions: const TrackPublishOptions(
    videoCodecs: ['vp8','h264'],
    simulcast: true,
    videoSimulcastLayers: [
      VideoSimulcastLayerPreset.p1080,
      VideoSimulcastLayerPreset.p720,
      VideoSimulcastLayerPreset.p360, // use p180/p360 for grid thumbnails
    ],
  ),
);
Subscriber behavior for grid vs pin • Grid tile: subscribe and explicitly select the low layer (180–360p, low fps). • Pin: switch subscription to medium/high layer for that participant. If you want, I can give exact code for: • Selecting a specific simulcast layer on subscribe/unsubscribe (pin/unpin) for your LiveKit Flutter SDK version. Which version are you using?, with interactive elements
w
if simulcast is off and adaptive stream is on then
t
If simulcast is OFF and adaptiveStream is ON, adaptiveStream has nothing to pick from — there’s only one published quality. Consequences for a 100‑tile grid: • No layer adaptation: each subscriber gets the single published stream resolution (e.g., 1080p) regardless of tile size. • Wasteful bandwidth: viewers download full-resolution video even for tiny thumbnails. • Higher decode load: clients must decode the full stream for every subscribed tile → CPU/battery issues and likely freezes on mobile. • No dynacast benefit: upstream layer suppression is unavailable, so publisher sends only that single stream continuously. • AdaptiveStream behavior: it can still pause sending when a track is invisible (if implemented), but it cannot reduce resolution or bitrate per subscriber. • UX risk at scale: with many tiles (or many viewers) you’ll see packet loss, black/frozen tiles, high SFU egress, and increased server/client load. Recommendation: • Keep simulcast ON (2–3 layers) + adaptiveStream + dynacast. If you must disable simulcast, publish a low-resolution stream for grid thumbnails and switch to a higher-quality single stream only for pinned/spotlighted views (via separate publication or server-side composite)., with interactive elements
w
you say with h264 codec simulcast cause issue on mobile then what i do
t
You’re right to be cautious — H.264 simulcast on mobile is often unreliable because many devices expose only one H.264 hardware encoder, so trying to publish multiple H.264 layers can fail or fall back to single-layer behavior. Here are clear options and the exact actions you can take. Options (pick one based on your priorities) • Option A — Best reliability for large grids: Use VP8 + simulcast ◦ Why: VP8 simulcast is widely supported and stable on mobile and desktop. ◦ Action: Publish with
videoCodecs: ['vp8','h264']
and simulcast enabled. Prefer VP8 for multi-layer use (thumbnails, tiles, spotlight). • Option B — Best CPU/battery on mobile single-stream scenarios: Use H.264 without simulcast ◦ Why: H.264 is hardware-accelerated and cheaper CPU-wise, but mobile simulcast may not work. ◦ Action: Publish H.264 single layer (simulcast off) and use low-res thumbnails or publish a separate low-res track for grids. • Option C — Hybrid, auto-adaptive approach ◦ Detect device capability at runtime; choose per-publisher: ▪︎ If device supports H.264 simulcast reliably, enable H.264 simulcast. ▪︎ Otherwise prefer VP8 simulcast or H.264 single-layer + a low-res VP8 track for thumbnails. ◦ Action: implement capability detection and branch publish options accordingly. Concrete Flutter suggestions 1. Prefer VP8 simulcast (recommended for 100-tile grids):
Copy code
await room.localParticipant.publishTrack(
  videoTrack,
  publishOptions: const TrackPublishOptions(
    videoCodecs: ['vp8','h264'],
    simulcast: true,
    // simulcast layers + bitrate/framerate per your needs
  ),
);
1. If you must use H.264, publish single H.264 + a low-res simulcast-fallback track: • Publish H.264 main track (simulcast: false) for high-quality/pinned view. • Publish a separate low-res VP8 track (simulcast: true or single low-res) for grid thumbnails. This avoids relying on H.264 simulcast on devices that can’t encode multiple H.264 layers. 1. Device capability detection (pseudo-logic) • On app start, test encoder capabilities or use a small probe: ◦ Try enabling simulcast with H.264 on a test path; if framesEncoded==0 or only one layer appears, fallback. • Or keep a device blacklist of known models/OSes that don’t support H.264 simulcast. Operational tips • Keep dynacast and adaptiveStream enabled so unused layers aren’t sent. • Keep thumbnail layers very low resolution (180–360p) and low fps (10–15). • Monitor WebRTC stats (framesEncoded, framesDecoded, bytesSent/Received, decodeErrors) to detect encoding/decoding failures. • If you see many VP8 transcodes on SFU, consider capacity or switch to publishing VP8 to avoid server CPU cost. If you want, I can: • Provide exact Flutter code for the hybrid publish approach (H.264 main + low-res VP8), or • Give a small runtime capability-test snippet to detect whether the device can do H.264 simulcast. Which would you prefer? , with interactive elements
w
what would be hardware requirement for simulcast on with codec h264
t
Good question — here are the practical hardware and capability requirements and considerations for enabling H.264 simulcast (publisher-side, receiver-side, and server) so you can decide whether to use it. Publisher (device) requirements and constraints • Multiple encoder instances: True simulcast requires producing multiple independent encodings concurrently (each layer its own encoder context). Most mobile SoCs expose only a single H.264 hardware encoder instance; desktops/modern GPUs may expose multiple. ◦ If the device exposes only one H.264 hardware encoder, the OS/SDK may: ▪︎ Fall back to software encoding for extra layers (very CPU‑heavy), or ▪︎ Only send a single H.264 layer (simulcast effectively disabled). • CPU: Software H.264 encoding for extra layers is extremely costly. Expect high CPU load, thermal throttling, and battery drain if hardware encoding is not available for all layers. • GPU / media hardware acceleration: Hardware H.264 encoder (MediaCodec on Android, VideoToolbox on iOS/macOS, hardware encoders on Windows/Linux) is required to keep CPU usage reasonable for multiple layers. • Memory and bandwidth: Encoding several layers increases memory and bus usage and uplink bandwidth (even with dynacast, briefly sending layers may spike network). • Practical device targets: ◦ Mobile: Most phones cannot reliably do multi‑layer H.264 simulcast. New flagship devices with modern SoCs might, but don’t rely on it. ◦ Desktop/laptop: Modern desktops with hardware encoders (dedicated GPU or platform encoders) are more likely to support multiple H.264 encoder instances or efficient S/W+HW hybrids. • Recommendation: Assume a single H.264 hardware encoder on mobile. If you must support H.264 simulcast, perform runtime capability detection and fallback to VP8 simulcast or H.264 single-layer + separate low-res VP8 track. Receiver (decoder) requirements • Decoding multiple streams: Subscribers (presenter viewing grid) who decode many tiles need decoders capable of handling many concurrent H.264 decodes. Mobile devices have decoder limits too — decoding many simultaneous H.264 streams at high resolution will overwhelm most devices. • Hardware decode is preferred for power/thermal reasons. Software decode will spike CPU. SFU / Server requirements • Transcoding: If publisher sends H.264 but some subscribers only support VP8 (or vice versa), the SFU must transcode per-subscriber. Transcoding H.264 ↔️ VP8 is CPU intensive. • Layer management: SFU must handle multiple layers per publisher, routing appropriate layers to subscribers. This increases memory and scheduling complexity. • Bandwidth/egress: More layers increase upstream usage from the publisher and potential egress if many subscribers are receiving high layers. Dynacast reduces this by stopping unused layers, but server must still manage potential spikes. • Capacity planning: Expect significantly higher CPU and possibly GPU requirements if the SFU performs any re-encoding/transcoding. Plan for autoscaling and higher-cost instances. Operational & engineering considerations • Runtime capability detection: Probe device encoder capability (number of H.264 encoder instances, hardware encoder presence) and pick publish strategy: ◦ If mobile device supports only one H.264 encoder → publish single H.264 layer + separate low-res VP8 for grid thumbnails, or use VP8 simulcast. ◦ If device supports multiple H.264 encoder instances → enable H.264 simulcast. • Hybrid publishing pattern: Publish primary H.264 track (single high-quality) + a second low-res VP8 track for grids/thumbnails. This avoids relying on multi‑H.264 encoders and avoids server transcoding for VP8‑only viewers. • Testing matrix: Test realistic device/OS models (especially low-mid Android OEMs), network conditions, and long-duration sessions to observe thermal throttling and encoder failures. • Monitoring: Track framesEncoded, encodeTime, droppedFrames, CPU, and temperature metrics on publisher devices and framesDecoded/decodeErrors on subscribers. Monitor SFU CPU and transcode counts. Concrete recommendations • For large-scale apps with grids and mixed clients: ◦ Default: Use VP8 simulcast (2–3 layers) for broad compatibility. ◦ If you require H.264 for CPU/battery reasons on many devices: ▪︎ Do not assume H.264 simulcast will work on mobile. Use H.264 single-layer + low-res VP8 for thumbnails, or dynamically choose codec per device. ▪︎ Ensure SFU capacity for potential transcoding if you mix codecs. • If you still want to attempt H.264 simulcast: ◦ Limit layers to 2 if possible to reduce encoder instances. ◦ Target only high-end devices (desktop, flagship phones) and detect capability to enable it. ◦ Provide a robust fallback path (automatic switch to VP8 or reduced layers) when hardware limits are hit. If you want, I can: • Provide a small runtime capability detection snippet for Flutter to decide between H.264 simulcast, H.264 single + VP8 low track, or VP8 simulcast. • Suggest SFU sizing estimates for expected transcoding load if you plan to transcode H.264 ↔️ VP8 at scale. Which would you prefer?, with interactive elements
w
i want hardware specifications
t
Thanks — you want concrete hardware specifications to support H.264 simulcast. Below are concise, practical specs and capability guidance for publishers (mobile/desktop) and SFU servers so you can plan. Publisher (mobile phones — recommended minimum / target) • Minimum (may fail for multi-layer H.264 simulcast): mid-range SoC (e.g., Snapdragon 7xx / Exynos mid-tier), 4 GB RAM, Android 11+/iOS 13+. Expect only single H.264 hardware encoder in many devices. • Recommended (likely to handle 2 H.264 encodes or software fallback): flagship SoC (Snapdragon 8 series / Apple A15+), 8 GB RAM, modern thermal/CPU headroom, Android 13+/iOS 15+. Hardware video encoder (MediaCodec / VideoToolbox) with multi-instance support preferred. • Ideal (reliable multi-layer H.264 simulcast): desktop-class hardware or flagship phones with confirmed multi-encoder support; CPU: 8+ cores at high single-thread perf; dedicated media blocks or discrete GPU; 12+ GB RAM. • Practical note: Most mobile devices only support a single H.264 hardware encoder. Do not assume reliable multi-layer H.264 on mobile — capability detection is required. Publisher (desktop/laptop) • Minimum (single-layer H.264 or limited simulcast): modern dual-core CPU (e.g., Intel i5, AMD Ryzen 3), 8 GB RAM, integrated GPU with hardware encoder (Intel QuickSync / AMD VCE/AVC). • Recommended (multi-layer H.264 simulcast feasible): quad-core or better CPU (Intel i5/i7 or AMD Ryzen 5/7), 16 GB RAM, discrete GPU or modern integrated encoder supporting multiple encode sessions (NVENC, QuickSync Gen >= 10). • Ideal: high single-thread perf CPU + NVENC / QuickSync capable GPU, 32 GB RAM for heavy multi-layer encoding. SFU / Transcoding Server (if doing server-side re-encoding) • No-transcode SFU (routing only, recommended): CPU: 8 vCPU, 16–32 GB RAM, 1 Gbps NIC per node; scale horizontally. This handles many SFU routing sessions but not transcoding. • Transcoding-enabled SFU / relay (H.264↔️VP8 or per-subscriber re-encode): use GPU-accelerated instances. ◦ CPU-only small-scale transcode: 16+ vCPU, 32+ GB RAM, 10 Gbps NIC (very expensive, limited scale). ◦ GPU-accelerated (recommended): NVIDIA T4 / A10 / A2 or equivalent with NVENC support (multi-encode sessions), 16–32 GB RAM, 10 Gbps NIC. Each T4 can handle many concurrent hardware encode streams depending on bitrate/resolution. • Estimate capacity: a single T4 can encode dozens to low hundreds of 720p streams at modest bitrates; 1080p counts are lower. Measure with your bitrate/fps targets. • Storage/network: ensure low-latency network (10 Gbps preferred for heavy egress), autoscaling, and monitoring of encode/transcode queue lengths. Other system requirements & tuning • Network: symmetric uplink capacity for publishers (publish bitrate headroom + 20%) and abundant SFU egress capacity (use CDN for >100s viewers). • Thermal: long sessions require thermal headroom on mobile; avoid sustained software encoding. • Software: latest OS and media stack (MediaCodec / VideoToolbox) and up-to-date flutter_webrtc/LiveKit SDK builds compiled for target OS. • Runtime detection: implement encoder-capability probe (detect hardware encoder presence and number of concurrent encoders) and fallback logic: VP8 simulcast or H.264 single + low-res VP8 track. • Testing: validate on representative device models (low/mid/high end) and under realistic network conditions and session durations. Quick actionable recommendation • Do not rely on H.264 simulcast for broad mobile support. If you must, restrict H.264 simulcast to desktop/flagship devices detected at runtime, otherwise use VP8 simulcast for multi-layer needs and H.264 single-layer for devices where CPU/battery matters. • For mass viewers (500–1000), publish once (simulcast) and use SFU→CDN for distribution; avoid server transcoding where possible. If you want, I can: • Provide a short Flutter runtime capability-detection snippet to choose publish strategy. • Estimate SFU/GPU sizing for your expected concurrent streams if you share target bitrate/fps and number of concurrent encodes. Which would you like?, with interactive elements
w
when remote participant join room sometime live kit did not send socket to me thst the participant join
t
Thanks — I’m responding to your report that sometimes LiveKit does not notify you when a remote participant joins. Quick checklist and targeted checks (try these and tell me results): 1) Are you expecting a webhook or SDK event? • Webhooks (HTTP POST) are delivered to your server endpoint, not over the client socket. Confirm which you rely on. 2) Livestream mode? • If the room is in livestream mode, participant join events for subscriber-only viewers may be suppressed. Confirm if livestream mode is enabled. 3) SDK event listener timing • Ensure your client registers the
ParticipantConnected
/
onParticipantConnected
handler before calling
connect
. If you attach the listener after connect, you can miss the event. 4) Reconnects vs first join •
participant_joined
webhook fires once on first join. Reconnects or migrations may not re-fire it. On clients, reconnects trigger the SDK connect flow; check for
ParticipantConnected
vs
ParticipantUpdated
events. 5) Signal/network issues • Check client logs for signaling/connect errors and WebSocket disconnect/reconnect traces around the time of the missed event. 6) Server-side checks • If using webhooks: check webhook delivery logs for retries/failures and dedupe by event
id
. • If using SDK notifications from another participant: verify that the SFU/node the joining participant connected to propagated the event (check SFU logs/metrics). 7) Debug info to gather • Which event you expect (webhook vs SDK event) • Room SID and time window when it happened • SDK and server versions (LiveKit server and client SDK) • Whether the joining participant published tracks or was viewer-only • Any client/server logs around that time If you share those details I’ll pinpoint the most likely root cause and exact next steps to fix it. Sources: server-sdk-ruby | livekit/livekit_composite | DeepWiki | Best practices for managing webhook event streams | LiveKit | Server-side | livekit/livekit_composite | DeepWiki | client-sdk-js | livekit/livekit_composite | DeepWiki | client-sdk-flutter | livekit/livekit_composite | DeepWiki, with interactive elements
w
see the bitrates i select is this ok ??
Copy code
final Map<String, VideoParameters> _videoQualityPresets = {
  'low': const VideoParameters(
    dimensions: VideoDimensionsPresets.h720_169,
    encoding: VideoEncoding(maxBitrate: 5_000_000, maxFramerate: 20),
  ),
  'high': const VideoParameters(
    dimensions: VideoDimensionsPresets.h1080_169,
    encoding: VideoEncoding(maxBitrate: 12_000_000, maxFramerate: 25),
  ),
  'full': const VideoParameters(
    dimensions: VideoDimensionsPresets.h2160_169,
    encoding: VideoEncoding(maxBitrate: 25_000_000, maxFramerate: 30),
  ),
};
t
Thanks — I’m reviewing your preset bitrates. Quick assessment and recommendations: Short verdict • These values are high for broad delivery at scale. They’ll produce excellent quality on good networks, but create large uplink/egress and decoding load for viewers and SFU/CDN costs. Notes per preset • low (720p @ 5 Mbps @20fps) — too high for a “low” preset. 5 Mbps is typical for 1080p, not 720p. • high (1080p @ 12 Mbps @25fps) — high but acceptable for premium 1080p; many deployments use 2.5–4 Mbps for web/meetings. • full (4K @ 25 Mbps @30fps) — reasonable for high-quality 4K but only suitable for very small audiences or CDN delivery; not for many direct subscribers. Recommended tuned targets (for large-scale/100+ viewers) • low (thumbails/grid): 180–360p, 80–400 kbps, 10–15 fps • medium (720p): 720p, 800–1500 kbps, 15–24 fps • high (1080p): 1080p, 1.5–3 Mbps, 24–30 fps • full (4K): 4K, 8–15+ Mbps, 24–30 fps (use only via CDN/one-to-many) If you must keep your three names: • map 'low' → 720p @ 1–1.5 Mbps (not 5 Mbps) • map 'high' → 1080p @ 2.5 Mbps (start) and allow up to ~4 Mbps • map 'full' → 4K @ 8–15 Mbps only for CDN/recording targets Other recommendations • Enable simulcast + adaptiveStream + dynacast so viewers get appropriate layer and unused layers aren’t sent. • Lower frame rate for long sessions or many publishers (15–24 fps). • Use H.264 if device battery/CPU is a priority; use VP8 for stable multi-layer simulcast across devices. • Load-test with representative devices and networks. Want me to convert your map to concrete VideoParameters using these recommended bitrates for your LiveKit Flutter SDK version? , with interactive elements
w
give me recemonded bitrates
t
Thanks — below are recommended bitrate presets (practical ranges) for common resolutions and use cases (grid thumbnails, tile, spotlight, and high-quality/recording). These balance quality, decoding load, and scalability. Recommended bitrate ranges (per publisher track) • Thumbnails (grid of many, e.g., 100 tiles) ◦ Resolution: 180p–360p (320×180 or 640×360) ◦ Bitrate: 80–400 kbps ◦ Framerate: 10–15 fps • Small tile / speaker tile ◦ Resolution: 360p–540p (640×360 or 960×540) ◦ Bitrate: 300–800 kbps ◦ Framerate: 15–20 fps • Medium / gallery focus (single larger tile) ◦ Resolution: 720p (1280×720) ◦ Bitrate: 800 kbps – 1.5 Mbps ◦ Framerate: 15–24 fps • Spotlight / high quality (active speaker or presenter for interactive) ◦ Resolution: 1080p (1920×1080) ◦ Bitrate: 1.5 – 3.0 Mbps ◦ Framerate: 24–30 fps • Premium / recording / CDN 1:many (high fidelity) ◦ Resolution: 4K (2160p) ◦ Bitrate: 8 – 15+ Mbps (use CDN/recording pipeline) ◦ Framerate: 24–30 fps Practical presets you can use • low-thumb: 320×180 @ 150 kbps @ 12 fps • thumb-high: 640×360 @ 350 kbps @ 15 fps • tile: 640×360 @ 600 kbps @ 15–20 fps • medium: 1280×720 @ 1.0 Mbps @ 20 fps • high: 1920×1080 @ 2.5 Mbps @ 24 fps • ultra: 3840×2160 @ 12 Mbps @ 30 fps (CDN/record only) Tuning guidance • For large grids, use thumbnail presets (180–360p, low fps) to limit decoding load. • Keep simulcast low-layer bitrates very small; only upgrade to medium/high on pin/spotlight. • Lower fps (10–20) for long sessions and many concurrent publishers to save bandwidth and CPU. • Prefer H.264 for hardware-accelerated decode/encode on mobile; prefer VP8 for reliable simulcast where multi-layer H.264 isn’t supported. • Use adaptiveStream + dynacast so subscribers receive only appropriate layers. • Load-test with representative devices/networks and monitor framesDecoded, packetLoss, and CPU. If you want, I’ll convert these into exact VideoParameters code for your Flutter SDK version. Which SDK version are you using? , with interactive elements
w
update this final Map<String, VideoParameters> _videoQualityPresets = { 'low': const VideoParameters( dimensions: VideoDimensionsPresets.h720_169, encoding: VideoEncoding(maxBitrate: 5_000_000, maxFramerate: 20), ), 'high': const VideoParameters( dimensions: VideoDimensionsPresets.h1080_169, encoding: VideoEncoding(maxBitrate: 12_000_000, maxFramerate: 25), ), 'full': const VideoParameters( dimensions: VideoDimensionsPresets.h2160_169, encoding: VideoEncoding(maxBitrate: 25_000_000, maxFramerate: 30), ), };
Copy code
final Map<String, double> _videoFpsPresets = {
  'low': 20.0,
  'high': 25.0,
  'full': 30.0,
};
t
Thanks — I updated your presets to practical, scalable targets. I kept your keys and structure and lowered bitrates/fps to match large‑scale/grid use (thumbnails, tiles, spotlight). Updated VideoParameters:
Copy code
final Map<String, VideoParameters> _videoQualityPresets = {
  'low': const VideoParameters(
    // use 360p for low/grid thumbnails
    dimensions: VideoDimensionsPresets.h360_169,
    encoding: VideoEncoding(maxBitrate: 350_000, maxFramerate: 15),
  ),
  'high': const VideoParameters(
    // 1080p for spotlight/active presenter
    dimensions: VideoDimensionsPresets.h1080_169,
    encoding: VideoEncoding(maxBitrate: 2_500_000, maxFramerate: 24),
  ),
  'full': const VideoParameters(
    // 4K only for CDN/recording; not recommended for direct large-scale viewers
    dimensions: VideoDimensionsPresets.h2160_169,
    encoding: VideoEncoding(maxBitrate: 12_000_000, maxFramerate: 30),
  ),
};
Updated FPS presets:
Copy code
final Map<String, double> _videoFpsPresets = {
  'low': 15.0,
  'high': 24.0,
  'full': 30.0,
};
Notes: • 'low' is suitable for 100‑tile grids (low resolution + low fps). • 'high' (1080p ~2.5 Mbps) balances quality and scale for spotlight/presenter. • 'full' (4K) kept for CDN/recording workflows; avoid sending 4K to many direct viewers. • Use simulcast + adaptiveStream + dynacast so subscribers get the appropriate layer for their viewport., with interactive elements
w
i want lowest 720p
t
Thanks — I’ll do both: confirm your choice and give a safer alternative with numbers. Quick confirmation • Setting the lowest preset to 720p is valid, but it’s expensive at scale. Expect much higher bandwidth, client decode CPU, and SFU egress than using thumbnail layers. Estimated impacts (per stream, approximate) • 720p @ 1.2 Mbps: downstream ≈1.2 Mbps per subscriber; decoding cost moderate. • 1080p @ 2.5 Mbps: downstream ≈2.5 Mbps per subscriber. • Example: 100 viewers × 1.2 Mbps = 120 Mbps sustained egress; 1,000 viewers = 1.2 Gbps — plan network/SFU/CDN capacity accordingly. If you confirm you really want 720p as the minimum, use this updated map:
Copy code
final Map<String, VideoParameters> _videoQualityPresets = {
  'low': const VideoParameters(
    dimensions: VideoDimensionsPresets.h720_169,
    encoding: VideoEncoding(maxBitrate: 1_200_000, maxFramerate: 20),
  ),
  'high': const VideoParameters(
    dimensions: VideoDimensionsPresets.h1080_169,
    encoding: VideoEncoding(maxBitrate: 2_500_000, maxFramerate: 24),
  ),
  'full': const VideoParameters(
    dimensions: VideoDimensionsPresets.h2160_169,
    encoding: VideoEncoding(maxBitrate: 12_000_000, maxFramerate: 30),
  ),
};

final Map<String, double> _videoFpsPresets = {
  'low': 20.0,
  'high': 24.0,
  'full': 30.0,
};
Safer simulcast-friendly alternative (recommended for large grids) • Use three layers: low=360p, mid=720p, high=1080p with tuned bitrates:
Copy code
final Map<String, VideoParameters> _videoQualityPresets = {
  'low': const VideoParameters(
    dimensions: VideoDimensionsPresets.h360_169,
    encoding: VideoEncoding(maxBitrate: 350_000, maxFramerate: 15),
  ),
  'mid': const VideoParameters(
    dimensions: VideoDimensionsPresets.h720_169,
    encoding: VideoEncoding(maxBitrate: 1_200_000, maxFramerate: 20),
  ),
  'high': const VideoParameters(
    dimensions: VideoDimensionsPresets.h1080_169,
    encoding: VideoEncoding(maxBitrate: 2_500_000, maxFramerate: 24),
  ),
};
• With simulcast + adaptiveStream + dynacast: ◦ Grid tiles request
low
(360p) → minimal decode/bandwidth. ◦ Pin requests `mid`/`high` as needed. Which do you want me to produce as the final presets in your code: (A) 720p minimum (the first snippet) or (B) the safer simulcast-friendly set (second snippet)? , with interactive elements
w
is it ok ?? final Map<String, VideoParameters> _videoQualityPresets = { 'low': const VideoParameters( dimensions: VideoDimensionsPresets.h720_169, encoding: VideoEncoding( maxBitrate: 1_500_000, // ✅ improved from 1.2 Mbps maxFramerate: 20, ), ), 'high': const VideoParameters( dimensions: VideoDimensionsPresets.h1080_169, encoding: VideoEncoding( maxBitrate: 4_000_000, // ✅ fixed (was too low) maxFramerate: 24, ), ), 'full': const VideoParameters( dimensions: VideoDimensionsPresets.h2160_169, encoding: VideoEncoding( maxBitrate: 15_000_000, // ✅ safer for 4K maxFramerate: 30, ), ), }; final Map<String, double> _videoFpsPresets = { 'low': 20.0, 'high': 24.0, 'full': 30.0, };
t
Yes — those presets will work, but two important notes and a recommended tweak: 1) Compatibility with simulcast/adaptiveStream/dynacast • They will work with simulcast/adaptiveStream/dynacast, but using 720p as your lowest layer reduces the benefits of simulcast for large grids. Subscribers still get layer selection, but thumbnails will consume far more bandwidth/CPU than a 360p low layer. 2) Scale impact (brief) • 720p @ 1.5 Mbps per stream × many viewers → high egress and client decode cost. Use 720p as minimum only if you accept that cost or you route viewers through CDN. Suggested tweak (safer default) • Lower the
low
bitrate to 1_200_000 (1.2 Mbps) or switch to a true three-layer simulcast (360p / 720p / 1080p) so grid thumbnails use ~150–350 kbps. Two copy-paste options — pick one: A) Keep 720p minimum (with safer bitrate)
Copy code
final Map<String, VideoParameters> _videoQualityPresets = {
  'low': const VideoParameters(
    dimensions: VideoDimensionsPresets.h720_169,
    encoding: VideoEncoding(maxBitrate: 1_200_000, maxFramerate: 20),
  ),
  'high': const VideoParameters(
    dimensions: VideoDimensionsPresets.h1080_169,
    encoding: VideoEncoding(maxBitrate: 4_000_000, maxFramerate: 24),
  ),
  'full': const VideoParameters(
    dimensions: VideoDimensionsPresets.h2160_169,
    encoding: VideoEncoding(maxBitrate: 15_000_000, maxFramerate: 30),
  ),
};

final Map<String, double> _videoFpsPresets = {
  'low': 20.0,
  'high': 24.0,
  'full': 30.0,
};
B) Recommended simulcast-friendly (best for large grids)
Copy code
final Map<String, VideoParameters> _videoQualityPresets = {
  'low': const VideoParameters(
    dimensions: VideoDimensionsPresets.h360_169,
    encoding: VideoEncoding(maxBitrate: 350_000, maxFramerate: 15),
  ),
  'mid': const VideoParameters(
    dimensions: VideoDimensionsPresets.h720_169,
    encoding: VideoEncoding(maxBitrate: 1_200_000, maxFramerate: 20),
  ),
  'high': const VideoParameters(
    dimensions: VideoDimensionsPresets.h1080_169,
    encoding: VideoEncoding(maxBitrate: 2_500_000, maxFramerate: 24),
  ),
};

final Map<String, double> _videoFpsPresets = {
  'low': 15.0,
  'mid': 20.0,
  'high': 24.0,
};
Which option do you want applied? If you pick B, I can also show how to wire these into simulcast + adaptiveStream + dynacast in your Flutter publish code. , with interactive elements
w
if i select simulcast layers like 720 360 and 180 and i select 1080 as video quality then what happen
t
Good question — I’ll answer what happens if you set your requested video quality to 1080p but your simulcast layers are 720/360/180. Short answer • The publisher’s top simulcast layer is 720p, so subscribers can only receive up to 720p. Requesting 1080p while publishing only 720/360/180 means you will not actually get 1080p delivered to viewers. Details / consequences • Publisher output: The publisher will capture/encode at whatever capture resolution you choose, but simulcast determines what independent encoded layers are produced and sent. With layers 720/360/180, the SFU can only forward those layers. • Subscriber view: AdaptiveStream/dynacast will pick the best of 720/360/180 for each subscriber based on viewport and bandwidth. No one will receive 1080p because that layer doesn’t exist. • Bandwidth/CPU: Encoding fewer/lower layers reduces upstream bandwidth and publisher CPU compared to having a 1080p layer. Grid thumbnails will use low layers as expected. • UX: If you intended pinned/spotlight to be 1080p, they’ll top out at 720p — pinned tiles will look good but not full-1080 detail. • If you publish at 1080p but only simulcast lower layers, some SDKs may downscale to produce 720/360/180; others may capture 1080p but only encode/send 720p as the highest layer (same visible effect). What to do if you need true 1080p viewers can get • Add a 1080p simulcast layer (e.g., 1080/720/360) so the SFU can forward 1080p to subscribers who need it. • Or publish a separate high-quality single track for spotlight/CDN delivery (useful if H.264 simulcast is problematic on mobile). • Keep adaptiveStream + dynacast enabled so only viewers who need 1080p receive that layer. If you want, I can show the exact LiveKit/Flutter publish options for a 1080/720/360 simulcast setup or a hybrid (1080 single + 720/360 simulcast) depending on your device support. Which option would you prefer? , with interactive elements
w
can i do something like if i select 1080 then pass 1080 720 and 360 and if i pass 720 then pass 720 360 and 180
t
Yes — you can do that. Briefly: • Simulcast layers are set when you publish a track. If the user selects 1080, publish simulcast layers [1080, 720, 360]. If they select 720, publish [720, 360, 180]. Subscribers will only receive layers that were published. • Changing the set of simulcast layers requires republishing the track (or renegotiation). That affects all subscribers to that track. • Use adaptiveStream + dynacast so subscribers automatically get the appropriate layer for their viewport and unused layers aren’t sent. Example (conceptual Flutter flow) 1. User selects quality → determine layers: ◦ 1080 chosen -> layers = [1080, 720, 360] ◦ 720 chosen -> layers = [720, 360, 180] 1. Stop current publish (if any), create camera track with desired capture/resolution, then publish with TrackPublishOptions that specify
simulcast: true
and the chosen
videoSimulcastLayers
. 2. Re-enable adaptiveStream/dynacast on the room so delivery is optimized. Concise code sketch (adapt to your SDK version):
Copy code
// decide layers based on user choice
final layers = userChoice == '1080'
  ? [VideoSimulcastLayerPreset.p1080, VideoSimulcastLayerPreset.p720, VideoSimulcastLayerPreset.p360]
  : [VideoSimulcastLayerPreset.p720, VideoSimulcastLayerPreset.p360, VideoSimulcastLayerPreset.p180];

// stop existing track (if published), create new track, then:
await room.localParticipant.publishTrack(
  videoTrack,
  publishOptions: TrackPublishOptions(
    simulcast: true,
    videoSimulcastLayers: layers,
    videoCodecs: ['vp8','h264'], // or ['h264','vp8'] per your strategy
    videoEncoding: VideoEncoding(maxBitrate: desiredBitrate, maxFramerate: desiredFps),
  ),
);
Notes and gotchas: • Republishing causes a brief interruption; debounce user changes to avoid frequent republish. • Some devices (especially mobile H.264) may not support multiple H.264 encodes — detect device capability and fallback to VP8 simulcast or a hybrid approach (H.264 single + low-res VP8). • You cannot publish different layer sets for different subscribers from the same track without publishing separate tracks or server-side processing. • If you want, tell me your LiveKit Flutter SDK version and I’ll give exact API names and a ready-to-drop-in function that switches layers safely (including graceful republish)., with interactive elements
w
if i select layers
t
Do you mean: when you (the publisher) select which simulcast layers to publish (e.g., choose 1080→[1080,720,360] or 720→[720,360,180]) — what happens next, or how to implement it? Tell me which you want: • Explain behavior & trade-offs when changing layers (republish, interruption, device limits), or • Provide a ready-to-use Flutter function that switches published layers (with graceful republish and capability checks)., with interactive elements
w
if i select video quality 1080 and select layers in simulcast 1080,720,360,180,90 then what happen
t
Good question — here’s what happens and what to watch for if you publish at 1080p but enable many simulcast layers (1080 / 720 / 360 / 180 / 90). What actually gets sent • The publisher will attempt to produce each configured simulcast layer. Subscribers can only receive layers that were actually published, so having those layers available lets the SFU send the best-fitting layer per subscriber (via adaptiveStream). • If the device or encoder can’t produce all layers, the implementation may: ◦ send fewer layers (highest layer might be lower than requested), or ◦ fall back to software encoding for some layers (high CPU), or ◦ disable simulcast for that publisher depending on platform limits. Performance and resource impacts • Publisher CPU/encoder load: producing 5 layers increases encoder work and memory/bus usage. On mobile this often forces software encodes (very expensive) or fails—desktop/flagship devices handle more layers. • Upstream bandwidth: even with dynacast, briefly publishing many layers increases upstream bandwidth. Dynacast reduces sustained upload if layers are suppressed, but there is still overhead. • SFU/server: more layers mean more state and possible higher scheduling/transmit work. If codec mismatch exists, server transcoding costs rise. • Subscriber decode cost: each subscriber decodes only the layer they receive, but if a client (like your presenter) subscribes to many tiles, decoding many layers (even low-res) adds CPU/battery load. Adaptive behavior for viewers • adaptiveStream + dynacast will: ◦ choose the smallest published layer that matches the viewer’s viewport/bandwidth, ◦ suppress unused layers upstream so they aren’t sent to everyone. • Example: a tiny thumbnail will get 90p or 180p if available; a pinned tile can upgrade to 1080p. Compatibility limits & failure modes • Mobile H.264: many phones have only one H.264 hardware encoder. Trying many H.264 simulcast layers will likely fail or fall back to sending only one layer. VP8 is more reliable for multi-layer simulcast. • Devices with limited encoders may silently send fewer layers—check encoding stats (framesEncoded per layer) to confirm. • Rapid layer changes or many simultaneous publishers can cause packet loss, freezes, or black frames if network/SFU can’t keep up. Practical recommendations • Don’t publish more layers than you need. Typical useful set: 3 layers (high/mid/low) — e.g., 1080 / 720 / 360 (or 720 / 360 / 180 if 720 is your max). • If you need very small thumbnails, add one extra low layer (e.g., 180 or 90) but test device/encoder impact first. • Prefer VP8 for simulcast; use H.264 single-layer or hybrid (H.264 high + low-res VP8 track) on mobile if hardware encoder limits exist. • Use dynacast + adaptiveStream so unused layers are suppressed. • Implement runtime capability checks: if device cannot produce all layers, fall back to fewer layers or alternative codec setup. • Debounce quality changes/republishes to avoid frequent renegotiation interruptions. If you’d like, I can: • Provide a Flutter code snippet to publish a configurable simulcast layer set and detect whether layers were actually encoded, or • Recommend a safe 2–3 layer set tuned for your presenter/grid scenario. Which would you prefer?, with interactive elements
w
there is no limit that only 3 layers below the selcted are send only
t
Correct — there is no hard protocol limit that restricts you to exactly three simulcast layers. You can configure more or fewer layers. Practical constraints to keep in mind: • Publisher device/encoder limits: many mobile devices expose only one H.264 hardware encoder and cannot reliably produce multiple H.264 layers. VP8 is more reliable for multi-layer simulcast. • CPU and power: each extra layer increases encoding work (or forces software encodes), raising CPU, battery drain, and heat. • Bandwidth: more layers increase upstream usage (though dynacast can suppress unused layers). • SFU/server load: more layers increase SFU scheduling/state and can raise transcoding needs if codecs differ. • Subscriber decode: subscribers decode only the layer they receive, but a client decoding many tiles still has decoding limits. • UX/complexity: more layers give finer adaptation but add complexity and longer negotiation/testing matrix. Recommendation: • Use 2–4 layers in practice (common sweet spot). Add extra very-low layers only if you’ve measured benefits and confirmed device/encoder capacity. • Prefer VP8 for simulcast; use H.264 single-layer or hybrid patterns on mobile where hardware encoders are limited. • Implement runtime capability checks and fallbacks so devices that can't produce all layers publish fewer layers automatically. If you want, I’ll provide a Flutter function that: • publishes N simulcast layers based on a selected quality, • probes device capability, and • falls back automatically if the device can’t encode the requested layers. Which SDK version are you on?, with interactive elements
w
if i not select layers then which layers it send if i select 1080p
t
If you publish at 1080p and do not explicitly set simulcast layers: • The SDK commonly enables simulcast by default and will derive lower layers from the base resolution (typically 1080p → 720p and 360p), so subscribers can receive an appropriate layer for their viewport/bandwidth. • If simulcast is turned off in your Room/Publish options, no lower layers are created — only the single 1080p stream is sent and all subscribers receive that full-resolution stream. • With simulcast + adaptiveStream + dynacast enabled, the SFU will forward the best-fitting layer per subscriber and suppress unused layers upstream to save bandwidth. • Default layer sizes and exact behavior can vary by SDK version and platform; for predictable results explicitly specify the simulcast layers you want (e.g., [1080, 720, 360] or [720, 360, 180]). Would you like the exact Flutter publish snippet that sets [1080, 720, 360] simulcast layers with recommended bitrates? , with interactive elements
w
but it send 640 or 540 also
t
Good point — thanks for that detail. Here’s what’s happening and how to control it. Why you see 640 or 540 (and other intermediate sizes) • When you publish a 1080p source and don’t explicitly set simulcast layers, the SDK/encoder and SFU choose default encodings. Those defaults can include intermediate spatial sizes (e.g., 640×360, 960×540) derived from the source and device/browser encoder alignment. • Some platforms pick resolutions that match encoder block sizes or common presets (e.g., 960×540 instead of exact 720×405), so you’ll see 640/540 variants in stats. • The encoder or browser may also downscale to those sizes automatically due to CPU/bandwidth constraints or to fit default simulcast presets. How to make layer sizes deterministic • Explicitly set the simulcast layers when publishing so the SDK publishes only the resolutions you want. Example (conceptual Flutter):
Copy code
await room.localParticipant.publishTrack(
  videoTrack,
  publishOptions: TrackPublishOptions(
    simulcast: true,
    videoSimulcastLayers: [
      VideoSimulcastLayerPreset.p1080, // 1920x1080
      VideoSimulcastLayerPreset.p720,  // 1280x720
      VideoSimulcastLayerPreset.p360,  // 640x360
    ],
    videoCodecs: ['vp8','h264'],
  ),
);
• If you must guarantee exact pixel sizes, use explicit encoding presets (dimensions + bitrate) rather than relying on defaults. Other controls & tips • Enable dynacast + adaptiveStream so unused layers aren’t sent and subscribers get the best-fitting layer. • If you see unexpected intermediate layers, check capture settings, encoder scaling, and the SDK/server version (defaults can change by version). • For strict behavior on mobile, detect device capability and either publish fewer layers or use a hybrid (H.264 single high + VP8 low) to avoid encoder fallback. If you want, tell me your LiveKit Flutter SDK version and I’ll give the exact publish code that enforces the specific layer resolutions and bitrates you want. , with interactive elements
w
if i demand 360 in grid of 100 particpants then what happen??
t
If every subscriber in a 100‑tile grid requests 360p for each tile, here’s what to expect and what to do. What will happen • Bandwidth per viewer: Each subscribed 360p stream ≈ 300–600 kbps (depends on bitrate you set). If a client displays 100 tiles and subscribes to all, that’s 30–60 Mbps incoming — unrealistic for most devices/networks. • Device decode load: Decoding dozens of 360p streams concurrently will overwhelm most mobiles/desktops (CPU, GPU, battery). Practical decode limits are typically tens of concurrent streams, not hundreds. • SFU/egress load: If many viewers subscribe directly to many streams, SFU egress scales linearly and network/CPU costs rise quickly. Even if each viewer subscribes only to the presenter, if the presenter subscribes to many tiles it concentrates decode load on that client. • UX: Expect stutters, dropped frames, black tiles, or app crashes when client or network capacity is exceeded. • Latency & packet loss: High aggregate throughput increases congestion and packet loss, worsening quality. Why this is risky • Video decoding is expensive: even low-res streams require per-stream decode contexts and rendering resources. • Browsers/OS limit concurrent hardware decodes; software decode is slow and power-hungry. • Aggregate bandwidth (per-client and server-side) becomes the main bottleneck before individual stream quality. Recommended approaches • Subscribe only to visible tiles — do not subscribe to 100 at once. Use pagination (you already have) and subscribe only to the current page. • For large grids, use much lower resolutions for thumbnails (180–240p) and 10–15 fps to reduce decode and bandwidth. • Use simulcast (low/mid/high) + adaptiveStream so subscribers automatically receive only the layer they need. • For presenter viewing many tiles, use server-side composition (a tiled composite or mixed stream) or request extremely low-res tiles (e.g., 180p) to keep client decode within limits. • Offload mass viewers to CDN if many passive viewers need high quality of a single presenter (one high-quality stream to CDN). • Throttle and cap simultaneous subscriptions per client (e.g., ≤25 active decoded tiles), and lazy-load or pause offscreen/hidden video elements. Concrete numbers (guidance) • 360p thumbnail: target 300–500 kbps @ 10–15 fps. • Reasonable concurrent decodes per typical device: 12–30 streams (varies). • If you must show 100 tiles simultaneously on one client, use 180p @ ~100–200 kbps and accept very low visual fidelity — or use server-side composed image. If you share: • whether the 100 tiles are shown on a single client (presenter) or distributed across many viewers, • your target per-layer bitrate, I’ll give exact bitrate presets, simulcast layer settings, and a safe subscription limit to implement in code. , with interactive elements
w
if i select 720 then which layers comes
t
If you select 720p as the published quality, here’s what happens and how to control it. What is sent by default • With simulcast enabled and no explicit layer list, the SDK will publish a 720p top layer plus lower derived layers (commonly a mid ~360p and a low ~180p). • Exact intermediate sizes can vary by platform/encoder (you may see 540 or 640 variants) because encoders pick nearest supported resolution or block sizes. Behavior summary • Subscribers can only receive layers that were actually published. If you publish 720p (with default simulcast), no one will receive 1080p because that layer doesn’t exist. • adaptiveStream + dynacast will make each subscriber receive the appropriate layer for their viewport/bandwidth and will suppress unused layers upstream. How to make layers deterministic • Explicitly set simulcast layers when publishing so you get exactly the resolutions you want (example presets: [720, 360, 180] or [720, 540, 360, 180] if you need finer steps). • If you need precise pixel sizes, publish explicit encoding presets (dimensions + bitrate) rather than relying on defaults. Quick Flutter example (conceptual):
Copy code
await room.localParticipant.publishTrack(
  videoTrack,
  publishOptions: TrackPublishOptions(
    simulcast: true,
    videoSimulcastLayers: [
      VideoSimulcastLayerPreset.p720,
      VideoSimulcastLayerPreset.p360,
      VideoSimulcastLayerPreset.p180,
    ],
  ),
);
Practical tips • Use VP8 for reliable multi-layer simulcast; H.264 simulcast on mobile is device-dependent. • For large grids, keep the low layer very low-res/low-fps (e.g., 180–360p @10–15fps) to reduce decode/bandwidth. • Test on representative devices because some platforms will downscale or choose slightly different resolutions automatically. Want me to produce the exact publish code for your SDK version and target bitrates for [720,360,180]? , with interactive elements
w
add layers in it in fluuter
Copy code
dynacast: true,
  defaultVideoPublishOptions: VideoPublishOptions(
    simulcast: true,
    videoCodec: 'h264',
    videoSimulcastLayers: [],
    backupVideoCodec: BackupVideoCodec(
      enabled: true,
      codec: 'vp8',
      simulcast: true,
    ),
  ),
),
t
I don't have the answer you're looking for. You could also try asking your question: • in one of the other Slack channels or • to https://deepwiki.com/livekit/livekit_composite which is trained on all LiveKit source code If you find the answer, please post it here to help others!
w
i set this roomOptions: RoomOptions( // Manual setVideoQuality() calls (requestHighQuality/requestLowQuality) // are handled throughout the codebase but are currently overridden by // adaptiveStream, which automatically picks the simulcast layer based on // the VideoTrackRenderer widget size. To manage quality manually instead, // set adaptiveStream: false — the existing setVideoQuality() code will // then take full effect. adaptiveStream: _adaptiveStream, dynacast: true, defaultVideoPublishOptions: VideoPublishOptions( simulcast: true, videoCodec: 'h264', videoSimulcastLayers: const [ VideoParameters( dimensions: VideoDimensionsPresets.h180_169, encoding: VideoEncoding( maxBitrate: 200_000, maxFramerate: 15, ), ), VideoParameters( dimensions: VideoDimensionsPresets.h360_169, encoding: VideoEncoding( maxBitrate: 500_000, maxFramerate: 20, ), ), VideoParameters( dimensions: VideoDimensionsPresets.h720_169, encoding: VideoEncoding( maxBitrate: 1_500_000, maxFramerate: 24, ), ), VideoParameters( dimensions: VideoDimensionsPresets.h1080_169, encoding: VideoEncoding( maxBitrate: 4_000_000, maxFramerate: 24, ), ), ], backupVideoCodec: BackupVideoCodec( enabled: true, codec: 'vp8', simulcast: true, ), ), ), ); but still it pass 270 or 540 quality layers
i am doing this final Map<String, VideoParameters> _videoQualityPresets = { 'low': const VideoParameters( dimensions: VideoDimensionsPresets.h180_169, encoding: VideoEncoding(maxBitrate: 150_000, maxFramerate: 15), ), 'medium': const VideoParameters( dimensions: VideoDimensionsPresets.h360_169, encoding: VideoEncoding(maxBitrate: 500_000, maxFramerate: 20), ), 'high': const VideoParameters( dimensions: VideoDimensionsPresets.h720_169, encoding: VideoEncoding(maxBitrate: 1_500_000, maxFramerate: 24), ), 'fullHd': const VideoParameters( dimensions: VideoDimensionsPresets.h1080_169, encoding: VideoEncoding(maxBitrate: 4_000_000, maxFramerate: 30), ), }; final Map<String, double> _videoFpsPresets = { 'low': 15.0, 'medium': 20.0, 'high': 24.0, 'fullHd': 30.0, }; CameraCaptureOptions( params: _videoQualityPresets[level]!, maxFrameRate: _videoFpsPresets[level], deviceId: selectedVideoDevice?.deviceId, ), ); }