Skip to content

Commit

Permalink
fix: remove js_bindings and use the built-in AudioContext for js inte…
Browse files Browse the repository at this point in the history
…rop. (#342)

* fix: remove js_bindings and use the built-in AudioContext for js interop.

* import sorter.

* remove eva_icons_flutter to fix web app compile.

* update.
  • Loading branch information
cloudwebrtc authored Aug 17, 2023
1 parent 2e8dd4e commit a5a98ee
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 65 deletions.
45 changes: 22 additions & 23 deletions example/lib/widgets/controls.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'dart:convert';
import 'dart:io';

import 'package:collection/collection.dart';
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_background/flutter_background.dart';
Expand Down Expand Up @@ -276,14 +275,14 @@ class _ControlsWidgetState extends State<ControlsWidget> {
children: [
IconButton(
onPressed: _unpublishAll,
icon: const Icon(EvaIcons.closeCircleOutline),
icon: const Icon(Icons.cancel),
tooltip: 'Unpublish all',
),
if (participant.isMicrophoneEnabled())
if (lkPlatformIs(PlatformType.android))
IconButton(
onPressed: _disableAudio,
icon: const Icon(EvaIcons.mic),
icon: const Icon(Icons.mic),
tooltip: 'mute audio',
)
else
Expand All @@ -296,7 +295,7 @@ class _ControlsWidgetState extends State<ControlsWidget> {
onTap: isMuted ? _enableAudio : _disableAudio,
child: const ListTile(
leading: Icon(
EvaIcons.micOff,
Icons.mic_off,
color: Colors.white,
),
title: Text('Mute Microphone'),
Expand All @@ -310,11 +309,11 @@ class _ControlsWidgetState extends State<ControlsWidget> {
leading: (device.deviceId ==
widget.room.selectedAudioInputDeviceId)
? const Icon(
EvaIcons.checkmarkSquare,
Icons.check_box_outlined,
color: Colors.white,
)
: const Icon(
EvaIcons.square,
Icons.check_box_outline_blank,
color: Colors.white,
),
title: Text(device.label),
Expand All @@ -328,7 +327,7 @@ class _ControlsWidgetState extends State<ControlsWidget> {
else
IconButton(
onPressed: _enableAudio,
icon: const Icon(EvaIcons.micOff),
icon: const Icon(Icons.mic_off),
tooltip: 'un-mute audio',
),
if (!lkPlatformIs(PlatformType.iOS))
Expand All @@ -340,7 +339,7 @@ class _ControlsWidgetState extends State<ControlsWidget> {
value: null,
child: ListTile(
leading: Icon(
EvaIcons.speaker,
Icons.speaker,
color: Colors.white,
),
title: Text('Select Audio Output'),
Expand All @@ -354,11 +353,11 @@ class _ControlsWidgetState extends State<ControlsWidget> {
leading: (device.deviceId ==
widget.room.selectedAudioOutputDeviceId)
? const Icon(
EvaIcons.checkmarkSquare,
Icons.check_box_outlined,
color: Colors.white,
)
: const Icon(
EvaIcons.square,
Icons.check_box_outline_blank,
color: Colors.white,
),
title: Text(device.label),
Expand All @@ -381,15 +380,15 @@ class _ControlsWidgetState extends State<ControlsWidget> {
),
if (participant.isCameraEnabled())
PopupMenuButton<MediaDevice>(
icon: const Icon(EvaIcons.video),
icon: const Icon(Icons.videocam_sharp),
itemBuilder: (BuildContext context) {
return [
PopupMenuItem<MediaDevice>(
value: null,
onTap: _disableVideo,
child: const ListTile(
leading: Icon(
EvaIcons.videoOff,
Icons.videocam_off,
color: Colors.white,
),
title: Text('Disable Camera'),
Expand All @@ -403,11 +402,11 @@ class _ControlsWidgetState extends State<ControlsWidget> {
leading: (device.deviceId ==
widget.room.selectedVideoInputDeviceId)
? const Icon(
EvaIcons.checkmarkSquare,
Icons.check_box_outlined,
color: Colors.white,
)
: const Icon(
EvaIcons.square,
Icons.check_box_outline_blank,
color: Colors.white,
),
title: Text(device.label),
Expand All @@ -421,46 +420,46 @@ class _ControlsWidgetState extends State<ControlsWidget> {
else
IconButton(
onPressed: _enableVideo,
icon: const Icon(EvaIcons.videoOff),
icon: const Icon(Icons.videocam_off),
tooltip: 'un-mute video',
),
IconButton(
icon: Icon(position == CameraPosition.back
? EvaIcons.camera
: EvaIcons.person),
? Icons.video_camera_back
: Icons.video_camera_front),
onPressed: () => _toggleCamera(),
tooltip: 'toggle camera',
),
if (participant.isScreenShareEnabled())
IconButton(
icon: const Icon(EvaIcons.monitorOutline),
icon: const Icon(Icons.monitor_outlined),
onPressed: () => _disableScreenShare(),
tooltip: 'unshare screen (experimental)',
)
else
IconButton(
icon: const Icon(EvaIcons.monitor),
icon: const Icon(Icons.monitor),
onPressed: () => _enableScreenShare(),
tooltip: 'share screen (experimental)',
),
IconButton(
onPressed: _onTapDisconnect,
icon: const Icon(EvaIcons.closeCircle),
icon: const Icon(Icons.close_sharp),
tooltip: 'disconnect',
),
IconButton(
onPressed: _onTapSendData,
icon: const Icon(EvaIcons.paperPlane),
icon: const Icon(Icons.message),
tooltip: 'send demo data',
),
IconButton(
onPressed: _onTapUpdateSubscribePermission,
icon: const Icon(EvaIcons.settings2),
icon: const Icon(Icons.settings),
tooltip: 'Subscribe permission',
),
IconButton(
onPressed: _onTapSimulateScenario,
icon: const Icon(EvaIcons.alertTriangle),
icon: const Icon(Icons.bug_report),
tooltip: 'Simulate scenario',
),
],
Expand Down
3 changes: 1 addition & 2 deletions example/lib/widgets/no_video.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;

Expand All @@ -13,7 +12,7 @@ class NoVideoWidget extends StatelessWidget {
alignment: Alignment.center,
child: LayoutBuilder(
builder: (ctx, constraints) => Icon(
EvaIcons.videoOffOutline,
Icons.videocam_off_outlined,
color: LKColors.lkBlue,
size: math.min(constraints.maxHeight, constraints.maxWidth) * 0.3,
),
Expand Down
7 changes: 3 additions & 4 deletions example/lib/widgets/participant.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:collection/collection.dart';
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:livekit_client/livekit_client.dart';
Expand Down Expand Up @@ -222,18 +221,18 @@ class _RemoteParticipantWidgetState
if (firstAudioPublication != null && !isScreenShare)
RemoteTrackPublicationMenuWidget(
pub: firstAudioPublication!,
icon: EvaIcons.volumeUp,
icon: Icons.volume_up,
),
// Menu for RemoteTrackPublication<RemoteVideoTrack>
if (videoPublication != null)
RemoteTrackPublicationMenuWidget(
pub: videoPublication!,
icon: isScreenShare ? EvaIcons.monitor : EvaIcons.video,
icon: isScreenShare ? Icons.monitor : Icons.videocam,
),
if (videoPublication != null)
RemoteTrackFPSMenuWidget(
pub: videoPublication!,
icon: EvaIcons.options2,
icon: Icons.menu,
),
],
),
Expand Down
11 changes: 5 additions & 6 deletions example/lib/widgets/participant_info.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:flutter/material.dart';
import 'package:livekit_client/livekit_client.dart';

Expand Down Expand Up @@ -51,15 +50,15 @@ class ParticipantInfoWidget extends StatelessWidget {
? const Padding(
padding: EdgeInsets.only(left: 5),
child: Icon(
EvaIcons.monitor,
Icons.monitor,
color: Colors.white,
size: 16,
),
)
: Padding(
padding: const EdgeInsets.only(left: 5),
child: Icon(
audioAvailable ? EvaIcons.mic : EvaIcons.micOff,
audioAvailable ? Icons.mic : Icons.mic_off,
color: audioAvailable ? Colors.white : Colors.red,
size: 16,
),
Expand All @@ -69,8 +68,8 @@ class ParticipantInfoWidget extends StatelessWidget {
padding: const EdgeInsets.only(left: 5),
child: Icon(
connectionQuality == ConnectionQuality.poor
? EvaIcons.wifiOffOutline
: EvaIcons.wifi,
? Icons.wifi_off_outlined
: Icons.wifi,
color: {
ConnectionQuality.excellent: Colors.green,
ConnectionQuality.good: Colors.orange,
Expand All @@ -82,7 +81,7 @@ class ParticipantInfoWidget extends StatelessWidget {
Padding(
padding: const EdgeInsets.only(left: 5),
child: Icon(
enabledE2EE ? EvaIcons.lock : EvaIcons.unlock,
enabledE2EE ? Icons.lock : Icons.lock_open,
color: enabledE2EE ? Colors.green : Colors.red,
size: 16,
),
Expand Down
6 changes: 2 additions & 4 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ publish_to: 'none'
version: 1.0.0

environment:
sdk: ">=2.15.0 <3.0.0"
sdk: ">=2.17.0 <4.0.0"

dependencies:
flutter:
Expand All @@ -17,14 +17,12 @@ dependencies:
provider: ^6.0.1
logging: ^1.0.1
shared_preferences: ^2.0.7

google_fonts: ^4.0.4
eva_icons_flutter: ^3.0.0
flutter_svg: ^2.0.5

livekit_client:
path: ../
js_bindings: ^0.1.2+1

# git:
# url: https://github.com/livekit/client-sdk-flutter
# ref: main
Expand Down
55 changes: 55 additions & 0 deletions lib/src/track/web/_audio_context.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import 'dart:js_util' as jsutil;

import 'package:js/js.dart';

@JS()
external dynamic get undefined;

enum AudioContextState {
suspended('suspended'),
running('running'),
closed('closed');

final String value;
static AudioContextState fromValue(String value) =>
values.firstWhere((e) => e.value == value);
static Iterable<AudioContextState> fromValues(Iterable<String> values) =>
values.map(fromValue);
const AudioContextState(this.value);
}

@anonymous
@JS()
@staticInterop
class AudioContextOptions {
external factory AudioContextOptions(
{dynamic latencyHint, double? sampleRate});
}

@JS()
@staticInterop
class AudioContext {
external factory AudioContext._([AudioContextOptions? contextOptions]);

factory AudioContext([AudioContextOptions? contextOptions]) =>
AudioContext._(contextOptions ?? undefined);
}

extension PropsBaseAudioContext on AudioContext {
AudioContextState get state =>
AudioContextState.fromValue(jsutil.getProperty(this, 'state'));
}
7 changes: 4 additions & 3 deletions lib/src/track/web/_audio_html.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@ import 'dart:html' as html;
import 'dart:js_util' as jsutil;

import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
import 'package:js_bindings/js_bindings.dart' as js_bindings;

import '_audio_context.dart';

// ignore: implementation_imports
import 'package:dart_webrtc/src/media_stream_track_impl.dart'; // import_sorter: keep

const audioContainerId = 'livekit_audio_container';
const audioPrefix = 'livekit_audio_';

js_bindings.AudioContext _audioContext = js_bindings.AudioContext();
AudioContext _audioContext = AudioContext();
Map<String, html.Element> _audioElements = {};

Future<dynamic> startAudio(String id, rtc.MediaStreamTrack track) async {
Expand Down Expand Up @@ -57,7 +58,7 @@ Future<bool> startAllAudioElement() async {
await element.play();
}
}
return _audioContext.state == js_bindings.AudioContextState.running;
return _audioContext.state == AudioContextState.running;
}

void stopAudio(String id) {
Expand Down
Loading

0 comments on commit a5a98ee

Please sign in to comment.