wonderful-airport-21213
04/14/2026, 7:27 AMtall-airline-16756
04/14/2026, 7:27 AMwonderful-airport-21213
04/14/2026, 7:28 AMtall-airline-16756
04/14/2026, 7:28 AMadaptiveStream. Adaptive stream automatically chooses a simulcast layer based on the renderer/widget size, so manual setVideoQuality() or your intended fixed 1080 choice can be ignored.
• Action: Temporarily set adaptiveStream: false to let manual quality settings take effect.
2) Capture source / browser fallback
• The camera or browser may not actually capture at 1080. Ensure your capture resolution is requested at 1080 (e.g., videoCaptureDefaults.resolution = VideoPresets.h1080.resolution) so the full-resolution layer exists.
3) Bandwidth / CPU limits and qualityLimitation reasons
• The browser can downscale if CPU or bandwidth are constrained. Check for qualityLimitationReason in the client — common values: cpu or bandwidth. If you see those, test on a higher-bandwidth network or a more powerful device.
4) Codec and simulcast vs SVC differences
• With H.264 + simulcast your videoSimulcastLayers are used (as you configured). With VP9/AV1 SVC is used instead and layer selection is controlled differently. Confirm the actual codec negotiated matches your intended behavior.
5) Dynacast and subscriber-side constraints
• Dynacast can disable unused layers to save bandwidth. That’s fine, but the publisher must still produce the full-resolution base layer. Ensure dynacast behavior isn’t removing the 1080 layer because no subscribers need it.
6) Renderer/widget size matters
• If the VideoTrackRenderer widget is small, adaptiveStream will pick a lower layer. Make the renderer large while testing (or disable adaptiveStream) to force higher-layer selection.
Quick checklist to try now:
1. Set adaptiveStream: false.
2. Ensure videoCaptureDefaults.resolution requests 1080p.
3. Confirm the published codec is H.264 (or your intended codec).
4. Test on a device/network with higher CPU and bandwidth.
5. Inspect qualityLimitationReason in the client logs.
If you try those and still see 270/540/720, tell me:
• Whether you set adaptiveStream to false and the result,
• The device/browser you’re testing on, and
• Any qualityLimitationReason values from client logs.
I’ll guide you through the next diagnostic steps.
, with interactive elementswonderful-airport-21213
04/14/2026, 7:30 AMadaptiveStream to false and the result,
• The device/browser you’re testing on, and
• Any qualityLimitationReason values from client logs.tall-airline-16756
04/14/2026, 7:30 AMadaptiveStream: false? If so, did the published layer change?
2. Device and browser (e.g., Windows Chrome 116, iPhone Safari 17, Android Chrome).
3. Any qualityLimitationReason values from the client logs (for example: cpu, bandwidth, or none) — paste the exact value(s).
Once you send those three details I’ll give the next targeted steps.
, with interactive elementswonderful-airport-21213
04/14/2026, 7:31 AMtall-airline-16756
04/14/2026, 7:31 AMadaptiveStream, you’re on a Mac M2, and qualityLimitationReason is none, try the following checks in order:
1. Confirm capture resolution is 1080
◦ Ensure you set videoCaptureDefaults.resolution = VideoPresets.h1080.resolution so the publisher actually captures a 1080 base layer.
1. Temporarily disable dynacast
◦ Turn off dynacast to ensure the 1080 layer isn’t being pruned when unused.
1. Verify negotiated codec & simulcast layers
◦ Confirm the published codec is h264 (your simulcast config applies only for simulcast codecs). If the client negotiated VP9/AV1, SVC rules apply and layer behavior differs.
1. Inspect WebRTC send stats
◦ Check getStats and look for the outbound video track’s `frameWidth`/`frameHeight` and bitrates to see what resolution is actually being sent (this confirms whether the publisher produced 1080).
1. Force publish 1080 encoding
◦ Temporarily publish with only the 1080 layer in videoSimulcastLayers (remove lower layers) to force sending the 1080 stream and observe results.
After you try those, tell me:
• Whether videoCaptureDefaults.resolution was set to 1080 and the getStats `frameWidth`/`frameHeight` values, and
• The result after disabling dynacast or publishing only the 1080 layer.
I’ll guide the next steps based on those results.
, with interactive elementswonderful-airport-21213
04/14/2026, 7:32 AMtall-airline-16756
04/14/2026, 7:33 AMadaptiveStream: false (you already did).
• Ensure the capture base layer is actually 1080: set videoCaptureDefaults.resolution = VideoPresets.h1080.resolution.
• Temporarily disable dynacast or remove lower layers from videoSimulcastLayers so only 1080 is published.
• Check WebRTC getStats for outbound `frameWidth`/`frameHeight` to confirm what the publisher is sending.
• Confirm the negotiated codec is H.264 (simulcast) rather than VP9/AV1 (SVC), since SVC handles layers differently.
If you want, tell me the getStats outbound frame size or the exact simulcast list you’re publishing now and I’ll point out the most likely step that’s causing the 270/540 layers.
, with interactive elementswonderful-airport-21213
04/14/2026, 9:27 AMfinal 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: 24),
),
};tall-airline-16756
04/14/2026, 9:27 AMvideoCaptureDefaults.resolution = VideoPresets.h1080.resolution.
• With adaptiveStream enabled the renderer size can cause lower layers to be chosen; set adaptiveStream: false or make the renderer large while testing. Also try disabling dynacast or temporarily publishing only the fullHd layer to force 1080.
If you want, I can give the exact config snippet to publish only the 1080 layer or how to read getStats to confirm the outbound `frameWidth`/`frameHeight`. Which would you prefer?
, with interactive elementswonderful-airport-21213
04/14/2026, 10:09 AMtall-airline-16756
04/14/2026, 10:09 AMvideoCaptureDefaults.resolution = VideoPresets.h360.resolution.
• Or publish only the 360 layer by removing higher layers from videoSimulcastLayers so the encoder cannot send 1080.
• Disable adaptiveStream and dynacast while testing to avoid automatic layer selection/pruning.
• Alternatively, explicitly publish a single encoding that matches 360p (no simulcast).
How to verify what’s actually sent
• Use WebRTC getStats and check the outbound track frameWidth / frameHeight and bitrate to confirm the published resolution.
If you want, tell me which approach you prefer (change OBS output, change capture defaults, or publish only the 360 layer) and I’ll give the exact config snippet and the getStats fields to check.
, with interactive elementswonderful-airport-21213
04/14/2026, 10:10 AMtall-airline-16756
04/14/2026, 10:10 AMvideoCaptureDefaults: VideoCaptureOptions(
resolution: VideoPresets.h360.resolution,
),
1. Keep simulcast layers if you want lower/higher variants, or remove higher layers (see B).
2. Verify with getStats (see verification below).
Option B — Publish only the 360 simulcast layer (forces encoder to send 360)
1. In your publish options, include only the 360 layer:
defaultVideoPublishOptions: VideoPublishOptions(
simulcast: true,
videoSimulcastLayers: const [
VideoParameters(
dimensions: VideoDimensionsPresets.h360_169,
encoding: VideoEncoding(maxBitrate: 500_000, maxFramerate: 20),
),
],
),
1. Disable adaptiveStream while testing:
adaptiveStream: false,
dynacast: false, // optional for testing
Option C — Change OBS virtual camera output to 360
• In OBS: Settings → Video → set “Base (Canvas) Resolution” and “Output (Scaled) Resolution” to 1280x720 or 640x360 as needed, then restart the virtual camera. This makes the source 360p so the publisher won’t have a 1080 base layer.
How to verify (use WebRTC stats)
• Call getStats and check the outbound video track fields:
◦ frameWidth / frameHeight (should be ~640x360)
◦ outbound bitrate to confirm bitrate matches 360p encoding
• If frame size reports 1920x1080, the publisher is still sending 1080.
Quick checklist to try now
• If you want me to provide the exact snippet for your current RoomOptions/publish config, paste that and I’ll adjust it to publish 360p., with interactive elementswonderful-airport-21213
04/14/2026, 10:12 AMtall-airline-16756
04/14/2026, 10:12 AMvideoCaptureDefaults: VideoCaptureOptions(
resolution: VideoPresets.h360.resolution,
maxFrameRate: _videoFpsPresets['medium'],
deviceId: selectedVideoDevice?.deviceId,
),
1. Ensure publisher can’t send 1080:
• Either publish only the 360 simulcast layer:
defaultVideoPublishOptions: VideoPublishOptions(
simulcast: true,
videoSimulcastLayers: const [
VideoParameters(
dimensions: VideoDimensionsPresets.h360_169,
encoding: VideoEncoding(maxBitrate: 500_000, maxFramerate: 20),
),
],
),
• Or set OBS virtual camera output to 640x360 (OBS Settings → Video → Output (Scaled) Resolution).
1. Disable auto choices while testing:
• adaptiveStream: false
• dynacast: false (optional)
1. Verify with getStats:
• Check outbound track frameWidth / frameHeight — should be ~640x360.
If you want, paste your current RoomOptions and publish config and I’ll give the exact snippet adjusted to force 360p.
, with interactive elementswonderful-airport-21213
04/15/2026, 12:37 PMtall-airline-16756
04/15/2026, 12:37 PMjitterBufferDelay is the delay introduced by the receiver’s jitter buffer to smooth out packet arrival variability so audio/video can play out smoothly.
• The num? prefix means the value is a numeric field (nullable/optional) — you’ll see a number (typically in seconds or milliseconds depending on the API) representing the current buffering delay.
• Why it matters: higher values mean more buffering (smoother playback but higher end-to-end latency); very low values risk glitches if packets arrive late.
If you tell me the unit shown in your stats (e.g., 0.12 or 120) I’ll confirm whether that’s seconds or milliseconds and what that value implies for perceived latency.
Sources: Why WebRTC beats WebSockets for realtime voice AI | LiveKit | examples/frontdesk | livekit/livekit_composite | DeepWiki, with interactive elementswonderful-airport-21213
04/15/2026, 12:39 PMtall-airline-16756
04/15/2026, 12:39 PMbitrate.
• Temporarily reduce sender bitrate/resolution (publish only 360p) and see if jitterBufferDelay drops.
If you share recent getStats values for RTT, packetsLost, inbound bitrate, and framesDecoded, I’ll help pinpoint the cause and next steps.
, with interactive elementswonderful-airport-21213
04/15/2026, 12:43 PMtall-airline-16756
04/15/2026, 12:44 PMjitterBufferDelay (ms) — confirm unit is ms by comparing typical values
◦ currentRoundTripTime / RTT
◦ jitter (RTP jitter)
◦ packetsLost and packetsReceived
◦ inbound bitrate and `frameWidth`/`frameHeight`
◦ framesDecoded / framesDropped
• Reproduce while monitoring network (ping/traceroute) and CPU usage on sender/receiver.
Quick fixes to try
• Test on a stable, higher-bandwidth network (avoid Wi‑Fi congestion).
• Reduce sender bitrate/resolution (publish 360p) to lower decoding/load.
• Ensure sender/receiver CPUs aren’t overloaded (close heavy apps).
• If using OBS, set the virtual camera output to a lower resolution.
• Disable large buffering if you control playout buffer settings; prefer smaller target (e.g., ~200 ms) for interactive apps.
• Update browser/SDK to latest versions.
When to escalate your troubleshooting
• If packet loss, RTT, or RTP jitter are high — focus on network path.
• If network stats look fine but jitterBufferDelay still grows, suspect client CPU/decoding or a timestamping/implementation bug.
If you want, paste a recent getStats snippet (jitterBufferDelay, RTT, packetsLost, inbound bitrate, frameWidth/frameHeight) and I’ll help interpret those values and suggest targeted next steps.
Sources: Why WebRTC beats WebSockets for realtime voice AI | LiveKit | Debugging Audio/Video Sync Issues in LiveKit Publishing | Why You Shouldn’t Build Real-Time Voice Agents Directly on Model APIs | Liv... | Resolving "InvalidState - failed to capture frame" errors in AudioSource, with interactive elementswonderful-airport-21213
04/15/2026, 12:48 PMtall-airline-16756
04/15/2026, 12:49 PMjitterBufferDelay reports how long media is sitting in the receiver’s jitter buffer (milliseconds). A large number (e.g., 6171) means the buffer has accumulated ~6.17s of media.
• That number measures buffer occupancy, not total end-to-end or perceived latency. If the buffer is full but playback is continuous and your UI/playout clock hides the buffered content, you may not notice big lag in short checks.
• Also, the receiver can mask jitter by holding and smoothing packets; playback appears smooth even though the buffer’s internal counter is high.
Practical checks and actions
• Inspect getStats fields: jitterBufferDelay, currentRoundTripTime, jitter (RTP), packetsLost, inbound bitrate, and `frameWidth`/`frameHeight`.
• If jitterBufferDelay is growing:
◦ Check packet loss and RTT — if high, fix network path.
◦ Reduce sender bitrate/resolution (publish 360p) to reduce decoding load.
◦ Test on another network or device to rule out client CPU issues.
◦ If you control playback buffering, lower playout buffer targets (e.g., ~200ms) for more interactivity, understanding this raises risk of glitches.
If you paste a recent getStats snippet (jitterBufferDelay, RTT, packetsLost, inbound bitrate, frameWidth/frameHeight) I’ll interpret the numbers and recommend next steps.
Sources: How Playback built a global stadium for watching sports together | LiveKit | Why WebRTC beats WebSockets for realtime voice AI | LiveKit | An introduction to WebRTC Simulcast | LiveKit | Bringing Zoom's end-to-end optimizations to WebRTC | LiveKit | client-sdk-android | livekit/livekit_composite | DeepWiki, with interactive elementswonderful-airport-21213
04/16/2026, 12:12 PM_room = Room(
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',
backupVideoCodec: BackupVideoCodec(
enabled: true,
codec: 'vp8',
simulcast: true,
),
),
),
);
this codec is software base or hardware basetall-airline-16756
04/16/2026, 12:12 PMvideoCodec: 'h264' and backupVideoCodec: 'vp8' does not force software or hardware encoding. The actual encoder (hardware-accelerated vs software) is chosen by the underlying WebRTC stack on the client device at runtime.
Why:
• WebRTC and the platform/browser decide whether to use a hardware encoder (preferred when available) or fall back to a software encoder when hardware support is missing or unavailable.
• Your RoomOptions only pick the codec; they don’t control whether encoding is done in hardware.
How to check which is being used:
• Inspect client stats / implementation fields if available (some SDKs/browsers expose encoder implementation info via getStats or logs).
• Check the client platform docs (browser or native SDK) — they'll describe hardware acceleration support for H.264/VP8 on that OS/device.
When to care:
• Hardware encoding improves CPU usage and power efficiency and is common on modern phones/desktops. If you see high CPU/quality issues, inspect whether hardware acceleration is being used and consider lowering bitrate/resolution or switching devices.
If you want, tell me the client platform (browser + version or native SDK + OS) and I’ll suggest the exact getStats fields or logs to look for to confirm hardware vs software encoding.
Sources: LiveKit SDK for ESP32: bringing voice AI to embedded devices | LiveKit | WebRTC Codecs Guide | LiveKit | Yeah, but can it run DOOM? | LiveKit, with interactive elementswonderful-airport-21213
04/16/2026, 12:13 PMvideoCodec: 'h264'tall-airline-16756
04/16/2026, 12:13 PMvideoCodec: 'h264' only selects the codec; whether encoding is hardware‑accelerated or software‑based is decided by the client’s WebRTC implementation and device at runtime.
Details:
• Modern browsers/SDKs will use hardware H.264 encoders when available (better CPU/power).
• If hardware support isn’t available or fails, the stack falls back to a software encoder.
• Your RoomOptions do not force hardware vs software; they only set the codec preference.
If you want, tell me the client platform (browser + version or OS + SDK) and I’ll show how to check via getStats/logs which encoder is actually being used.
, with interactive elementswonderful-airport-21213
04/16/2026, 12:13 PMtall-airline-16756
04/16/2026, 12:14 PMencoderImplementation or similar field in getStats or in SDK logs that indicates “Hardware” vs “Software.”
• Monitor CPU usage during publish: significantly lower CPU suggests hardware encoding.
If you tell me the client platform (browser + version or native SDK + OS), I’ll point to the exact place to check for encoder info and any platform-specific steps.
, with interactive elementswonderful-airport-21213
04/16/2026, 12:25 PMtall-airline-16756
04/16/2026, 12:27 PMwonderful-airport-21213
04/16/2026, 12:29 PMtall-airline-16756
04/16/2026, 12:30 PMfinal room = await LiveKitClient.connect(url, token, options: ConnectOptions(
// prefer h264 so platforms that support HW H264 will pick it
preferredVideoCodecs: ['h264'],
));
final roomOptions = RoomOptions(
adaptiveStream: false, // manage quality manually while testing
dynacast: true,
defaultVideoPublishOptions: VideoPublishOptions(
simulcast: true,
videoCodec: 'h264', // request H.264
// keep simulcast layers sensible; high bitrates may push fallback to SW encoder
videoSimulcastLayers: const [
VideoParameters(
dimensions: VideoDimensionsPresets.h360_169,
encoding: VideoEncoding(maxBitrate: 500000, maxFramerate: 20),
),
VideoParameters(
dimensions: VideoDimensionsPresets.h720_169,
encoding: VideoEncoding(maxBitrate: 1500000, maxFramerate: 24),
),
],
backupVideoCodec: BackupVideoCodec(enabled: true, codec: 'vp8', simulcast: true),
),
);
2) Set capture resolution moderately (reduces chance of SW fallback due to extreme settings)
final captureOptions = CameraCaptureOptions(
params: VideoParameters(
dimensions: VideoDimensionsPresets.h720_169,
encoding: VideoEncoding(maxBitrate: 1500000, maxFramerate: 24),
),
maxFrameRate: 24,
deviceId: selectedVideoDevice?.deviceId,
);
// create/publish track with these capture options
3) How to encourage hardware encoding (practical tips)
• Use H.264 as above — most devices/browsers support HW H.264 acceleration.
• Avoid extreme capture resolutions/bitrates that might force software fallback.
• Keep OS/browser/SDK updated (hardware encoders exposed by drivers/browsers).
• On native apps (iOS/Android) use the platform SDKs where available — they usually enable HW encoders by default.
4) How to force software (only possible when platform/SDK exposes such an option)
• Flutter LiveKit doesn’t expose a cross-platform flag to force software. If you control the native layer (Android/iOS) you could disable hardware encoder at native WebRTC build time or use a software encoder pipeline; this requires native changes outside Flutter.
5) How to detect which encoder is used (diagnose)
• Pull getStats and look for encoder/implementation fields or CPU usage:
◦ Some platforms expose encoderImplementation or similar in getStats; check logs or webrtc-internals in the browser.
◦ Monitor CPU during publishing: low CPU usage suggests HW encoder; high CPU suggests SW encoder.
Example: request and inspect stats (pseudo-Dart)
final stats = await room.localParticipant?.getStats();
// inspect stats for encoderImplementation or check outbound-rtp/frameWidth/frameHeight and CPU externally
print(stats);
If you tell me:
• target platform (Web/Android/iOS/macOS) and whether you can modify native code, I’ll give exact platform steps to prefer/verify hardware encoders or show the native flag changes needed to force software encoding., with interactive elementswonderful-airport-21213
04/16/2026, 12:36 PMtall-airline-16756
04/16/2026, 12:37 PM// Presenter preferred options
final presenterPublishOptions = VideoPublishOptions(
simulcast: false, // single stream for presenter to simplify encoding
videoCodec: 'h264', // prefer H.264 (more likely HW-accelerated)
videoEncoding: VideoEncoding(
maxBitrate: 2_500_000, // sensible upper bound for 720/1080 presenter
maxFramerate: 30,
),
backupVideoCodec: BackupVideoCodec(enabled: true, codec: 'vp8', simulcast: false),
);
// Capture options for presenter (request high but reasonable)
final presenterCaptureOptions = CameraCaptureOptions(
params: VideoParameters(
dimensions: VideoDimensionsPresets.h720_169, // prefer 720 for reliable HW support
encoding: VideoEncoding(maxBitrate: 2_000_000, maxFramerate: 30),
),
maxFrameRate: 30,
deviceId: selectedVideoDevice?.deviceId,
);
// Example publish flow (pseudo)
final localTrack = await LocalVideoTrack.createCameraTrack(
captureOptions: presenterCaptureOptions,
);
await room.localParticipant?.publishTrack(
localTrack,
publisherOptions: presenterPublishOptions,
);
2) Fallback / conservative path (if you detect SW encoder)
• If you detect high CPU or other signs below, reduce resolution/bitrate or switch codec to VP8 as needed:
// Lower-quality fallback
final fallbackPublishOptions = presenterPublishOptions.copyWith(
videoEncoding: VideoEncoding(maxBitrate: 800_000, maxFramerate: 20),
);
3) How to detect likely software encoding (heuristic)
• WebRTC/Flutter does not expose a direct “hardware vs software” boolean. Use these checks:
◦ Monitor device CPU usage while publishing (if CPU rises significantly, likely software).
◦ Pull stats and inspect outbound-rtp/frame rates/bitrate; if CPU high but bitrate low, suspect SW encoder.
◦ On web, inspect chrome://webrtc-internals or getStats for encoder implementation strings when available.
Simple example to pull stats (pseudo Dart, adapt to your SDK):
Future<void> printLocalStats() async {
final stats = await room.localParticipant?.getStats();
print(stats); // inspect for encoder info or outbound-rtp, frame sizes, bitrates
}
4) Platform-specific options (if you control native layer)
• If you can modify native code (Android/iOS/macOS):
◦ Android: ensure MediaCodec is used; use official WebRTC builds that enable MediaCodec HW encoders.
◦ iOS/macOS: WebRTC typically uses VideoToolbox for HW H.264. Use native SDKs to ensure HW is enabled.
◦ Server-side native pipelines: use NVENC/x264 as needed via ffmpeg or custom WebRTC builds.
• If you need an enforced choice (force SW or force HW) you must change native WebRTC build/config or use native APIs — not possible purely from Flutter LiveKit options.
5) Recommended workflow
• Prefer H.264 for the presenter and request a single, reasonable resolution (720) and bitrate.
• On app startup for presenter, run a short CPU/bandwidth test; if CPU usage is low, assume HW; if high, switch to fallback lower bitrate or resolution.
• Provide UI to let the presenter toggle “High quality (may use more CPU)” vs “Low CPU mode”.
If you tell me the target platforms you must support (Web / Android / iOS / macOS / Windows) and whether you can modify native code, I’ll provide platform-specific checks or native snippets to help enforce or verify hardware encoding.
Sources: LiveKit SDK for ESP32: bringing voice AI to embedded devices | LiveKit | Configuring the Client SDK for Optimal Video Quality | WebRTC Codecs Guide | LiveKit | lib/src/options.dart | An introduction to WebRTC Simulcast | LiveKit, with interactive elementswonderful-airport-21213
04/16/2026, 12:41 PMtall-airline-16756
04/16/2026, 12:41 PM// Presenter simulcast presets (low/medium/high)
const presenterSimulcastLayers = [
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: 3_500_000, maxFramerate: 30),
),
];
// Room options preferring H.264 and enabling simulcast/dynacast
final roomOptions = RoomOptions(
adaptiveStream: false, // manage quality manually for presenter
dynacast: true, // allow SFU to trim unused layers
defaultVideoPublishOptions: VideoPublishOptions(
simulcast: true,
videoCodec: 'h264', // prefer H.264 (more likely HW-accelerated)
videoSimulcastLayers: presenterSimulcastLayers,
backupVideoCodec: BackupVideoCodec(enabled: true, codec: 'vp8', simulcast: true),
),
);
// Capture options - request a high-but-reasonable base resolution
final presenterCapture = CameraCaptureOptions(
params: VideoParameters(
dimensions: VideoDimensionsPresets.h1080_169, // request base 1080 if presenter needs it
encoding: VideoEncoding(maxBitrate: 3_500_000, maxFramerate: 30),
),
maxFrameRate: 30,
deviceId: selectedVideoDevice?.deviceId,
);
// Create and publish presenter track
final presenterTrack = await LocalVideoTrack.createCameraTrack(
captureOptions: presenterCapture,
);
await room.localParticipant?.publishTrack(
presenterTrack,
publisherOptions: roomOptions.defaultVideoPublishOptions,
);
2) Practical guidance to encourage HW usage
• Prefer videoCodec: 'h264' for presenter (HW H.264 is widely supported).
• Request realistic base resolution/bitrates (very high settings can push some devices to SW fallback).
• Keep simulcast layers limited to needed tiers (too many layers increases encoder load).
• Use dynacast: true so the SFU disables layers not required by subscribers (saves bandwidth).
3) Heuristics and runtime checks (detect likely SW encoder)
• Flutter cannot directly query “HW vs SW” in all environments. Use these signals:
◦ Monitor CPU usage while publishing; high sustained CPU suggests software encoding.
◦ Pull SDK/browser stats and inspect outbound-rtp/frame sizes/bitrates.
◦ On web, inspect chrome://webrtc-internals for encoder implementation strings (VideoToolbox/MediaCodec vs libx264).
• Example to fetch stats (pseudo-Dart):
final stats = await room.localParticipant?.getStats();
print(stats); // inspect outbound-rtp, frameWidth/frameHeight, bitrate, and any encoder info
4) Fallback automation for presenters
• If CPU > threshold or stats show encoding problems, automatically reduce layers or bitrates:
// Fallback: remove 1080 layer and reduce bitrate
final fallbackLayers = [
VideoParameters(
dimensions: VideoDimensionsPresets.h360_169,
encoding: VideoEncoding(maxBitrate: 500_000, maxFramerate: 20),
),
VideoParameters(
dimensions: VideoDimensionsPresets.h720_169,
encoding: VideoEncoding(maxBitrate: 1_000_000, maxFramerate: 24),
),
];
await room.localParticipant?.updatePublishOptions(
track: presenterTrack,
publisherOptions: roomOptions.defaultVideoPublishOptions.copyWith(
videoSimulcastLayers: fallbackLayers,
videoEncoding: VideoEncoding(maxBitrate: 1_000_000, maxFramerate: 24),
),
);
5) Verification steps
• Confirm outbound resolution with getStats (`frameWidth`/`frameHeight`) to ensure the base layer is as expected.
• On desktop/native, check process CPU during publish. Low CPU + expected bitrate -> likely HW encoder.
• On web, use chrome://webrtc-internals to see encoder implementation names.
6) Platform-specific enforcement (optional, native changes required)
• If you must force HW/SW, modify native WebRTC configurations:
◦ Android: ensure MediaCodec is enabled in the WebRTC build/config.
◦ iOS/macOS: VideoToolbox is used by native WebRTC builds for HW H.264.
◦ Server/native pipelines: use nvenc/x264 via ffmpeg as needed.
• These changes require native code or custom WebRTC builds — not possible from pure Flutter code.
If you want, tell me target platforms (Android / iOS / Web / macOS / Windows) and whether you can change native code. I’ll provide platform-specific commands or native snippets to verify or require hardware encoders.
, with interactive elements