Skip to content

πŸ› switchVideo(isOn:) has inverted semantics β€” isOn: true mutes the camera; no deterministic local-video mute setter existsΒ #1856

Description

@gurvan-m

Description

There is no deterministic way to set the local video mute state from Dart, and the only setter that maps to the native setMute(bool) is both deprecated and inverted.

HMSSDK.switchVideo({bool isOn}) passes its isOn value straight through to the native HMSLocalVideoTrack.setMute(...) without negation, on both platforms. So isOn: true mutes the camera (turns it OFF) β€” the opposite of
what the parameter name implies.

Dart β€” lib/src/hmssdk.dart:

@Deprecated('Use [toggleCameraMuteState]')
Future<HMSException?> switchVideo({bool isOn = false}) async {
  bool result = await PlatformService.invokeMethod(
      PlatformMethod.switchVideo, arguments: {'is_on': isOn});
  ...
}

iOS β€” ios/Classes/Actions/HMSVideoAction.swift:

guard let shouldMute = arguments["is_on"] as? Bool,   // value of is_on
      let peer = hmsSDK?.localPeer,
      let video = peer.videoTrack as? HMSLocalVideoTrack else { ... }
video.setMute(shouldMute)                              // is_on -> setMute(), no negation

Android β€” android/.../methods/HMSVideoAction.kt:

val argsIsOn = call.argument<Boolean>("is_on")
val videoTrack = peer?.videoTrack
videoTrack.setMute(argsIsOn ?: false)                 // same: is_on -> setMute()

Resulting behaviour:

Call Native effect Camera
switchVideo(isOn: true) setMute(true) OFF
switchVideo(isOn: false) setMute(false) ON

Why toggleCameraMuteState() is not a sufficient replacement: it is a blind toggle (setMute(!isMute())). When the underlying mute state is changing on its own β€” e.g. the burst of trackMuted/trackUnMuted updates emitted
during the preview→join handoff, or when another AVCaptureSession acquires/releases the camera — a single toggle races those changes and lands on a non-deterministic final state. Only an absolute setMute(bool) can converge the
camera to a known state.

Request: expose a deterministic, non-deprecated local-video mute setter, e.g. Future<HMSException?> setLocalVideoMute(bool mute) (or HMSLocalVideoTrack.setMute(bool)). The native setMute(bool) already exists; it's simply
not reachable from Dart except through the broken/deprecated switchVideo. Either fix switchVideo's inversion and un-deprecate it, or add a clearly-named replacement.

100ms Flutter Version

1.11.1

Steps to reproduce

  1. Build and join a room: HMSSDK()..build(), addUpdateListener, join(config).
  2. After onJoin, call hmsSDK.switchVideo(isOn: true), intending β€” per the parameter name β€” to turn the local camera ON.
  3. Observe onTrackUpdate for the local video track and/or the HMSVideoView.

Expected results

switchVideo(isOn: true) turns the local camera ON (unmuted), as the isOn parameter name implies.

More generally: a non-deprecated, deterministic API to set the local video mute state to an absolute value, so the camera can be forced to a known state regardless of in-flight mute events.

Code example, screenshot, or link to a repository

Code sample
// After onJoin has fired:
await hmsSDK.switchVideo(isOn: true);
// Parameter name implies "camera ON".
// Actual: onTrackUpdate reports trackMuted / isMute == true β†’ camera OFF.

await hmsSDK.switchVideo(isOn: false);
// Parameter name implies "camera OFF".
// Actual: isMute == false β†’ camera ON.

// Root cause, verifiable in the SDK's own source:
//   Dart   switchVideo({bool isOn}) -> invokeMethod(switchVideo, {'is_on': isOn})
//   iOS    HMSVideoAction.switchVideo: video.setMute(arguments["is_on"])
//   Kotlin HMSVideoAction.switchVideo: videoTrack.setMute(argsIsOn ?: false)
// 'is_on' is forwarded directly to setMute() with no negation on either platform.

Logs

Logs
// App-level logs after calling switchVideo(isOn: true) post-join
// (our wrapper logs the intent; the SDK then reports the inverted result):

setLocalVideoEnabled(enabled=true) joined=true   // we want camera ON
trackUpdate=HMSTrackUpdate.trackMuted, kind=video, isMute=true   // SDK muted it (OFF)

Flutter Doctor output

Doctor output
[βœ“] Flutter (Channel stable, 3.41.8, on macOS 26.2 25C56 darwin-arm64, locale fr-FR)
    β€’ Flutter version 3.41.8 on channel stable
    β€’ Framework revision 02085feb3f, 2026-04-24
    β€’ Engine revision 59aa584fdf
    β€’ Dart version 3.11.5
[βœ“] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
[βœ“] Xcode - develop for iOS and macOS (Xcode 26.1, Build 17B55)
    β€’ CocoaPods version 1.16.2
[βœ“] Connected device (2 available)
    β€’ iPhone (mobile) β€’ ios β€’ iOS 26.5 23F77
    β€’ macOS (desktop) β€’ darwin-arm64 β€’ macOS 26.2
[βœ“] Network resources

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions