From 63586804ac55d3ef75c7722e9f0e1933e724f2e9 Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:26:19 +0800 Subject: [PATCH 1/6] fix --- lib/src/track/local/audio.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/src/track/local/audio.dart b/lib/src/track/local/audio.dart index e3675a340..0ac59c797 100644 --- a/lib/src/track/local/audio.dart +++ b/lib/src/track/local/audio.dart @@ -15,6 +15,7 @@ import 'dart:async'; import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; import 'package:meta/meta.dart'; @@ -110,6 +111,14 @@ class LocalAudioTrack extends LocalTrack with AudioTrack, LocalAudioManagementMi return senderStats; } + @override + Future start() async { + if (!kIsWeb) { + await rtc.NativeAudioManagement.startLocalRecording(); + } + return await super.start(); + } + // private constructor @internal LocalAudioTrack( From 60bb2520c7559a8118ab6e71666950ed28b53043 Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Sun, 29 Mar 2026 21:05:37 +0800 Subject: [PATCH 2/6] changes --- .changes/fix-audio-visualizer-unpublished-track | 1 + 1 file changed, 1 insertion(+) create mode 100644 .changes/fix-audio-visualizer-unpublished-track diff --git a/.changes/fix-audio-visualizer-unpublished-track b/.changes/fix-audio-visualizer-unpublished-track new file mode 100644 index 000000000..5c97dce38 --- /dev/null +++ b/.changes/fix-audio-visualizer-unpublished-track @@ -0,0 +1 @@ +patch type="fixed" "Fix audio visualizer not working for unpublished local audio tracks" From f7d4a2600ced10977745376ac87a4fbf69c2031f Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Sun, 29 Mar 2026 21:17:29 +0800 Subject: [PATCH 3/6] expose --- lib/src/hardware/hardware.dart | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/src/hardware/hardware.dart b/lib/src/hardware/hardware.dart index 065d4f361..0e44b55e5 100644 --- a/lib/src/hardware/hardware.dart +++ b/lib/src/hardware/hardware.dart @@ -15,6 +15,7 @@ import 'dart:async'; import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; import '../logger.dart'; @@ -194,6 +195,31 @@ class Hardware { }); } + /// Explicitly starts the audio device module's recording pipeline. + /// + /// This is called automatically when a [LocalAudioTrack] is started, but + /// can be called manually for pre-join scenarios (e.g. audio visualization + /// before connecting to a room). + /// + /// No-ops on web. Safe to call multiple times (idempotent). + Future startLocalRecording() async { + if (!kIsWeb) { + await rtc.NativeAudioManagement.startLocalRecording(); + } + } + + /// Stops the audio device module's recording pipeline. + /// + /// Normally you don't need to call this — WebRTC manages the ADM lifecycle. + /// Use this only for explicit control (e.g. CallKit flows). + /// + /// No-ops on web. + Future stopLocalRecording() async { + if (!kIsWeb) { + await rtc.NativeAudioManagement.stopLocalRecording(); + } + } + dynamic _onDeviceChange(dynamic _) async { final devices = await enumerateDevices(); selectedAudioInput ??= devices.firstWhereOrNull((element) => element.kind == 'audioinput'); From 8ae3062fc763c12e2b8b62868bb6965061d4a9ae Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Sun, 29 Mar 2026 21:17:48 +0800 Subject: [PATCH 4/6] fmt --- lib/src/hardware/hardware.dart | 3 ++- lib/src/track/local/audio.dart | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/src/hardware/hardware.dart b/lib/src/hardware/hardware.dart index 0e44b55e5..db9dfc62a 100644 --- a/lib/src/hardware/hardware.dart +++ b/lib/src/hardware/hardware.dart @@ -14,8 +14,9 @@ import 'dart:async'; -import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart' show kIsWeb; + +import 'package:collection/collection.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; import '../logger.dart'; diff --git a/lib/src/track/local/audio.dart b/lib/src/track/local/audio.dart index 0ac59c797..ea1fbff65 100644 --- a/lib/src/track/local/audio.dart +++ b/lib/src/track/local/audio.dart @@ -14,8 +14,9 @@ import 'dart:async'; -import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart' show kIsWeb; + +import 'package:collection/collection.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; import 'package:meta/meta.dart'; From 7193560978c0417ca506f203a1c2b888c9556932 Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:34:49 +0800 Subject: [PATCH 5/6] check started --- lib/src/track/local/audio.dart | 1 + lib/src/track/track.dart | 28 ++++++++++++++++------------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/src/track/local/audio.dart b/lib/src/track/local/audio.dart index ea1fbff65..c02742b8a 100644 --- a/lib/src/track/local/audio.dart +++ b/lib/src/track/local/audio.dart @@ -114,6 +114,7 @@ class LocalAudioTrack extends LocalTrack with AudioTrack, LocalAudioManagementMi @override Future start() async { + if (isStarted) return false; if (!kIsWeb) { await rtc.NativeAudioManagement.startLocalRecording(); } diff --git a/lib/src/track/track.dart b/lib/src/track/track.dart index 520a69169..f09fb9834 100644 --- a/lib/src/track/track.dart +++ b/lib/src/track/track.dart @@ -52,11 +52,15 @@ abstract class Track extends DisposableChangeNotifier with EventsEmittable _active; + bool _isStarted = false; + bool get isStarted => _isStarted; + @Deprecated('Use isStarted instead') + bool get isActive => _isStarted; - bool _muted = false; - bool get muted => _muted; + bool _isMuted = false; + bool get isMuted => _isMuted; + @Deprecated('Use isMuted instead') + bool get muted => _isMuted; rtc.RTCRtpSender? get sender => transceiver?.sender; @@ -103,7 +107,7 @@ abstract class Track extends DisposableChangeNotifier with EventsEmittable start() async { - if (_active) { + if (_isStarted) { // already started return false; } @@ -114,7 +118,7 @@ abstract class Track extends DisposableChangeNotifier with EventsEmittable stop() async { - if (!_active) { + if (!_isStarted) { // already stopped return false; } @@ -142,14 +146,14 @@ abstract class Track extends DisposableChangeNotifier with EventsEmittable enable() async { logger.fine('$objectId.enable() enabling ${mediaStreamTrack.objectId}...'); try { - if (_active) { + if (_isStarted) { mediaStreamTrack.enabled = true; } } catch (e) { @@ -160,7 +164,7 @@ abstract class Track extends DisposableChangeNotifier with EventsEmittable disable() async { logger.fine('$objectId.disable() disabling ${mediaStreamTrack.objectId}...'); try { - if (_active) { + if (_isStarted) { mediaStreamTrack.enabled = false; } } catch (e) { @@ -200,8 +204,8 @@ abstract class Track extends DisposableChangeNotifier with EventsEmittable Date: Mon, 30 Mar 2026 21:37:23 +0800 Subject: [PATCH 6/6] Revert track.dart renames, use isActive in LocalAudioTrack --- lib/src/track/local/audio.dart | 2 +- lib/src/track/track.dart | 28 ++++++++++++---------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/lib/src/track/local/audio.dart b/lib/src/track/local/audio.dart index c02742b8a..8233c44da 100644 --- a/lib/src/track/local/audio.dart +++ b/lib/src/track/local/audio.dart @@ -114,7 +114,7 @@ class LocalAudioTrack extends LocalTrack with AudioTrack, LocalAudioManagementMi @override Future start() async { - if (isStarted) return false; + if (isActive) return false; if (!kIsWeb) { await rtc.NativeAudioManagement.startLocalRecording(); } diff --git a/lib/src/track/track.dart b/lib/src/track/track.dart index f09fb9834..520a69169 100644 --- a/lib/src/track/track.dart +++ b/lib/src/track/track.dart @@ -52,15 +52,11 @@ abstract class Track extends DisposableChangeNotifier with EventsEmittable _isStarted; - @Deprecated('Use isStarted instead') - bool get isActive => _isStarted; + bool _active = false; + bool get isActive => _active; - bool _isMuted = false; - bool get isMuted => _isMuted; - @Deprecated('Use isMuted instead') - bool get muted => _isMuted; + bool _muted = false; + bool get muted => _muted; rtc.RTCRtpSender? get sender => transceiver?.sender; @@ -107,7 +103,7 @@ abstract class Track extends DisposableChangeNotifier with EventsEmittable start() async { - if (_isStarted) { + if (_active) { // already started return false; } @@ -118,7 +114,7 @@ abstract class Track extends DisposableChangeNotifier with EventsEmittable stop() async { - if (!_isStarted) { + if (!_active) { // already stopped return false; } @@ -146,14 +142,14 @@ abstract class Track extends DisposableChangeNotifier with EventsEmittable enable() async { logger.fine('$objectId.enable() enabling ${mediaStreamTrack.objectId}...'); try { - if (_isStarted) { + if (_active) { mediaStreamTrack.enabled = true; } } catch (e) { @@ -164,7 +160,7 @@ abstract class Track extends DisposableChangeNotifier with EventsEmittable disable() async { logger.fine('$objectId.disable() disabling ${mediaStreamTrack.objectId}...'); try { - if (_isStarted) { + if (_active) { mediaStreamTrack.enabled = false; } } catch (e) { @@ -204,8 +200,8 @@ abstract class Track extends DisposableChangeNotifier with EventsEmittable