wonderful-airport-21213
04/10/2026, 5:08 AMtall-airline-16756
04/10/2026, 5:09 AMtall-airline-16756
04/10/2026, 5:09 AMwonderful-airport-21213
04/10/2026, 5:11 AMtall-airline-16756
04/10/2026, 5:12 AMwonderful-airport-21213
04/10/2026, 5:14 AMtall-airline-16756
04/10/2026, 5:15 AMh264, 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 elementswonderful-airport-21213
04/10/2026, 5:17 AMtall-airline-16756
04/10/2026, 5:17 AMwonderful-airport-21213
04/10/2026, 5:19 AMtall-airline-16756
04/10/2026, 5:19 AMvideoCodecs, 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 elementswonderful-airport-21213
04/10/2026, 5:25 AMtall-airline-16756
04/10/2026, 5:25 AMimport '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's end-to-end optimizations to WebRTC | LiveKit | Configuring the Client SDK for Optimal Video Quality | WebRTC Codecs Guide | LiveKit, with interactive elementswonderful-airport-21213
04/10/2026, 5:28 AMtall-airline-16756
04/10/2026, 5:28 AM['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 elementswonderful-airport-21213
04/10/2026, 5:29 AMtall-airline-16756
04/10/2026, 5:29 AM['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 elementswonderful-airport-21213
04/10/2026, 5:34 AMtall-airline-16756
04/10/2026, 5:34 AM['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 elementswonderful-airport-21213
04/10/2026, 5:36 AMtall-airline-16756
04/10/2026, 5:36 AM['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 elementswonderful-airport-21213
04/10/2026, 5:38 AMtall-airline-16756
04/10/2026, 5:38 AMimport '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
// 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 elementswonderful-airport-21213
04/10/2026, 5:41 AMtall-airline-16756
04/10/2026, 5:41 AM['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's end-to-end optimizations to WebRTC | LiveKit | An introduction to WebRTC Simulcast | LiveKit, with interactive elementswonderful-airport-21213
04/10/2026, 5:43 AMtall-airline-16756
04/10/2026, 5:44 AMflutter_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 elementswonderful-airport-21213
04/10/2026, 5:46 AMlivekit_client: ^2.6.0
publish with vp8
both adaptive stream and dynacast enable
only audio is seentall-airline-16756
04/10/2026, 5:47 AMawait 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's end-to-end optimizations to WebRTC | LiveKit | examples/other | livekit/livekit_composite | DeepWiki, with interactive elementswonderful-airport-21213
04/10/2026, 5:50 AMtall-airline-16756
04/10/2026, 5:50 AM['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 elementswonderful-airport-21213
04/10/2026, 5:57 AMtall-airline-16756
04/10/2026, 5:57 AMawait 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 elementswonderful-airport-21213
04/10/2026, 6:07 AMtall-airline-16756
04/10/2026, 6:07 AMwonderful-airport-21213
04/10/2026, 8:45 AMtall-airline-16756
04/10/2026, 8:46 AMawait 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)
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 elementswonderful-airport-21213
04/10/2026, 12:32 PMtall-airline-16756
04/10/2026, 12:32 PM['h264','vp8'] so H.264 is preferred and VP8 used when necessary., with interactive elementswonderful-airport-21213
04/10/2026, 12:33 PMtall-airline-16756
04/10/2026, 12:33 PMroom.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 elementswonderful-airport-21213
04/10/2026, 12:36 PM3 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:
VP8 + simulcast
instead of
H264 + simulcast
because it is more stable across platforms.
What Happens if You Enable It?
Example config:
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
VideoPublishOptions(
videoCodec: 'vp8',
simulcast: true,
)
Best for:
• large meetings
• adaptive quality
• bandwidth control
Mobile optimized
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:
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.tall-airline-16756
04/10/2026, 12:36 PMwonderful-airport-21213
04/10/2026, 12:49 PMtall-airline-16756
04/10/2026, 12:49 PMwonderful-airport-21213
04/10/2026, 12:50 PMtall-airline-16756
04/10/2026, 12:50 PM// 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 elementswonderful-airport-21213
04/10/2026, 12:51 PMtall-airline-16756
04/10/2026, 12:52 PMwonderful-airport-21213
04/10/2026, 12:53 PMtall-airline-16756
04/10/2026, 12:53 PMvideoCodecs: ['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):
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 elementswonderful-airport-21213
04/10/2026, 12:54 PMtall-airline-16756
04/10/2026, 12:55 PMwonderful-airport-21213
04/10/2026, 12:56 PMtall-airline-16756
04/10/2026, 12:56 PMwonderful-airport-21213
04/11/2026, 8:53 AMtall-airline-16756
04/11/2026, 8:54 AMParticipantConnected / 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 elementswonderful-airport-21213
04/13/2026, 7:28 AMfinal 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),
),
};tall-airline-16756
04/13/2026, 7:28 AMwonderful-airport-21213
04/13/2026, 7:30 AMtall-airline-16756
04/13/2026, 7:30 AMwonderful-airport-21213
04/13/2026, 7:32 AMfinal Map<String, double> _videoFpsPresets = {
'low': 20.0,
'high': 25.0,
'full': 30.0,
};tall-airline-16756
04/13/2026, 7:32 AMfinal 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:
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 elementswonderful-airport-21213
04/13/2026, 7:34 AMtall-airline-16756
04/13/2026, 7:35 AMfinal 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:
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 elementswonderful-airport-21213
04/13/2026, 7:37 AMtall-airline-16756
04/13/2026, 7:38 AMlow 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)
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)
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 elementswonderful-airport-21213
04/14/2026, 5:04 AMtall-airline-16756
04/14/2026, 5:04 AMwonderful-airport-21213
04/14/2026, 5:06 AMtall-airline-16756
04/14/2026, 5:06 AMsimulcast: 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):
// 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 elementswonderful-airport-21213
04/14/2026, 6:46 AMtall-airline-16756
04/14/2026, 6:47 AMwonderful-airport-21213
04/14/2026, 6:49 AMtall-airline-16756
04/14/2026, 6:50 AMwonderful-airport-21213
04/14/2026, 6:52 AMtall-airline-16756
04/14/2026, 6:52 AMwonderful-airport-21213
04/14/2026, 6:53 AMtall-airline-16756
04/14/2026, 6:53 AMwonderful-airport-21213
04/14/2026, 6:54 AMtall-airline-16756
04/14/2026, 6:54 AMawait 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 elementswonderful-airport-21213
04/14/2026, 6:56 AMtall-airline-16756
04/14/2026, 6:56 AMwonderful-airport-21213
04/14/2026, 6:59 AMtall-airline-16756
04/14/2026, 6:59 AMawait 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 elementswonderful-airport-21213
04/14/2026, 7:03 AMdynacast: true,
defaultVideoPublishOptions: VideoPublishOptions(
simulcast: true,
videoCodec: 'h264',
videoSimulcastLayers: [],
backupVideoCodec: BackupVideoCodec(
enabled: true,
codec: 'vp8',
simulcast: true,
),
),
),tall-airline-16756
04/14/2026, 7:03 AMwonderful-airport-21213
04/14/2026, 7:26 AMwonderful-airport-21213
04/14/2026, 10:12 AM