diff --git a/lib/livekit_client_web.dart b/lib/livekit_client_web.dart index 66904195..9c42b885 100644 --- a/lib/livekit_client_web.dart +++ b/lib/livekit_client_web.dart @@ -23,7 +23,8 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart'; // package as the core of your plugin. // ignore: unused_import -import 'dart:html' as html show document, ScriptElement; // import_sorter: keep +import 'package:web/web.dart' as web + show document, HTMLScriptElement; // import_sorter: keep /// A web implementation of the Livekit plugin. class LiveKitWebPlugin { diff --git a/lib/src/internal/events.dart b/lib/src/internal/events.dart index 3a7ddca0..5e7cdb2e 100644 --- a/lib/src/internal/events.dart +++ b/lib/src/internal/events.dart @@ -24,7 +24,7 @@ import '../track/options.dart'; import '../track/track.dart'; import '../types/other.dart'; -abstract class InternalEvent implements LiveKitEvent {} +mixin InternalEvent implements LiveKitEvent {} @internal abstract class EnginePeerStateUpdatedEvent with EngineEvent, InternalEvent { diff --git a/lib/src/support/websocket/web.dart b/lib/src/support/websocket/web.dart index fd185b35..574e1afa 100644 --- a/lib/src/support/websocket/web.dart +++ b/lib/src/support/websocket/web.dart @@ -13,9 +13,11 @@ // limitations under the License. import 'dart:async'; -import 'dart:html' as html; +import 'dart:js_interop'; import 'dart:typed_data'; +import 'package:web/web.dart' as web; + import '../../extensions.dart'; import '../../logger.dart'; import '../websocket.dart'; @@ -29,7 +31,7 @@ Future lkWebSocketConnect( LiveKitWebSocketWeb.connect(uri, options); class LiveKitWebSocketWeb extends LiveKitWebSocket { - final html.WebSocket _ws; + final web.WebSocket _ws; final WebSocketEventHandlers? options; late final StreamSubscription _messageSubscription; late final StreamSubscription _closeSubscription; @@ -44,7 +46,8 @@ class LiveKitWebSocketWeb extends LiveKitWebSocket { logger.warning('$objectId already disposed, ignoring received data.'); return; } - dynamic data = _.data is ByteBuffer ? _.data.asUint8List() : _.data; + dynamic data = + _.data is ByteBuffer ? (_.data as ByteBuffer).asUint8List() : _.data; options?.onData?.call(data); }); _closeSubscription = _ws.onClose.listen((_) async { @@ -54,21 +57,21 @@ class LiveKitWebSocketWeb extends LiveKitWebSocket { }); onDispose(() async { - if (_ws.readyState != html.WebSocket.CLOSED) { + if (_ws.readyState != web.WebSocket.CLOSED) { _ws.close(); } }); } @override - void send(List data) => _ws.send(data); + void send(List data) => _ws.send(Uint8List.fromList(data).toJS); static Future connect( Uri uri, [ WebSocketEventHandlers? options, ]) async { final completer = Completer(); - final ws = html.WebSocket(uri.toString()); + final ws = web.WebSocket(uri.toString()); ws.onOpen .listen((_) => completer.complete(LiveKitWebSocketWeb._(ws, options))); ws.onError diff --git a/lib/src/track/web/_audio_html.dart b/lib/src/track/web/_audio_html.dart index 075cef16..e0399ef2 100644 --- a/lib/src/track/web/_audio_html.dart +++ b/lib/src/track/web/_audio_html.dart @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:html' as html; +import 'dart:js_interop'; import 'dart:js_util' as jsutil; import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc; +import 'package:web/web.dart' as web; import '_audio_context.dart'; @@ -26,7 +27,7 @@ const audioContainerId = 'livekit_audio_container'; const audioPrefix = 'livekit_audio_'; AudioContext _audioContext = AudioContext(); -Map _audioElements = {}; +Map _audioElements = {}; Future startAudio(String id, rtc.MediaStreamTrack track) async { if (track is! MediaStreamTrackWeb) { @@ -34,19 +35,19 @@ Future startAudio(String id, rtc.MediaStreamTrack track) async { } final elementId = audioPrefix + id; - var audioElement = html.document.getElementById(elementId); + var audioElement = web.document.getElementById(elementId); if (audioElement == null) { - audioElement = html.AudioElement() + audioElement = web.HTMLAudioElement() ..id = elementId ..autoplay = true; findOrCreateAudioContainer().append(audioElement); _audioElements[id] = audioElement; } - if (audioElement is! html.AudioElement) { + if (audioElement is! web.HTMLAudioElement) { return; } - final audioStream = html.MediaStream(); + final audioStream = web.MediaStream(); audioStream.addTrack(track.jsTrack); audioElement.srcObject = audioStream; return audioElement.play(); @@ -54,17 +55,17 @@ Future startAudio(String id, rtc.MediaStreamTrack track) async { Future startAllAudioElement() async { for (final element in _audioElements.values) { - if (element is html.AudioElement) { - await element.play(); + if (element is web.HTMLAudioElement) { + await element.play().toDart; } } return _audioContext.state == AudioContextState.running; } void stopAudio(String id) { - final audioElement = html.document.getElementById(audioPrefix + id); + final audioElement = web.document.getElementById(audioPrefix + id); if (audioElement != null) { - if (audioElement is html.AudioElement) { + if (audioElement is web.HTMLAudioElement) { audioElement.srcObject = null; } _audioElements.remove(id); @@ -72,23 +73,27 @@ void stopAudio(String id) { } } -html.DivElement findOrCreateAudioContainer() { - var div = html.document.getElementById(audioContainerId); +web.HTMLDivElement findOrCreateAudioContainer() { + var div = web.document.getElementById(audioContainerId); if (div != null) { - return div as html.DivElement; + return div as web.HTMLDivElement; } - div = html.DivElement(); + div = web.HTMLDivElement(); div.id = audioContainerId; - div.style.display = 'none'; - html.document.body?.append(div); - return div as html.DivElement; + (div as web.HTMLDivElement).style.display = 'none'; + web.document.body?.append(div); + return div; } void setSinkId(String id, String deviceId) { - final audioElement = html.document.getElementById(audioPrefix + id); - if (audioElement is html.AudioElement && + final audioElement = web.document.getElementById(audioPrefix + id); + if (audioElement is web.HTMLAudioElement && jsutil.hasProperty(audioElement, 'setSinkId')) { audioElement.setSinkId(deviceId); } } + +extension _SetSinkId on web.HTMLMediaElement { + external JSPromise setSinkId(String sinkId); +} diff --git a/pubspec.yaml b/pubspec.yaml index e5c4395f..bfe4762b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ version: 2.1.0 homepage: https://github.com/livekit/client-sdk-flutter environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" flutter: ">=1.17.0" dependencies: @@ -38,11 +38,11 @@ dependencies: uuid: '>=3.0.6' synchronized: ^3.0.0+3 protobuf: ^3.0.0 - flutter_webrtc: ^0.9.48+hotfix.1 + flutter_webrtc: ^0.10.1 device_info_plus: '>=8.0.0' js: ^0.6.4 platform_detect: ^2.0.7 - dart_webrtc: ^1.2.0 + dart_webrtc: ^1.3.0 sdp_transform: ^0.3.2 dev_dependencies: @@ -81,4 +81,4 @@ topics: - video - livestream - conference - \ No newline at end of file + diff --git a/web/crypto.dart b/web/crypto.dart index ff27126a..805e238c 100644 --- a/web/crypto.dart +++ b/web/crypto.dart @@ -13,11 +13,11 @@ // limitations under the License. import 'dart:async'; -import 'dart:html' as html; import 'dart:js_util' as jsutil; import 'dart:typed_data'; import 'package:js/js.dart'; +import 'package:web/web.dart' as web; @JS('Promise') class Promise { @@ -32,14 +32,14 @@ class Algorithm { @JS('crypto.subtle.encrypt') external Promise encrypt( dynamic algorithm, - html.CryptoKey key, + web.CryptoKey key, ByteBuffer data, ); @JS('crypto.subtle.decrypt') external Promise decrypt( dynamic algorithm, - html.CryptoKey key, + web.CryptoKey key, ByteBuffer data, ); @@ -66,7 +66,7 @@ ByteBuffer jsArrayBufferFrom(List data) { } @JS('crypto.subtle.importKey') -external Promise importKey( +external Promise importKey( String format, ByteBuffer keyData, dynamic algorithm, @@ -77,13 +77,13 @@ external Promise importKey( @JS('crypto.subtle.exportKey') external Promise exportKey( String format, - html.CryptoKey key, + web.CryptoKey key, ); @JS('crypto.subtle.deriveKey') -external Promise deriveKey( +external Promise deriveKey( dynamic algorithm, - html.CryptoKey baseKey, + web.CryptoKey baseKey, dynamic derivedKeyAlgorithm, bool extractable, List keyUsages); @@ -91,14 +91,14 @@ external Promise deriveKey( @JS('crypto.subtle.deriveBits') external Promise deriveBits( dynamic algorithm, - html.CryptoKey baseKey, + web.CryptoKey baseKey, int length, ); -Future impportKeyFromRawData(List secretKeyData, +Future impportKeyFromRawData(List secretKeyData, {required String webCryptoAlgorithm, required List keyUsages}) async { - return jsutil.promiseToFuture(importKey( + return jsutil.promiseToFuture(importKey( 'raw', jsArrayBufferFrom(secretKeyData), jsutil.jsify({'name': webCryptoAlgorithm}), diff --git a/web/e2ee.cryptor.dart b/web/e2ee.cryptor.dart index 2b4f9e9e..1c4134e8 100644 --- a/web/e2ee.cryptor.dart +++ b/web/e2ee.cryptor.dart @@ -12,16 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -// ignore_for_file: constant_identifier_names - import 'dart:async'; -import 'dart:html'; import 'dart:js'; +import 'dart:js_interop'; import 'dart:js_util' as jsutil; import 'dart:math'; import 'dart:typed_data'; import 'package:dart_webrtc/src/rtc_transform_stream.dart'; +import 'package:web/web.dart' as web; + import 'crypto.dart' as crypto; import 'e2ee.keyhandler.dart'; import 'e2ee.logger.dart'; @@ -152,7 +152,7 @@ class FrameCryptor { bool _enabled = false; CryptorError lastError = CryptorError.kNew; int currentKeyIndex = 0; - final DedicatedWorkerGlobalScope worker; + final web.DedicatedWorkerGlobalScope worker; SifGuard sifGuard = SifGuard(); void setParticipant(String identity, ParticipantKeyHandler keys) { @@ -235,7 +235,7 @@ class FrameCryptor { } void postMessage(Object message) { - worker.postMessage(message); + worker.postMessage(message.jsify()); } Future setupTransform({ @@ -333,8 +333,6 @@ class FrameCryptor { 'kind': kind, 'state': 'missingKey', 'error': 'Missing key for track $trackId', - 'currentKeyIndex': currentKeyIndex, - 'secretKey': secretKey.toString() }); } return; @@ -384,10 +382,7 @@ class FrameCryptor { 'trackId': trackId, 'kind': kind, 'state': 'ok', - 'error': 'encryption ok', - 'frameTrailer': frameTrailer.buffer.asUint8List(), - 'currentKeyIndex': currentKeyIndex, - 'secretKey': secretKey.toString(), + 'error': 'encryption ok' }); } @@ -478,10 +473,7 @@ class FrameCryptor { 'trackId': trackId, 'kind': kind, 'state': 'missingKey', - 'error': 'Missing key for track $trackId', - 'frameTrailer': frameTrailer.buffer.asUint8List(), - 'currentKeyIndex': keyIndex, - 'secretKey': initialKeySet?.encryptionKey.toString() + 'error': 'Missing key for track $trackId' }); } controller.enqueue(frame); @@ -504,7 +496,7 @@ class FrameCryptor { )); if (currentkeySet != initialKeySet) { - logger.warning( + logger.fine( 'ratchetKey: decryption ok, reset state to kKeyRatcheted'); await keyHandler.setKeySetFromMaterial( currentkeySet, initialKeyIndex); @@ -528,10 +520,7 @@ class FrameCryptor { 'trackId': trackId, 'kind': kind, 'state': 'keyRatcheted', - 'error': 'Key ratcheted ok', - 'frameTrailer': frameTrailer.buffer.asUint8List(), - 'currentKeyIndex': currentKeyIndex, - 'secretKey': currentkeySet.encryptionKey.toString() + 'error': 'Key ratcheted ok' }); } } catch (e) { @@ -554,6 +543,7 @@ class FrameCryptor { logger.finer( 'buffer: ${buffer.length}, decrypted: ${decrypted?.asUint8List().length ?? 0}'); var finalBuffer = BytesBuilder(); + finalBuffer.add(Uint8List.fromList(buffer.sublist(0, headerLength))); finalBuffer.add(decrypted!.asUint8List()); frame.data = crypto.jsArrayBufferFrom(finalBuffer.toBytes()); @@ -568,10 +558,7 @@ class FrameCryptor { 'trackId': trackId, 'kind': kind, 'state': 'ok', - 'error': 'decryption ok', - 'frameTrailer': frameTrailer.buffer.asUint8List(), - 'currentKeyIndex': currentKeyIndex, - 'secretKey': currentkeySet.encryptionKey.toString() + 'error': 'decryption ok' }); } diff --git a/web/e2ee.keyhandler.dart b/web/e2ee.keyhandler.dart index cf435fde..481b5730 100644 --- a/web/e2ee.keyhandler.dart +++ b/web/e2ee.keyhandler.dart @@ -13,10 +13,11 @@ // limitations under the License. import 'dart:async'; -import 'dart:html'; import 'dart:js_util' as jsutil; import 'dart:typed_data'; +import 'package:web/web.dart' as web; + import 'crypto.dart' as crypto; import 'e2ee.logger.dart'; import 'e2ee.utils.dart'; @@ -43,7 +44,7 @@ class KeyOptions { class KeyProvider { KeyProvider(this.worker, this.id, this.keyProviderOptions); - final DedicatedWorkerGlobalScope worker; + final web.DedicatedWorkerGlobalScope worker; final String id; final KeyOptions keyProviderOptions; var participantKeys = {}; @@ -94,8 +95,8 @@ const KEYRING_SIZE = 16; class KeySet { KeySet(this.material, this.encryptionKey); - CryptoKey material; - CryptoKey encryptionKey; + web.CryptoKey material; + web.CryptoKey encryptionKey; } class ParticipantKeyHandler { @@ -114,7 +115,7 @@ class ParticipantKeyHandler { final KeyOptions keyOptions; - final DedicatedWorkerGlobalScope worker; + final web.DedicatedWorkerGlobalScope worker; final String participantIdentity; @@ -171,8 +172,8 @@ class ParticipantKeyHandler { return newKey; } - Future ratchetMaterial( - CryptoKey currentMaterial, ByteBuffer newKeyBuffer) async { + Future ratchetMaterial( + web.CryptoKey currentMaterial, ByteBuffer newKeyBuffer) async { var newMaterial = await jsutil.promiseToFuture(crypto.importKey( 'raw', newKeyBuffer, @@ -204,19 +205,18 @@ class ParticipantKeyHandler { currentKeyIndex = keyIndex % cryptoKeyRing.length; } cryptoKeyRing[currentKeyIndex] = keySet; - logger.config('setKeySetFromMaterial: currentIndex: $currentKeyIndex'); } /// Derives a set of keys from the master key. /// See https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.1 - Future deriveKeys(CryptoKey material, Uint8List salt) async { + Future deriveKeys(web.CryptoKey material, Uint8List salt) async { var algorithmOptions = getAlgoOptions((material.algorithm as crypto.Algorithm).name, salt); // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#HKDF // https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams var encryptionKey = - await jsutil.promiseToFuture(crypto.deriveKey( + await jsutil.promiseToFuture(crypto.deriveKey( jsutil.jsify(algorithmOptions), material, jsutil.jsify({'name': 'AES-GCM', 'length': 128}), @@ -230,7 +230,7 @@ class ParticipantKeyHandler { /// Ratchets a key. See /// https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.5.1 - Future ratchet(CryptoKey material, Uint8List salt) async { + Future ratchet(web.CryptoKey material, Uint8List salt) async { var algorithmOptions = getAlgoOptions('PBKDF2', salt); // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits diff --git a/web/e2ee.utils.dart b/web/e2ee.utils.dart index 1e00d6a4..91384333 100644 --- a/web/e2ee.utils.dart +++ b/web/e2ee.utils.dart @@ -12,10 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:html'; import 'dart:js' as js; import 'dart:typed_data'; +import 'package:js/js_util.dart'; +import 'package:web/web.dart' as web; + import 'crypto.dart' as crypto; bool isE2EESupported() { @@ -31,10 +33,10 @@ bool isInsertableStreamSupported() { js.context['RTCRtpSender']['prototype']['createEncodedStreams'] != null; } -Future importKey( +Future importKey( Uint8List keyBytes, String algorithm, String usage) { // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey - return promiseToFuture(crypto.importKey( + return promiseToFuture(crypto.importKey( 'raw', crypto.jsArrayBufferFrom(keyBytes), js.JsObject.jsify({'name': algorithm}), @@ -43,10 +45,10 @@ Future importKey( )); } -Future createKeyMaterialFromString( +Future createKeyMaterialFromString( Uint8List keyBytes, String algorithm, String usage) { // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey - return promiseToFuture(crypto.importKey( + return promiseToFuture(crypto.importKey( 'raw', crypto.jsArrayBufferFrom(keyBytes), js.JsObject.jsify({'name': 'PBKDF2'}), diff --git a/web/e2ee.worker.dart b/web/e2ee.worker.dart index fc7e2d76..8bea5cd1 100644 --- a/web/e2ee.worker.dart +++ b/web/e2ee.worker.dart @@ -13,60 +13,23 @@ // limitations under the License. import 'dart:convert'; -import 'dart:html' as html; +import 'dart:js_interop'; import 'dart:js_util' as js_util; import 'dart:typed_data'; import 'package:collection/collection.dart'; import 'package:dart_webrtc/src/rtc_transform_stream.dart'; -import 'package:js/js.dart'; import 'package:logging/logging.dart'; +import 'package:web/web.dart' as web; import 'e2ee.cryptor.dart'; import 'e2ee.keyhandler.dart'; import 'e2ee.logger.dart'; @JS() -abstract class TransformMessage { - external String get msgType; - external String get kind; -} - -@anonymous -@JS() -class EnableTransformMessage { - external factory EnableTransformMessage({ - ReadableStream readable, - WritableStream writable, - String msgType, - String kind, - String participantId, - String trackId, - String codec, - }); - external ReadableStream get readable; - external WritableStream get writable; - external String get msgType; // 'encode' or 'decode' - external String get participantId; - external String get trackId; - external String get kind; - external String get codec; -} - -@anonymous -@JS() -class RemoveTransformMessage { - external factory RemoveTransformMessage( - {String msgType, String participantId, String trackId}); - external String get msgType; // 'removeTransform' - external String get participantId; - external String get trackId; -} +external web.DedicatedWorkerGlobalScope get self; -@JS('self') -external html.DedicatedWorkerGlobalScope get self; - -extension PropsRTCTransformEventHandler on html.DedicatedWorkerGlobalScope { +extension PropsRTCTransformEventHandler on web.DedicatedWorkerGlobalScope { set onrtctransform(Function(dynamic) callback) => js_util.setProperty(this, 'onrtctransform', callback); } @@ -116,7 +79,7 @@ void main() async { if (js_util.getProperty(self, 'RTCTransformEvent') != null) { logger.info('setup RTCTransformEvent event handler'); - self.onrtctransform = allowInterop((event) { + self.onrtctransform = (web.RTCTransformEvent event) { logger.info('Got onrtctransform event'); var transformer = (event as RTCTransformEvent).transformer; @@ -146,352 +109,355 @@ void main() async { trackId: trackId, kind: kind, codec: codec); - }); + }.toJS; } - self.onMessage.listen((e) async { - var msg = e.data; - var msgType = msg['msgType']; - var msgId = msg['msgId'] as String?; - logger.info('Got message $msgType, msgId $msgId'); - switch (msgType) { - case 'keyProviderInit': - { - var options = msg['keyOptions']; - var keyProviderId = msg['keyProviderId'] as String; - var keyProviderOptions = KeyOptions( - sharedKey: options['sharedKey'], - ratchetSalt: Uint8List.fromList( - base64Decode(options['ratchetSalt'] as String)), - ratchetWindowSize: options['ratchetWindowSize'], - failureTolerance: options['failureTolerance'] ?? -1, - uncryptedMagicBytes: options['uncryptedMagicBytes'] != null - ? Uint8List.fromList( - base64Decode(options['uncryptedMagicBytes'] as String)) - : null); - logger.config( - 'Init with keyProviderOptions:\n ${keyProviderOptions.toString()}'); - - var keyProvider = - KeyProvider(self, keyProviderId, keyProviderOptions); - keyProviders[keyProviderId] = keyProvider; - - self.postMessage({ - 'type': 'init', - 'msgId': msgId, - 'msgType': 'response', - }); + self.onmessage = (web.MessageEvent e) { + (e) async { + var msg = e.data; + var msgType = msg['msgType']; + var msgId = msg['msgId'] as String?; + logger.fine('Got message $msgType, msgId $msgId'); + switch (msgType) { + case 'keyProviderInit': + { + var options = msg['keyOptions']; + var keyProviderId = msg['keyProviderId'] as String; + var keyProviderOptions = KeyOptions( + sharedKey: options['sharedKey'], + ratchetSalt: Uint8List.fromList( + base64Decode(options['ratchetSalt'] as String)), + ratchetWindowSize: options['ratchetWindowSize'], + failureTolerance: options['failureTolerance'] ?? -1, + uncryptedMagicBytes: options['uncryptedMagicBytes'] != null + ? Uint8List.fromList( + base64Decode(options['uncryptedMagicBytes'] as String)) + : null); + logger.config( + 'Init with keyProviderOptions:\n ${keyProviderOptions.toString()}'); + + var keyProvider = + KeyProvider(self, keyProviderId, keyProviderOptions); + keyProviders[keyProviderId] = keyProvider; + + self.postMessage({ + 'type': 'init', + 'msgId': msgId, + 'msgType': 'response', + }.jsify()); + break; + } + case 'keyProviderDispose': + { + var keyProviderId = msg['keyProviderId'] as String; + logger.config('Dispose keyProvider $keyProviderId'); + keyProviders.remove(keyProviderId); + self.postMessage({ + 'type': 'dispose', + 'msgId': msgId, + 'msgType': 'response', + }.jsify()); + } break; - } - case 'keyProviderDispose': - { - var keyProviderId = msg['keyProviderId'] as String; - logger.config('Dispose keyProvider $keyProviderId'); - keyProviders.remove(keyProviderId); - self.postMessage({ - 'type': 'dispose', - 'msgId': msgId, - 'msgType': 'response', - }); - } - break; - case 'enable': - { - var enabled = msg['enabled'] as bool; - var trackId = msg['trackId'] as String; - - var cryptors = - participantCryptors.where((c) => c.trackId == trackId).toList(); - for (var cryptor in cryptors) { - logger.config('Set enable $enabled for trackId ${cryptor.trackId}'); - cryptor.setEnabled(enabled); + case 'enable': + { + var enabled = msg['enabled'] as bool; + var trackId = msg['trackId'] as String; + + var cryptors = + participantCryptors.where((c) => c.trackId == trackId).toList(); + for (var cryptor in cryptors) { + logger + .config('Set enable $enabled for trackId ${cryptor.trackId}'); + cryptor.setEnabled(enabled); + } + self.postMessage({ + 'type': 'cryptorEnabled', + 'enable': enabled, + 'msgId': msgId, + 'msgType': 'response', + }.jsify()); } - self.postMessage({ - 'type': 'cryptorEnabled', - 'enable': enabled, - 'msgId': msgId, - 'msgType': 'response', - }); - } - break; - case 'decode': - case 'encode': - { - var kind = msg['kind']; - var exist = msg['exist'] as bool; - var participantId = msg['participantId'] as String; - var trackId = msg['trackId']; - var readable = msg['readableStream'] as ReadableStream; - var writable = msg['writableStream'] as WritableStream; - var keyProviderId = msg['keyProviderId'] as String; - - logger.config( - 'SetupTransform for kind $kind, trackId $trackId, participantId $participantId, ${readable.runtimeType} ${writable.runtimeType}}'); - - var keyProvider = keyProviders[keyProviderId]; - if (keyProvider == null) { - logger.warning('KeyProvider not found for $keyProviderId'); + break; + case 'decode': + case 'encode': + { + var kind = msg['kind']; + var exist = msg['exist'] as bool; + var participantId = msg['participantId'] as String; + var trackId = msg['trackId']; + var readable = msg['readableStream'] as ReadableStream; + var writable = msg['writableStream'] as WritableStream; + var keyProviderId = msg['keyProviderId'] as String; + + logger.config( + 'SetupTransform for kind $kind, trackId $trackId, participantId $participantId, ${readable.runtimeType} ${writable.runtimeType}}'); + + var keyProvider = keyProviders[keyProviderId]; + if (keyProvider == null) { + logger.warning('KeyProvider not found for $keyProviderId'); + self.postMessage({ + 'type': 'cryptorSetup', + 'participantId': participantId, + 'trackId': trackId, + 'exist': exist, + 'operation': msgType, + 'error': 'KeyProvider not found', + 'msgId': msgId, + 'msgType': 'response', + }.jsify()); + return; + } + + var cryptor = getTrackCryptor(participantId, trackId, keyProvider); + + await cryptor.setupTransform( + operation: msgType, + readable: readable, + writable: writable, + trackId: trackId, + kind: kind, + ); + self.postMessage({ 'type': 'cryptorSetup', 'participantId': participantId, 'trackId': trackId, 'exist': exist, 'operation': msgType, - 'error': 'KeyProvider not found', 'msgId': msgId, 'msgType': 'response', - }); - return; + }.jsify()); + cryptor.lastError = CryptorError.kNew; } - - var cryptor = getTrackCryptor(participantId, trackId, keyProvider); - - await cryptor.setupTransform( - operation: msgType, - readable: readable, - writable: writable, - trackId: trackId, - kind: kind, - ); - - self.postMessage({ - 'type': 'cryptorSetup', - 'participantId': participantId, - 'trackId': trackId, - 'exist': exist, - 'operation': msgType, - 'msgId': msgId, - 'msgType': 'response', - }); - cryptor.lastError = CryptorError.kNew; - } - break; - case 'removeTransform': - { - var trackId = msg['trackId'] as String; - logger.config('Removing trackId $trackId'); - unsetCryptorParticipant(trackId); - self.postMessage({ - 'type': 'cryptorRemoved', - 'trackId': trackId, - 'msgId': msgId, - 'msgType': 'response', - }); - } - break; - case 'setKey': - case 'setSharedKey': - { - var key = Uint8List.fromList(base64Decode(msg['key'] as String)); - var keyIndex = msg['keyIndex'] as int; - var keyProviderId = msg['keyProviderId'] as String; - var keyProvider = keyProviders[keyProviderId]; - if (keyProvider == null) { - logger.warning('KeyProvider not found for $keyProviderId'); + break; + case 'removeTransform': + { + var trackId = msg['trackId'] as String; + logger.config('Removing trackId $trackId'); + unsetCryptorParticipant(trackId); self.postMessage({ - 'type': 'setKey', - 'error': 'KeyProvider not found', + 'type': 'cryptorRemoved', + 'trackId': trackId, 'msgId': msgId, 'msgType': 'response', - }); - return; - } - var keyProviderOptions = keyProvider.keyProviderOptions; - if (keyProviderOptions.sharedKey) { - logger.config('Set SharedKey keyIndex $keyIndex'); - keyProvider.setSharedKey(key, keyIndex: keyIndex); - } else { - var participantId = msg['participantId'] as String; - logger.config( - 'Set key for participant $participantId, keyIndex $keyIndex'); - await keyProvider - .getParticipantKeyHandler(participantId) - .setKey(key, keyIndex: keyIndex); + }.jsify()); } + break; + case 'setKey': + case 'setSharedKey': + { + var key = Uint8List.fromList(base64Decode(msg['key'] as String)); + var keyIndex = msg['keyIndex'] as int; + var keyProviderId = msg['keyProviderId'] as String; + var keyProvider = keyProviders[keyProviderId]; + if (keyProvider == null) { + logger.warning('KeyProvider not found for $keyProviderId'); + self.postMessage({ + 'type': 'setKey', + 'error': 'KeyProvider not found', + 'msgId': msgId, + 'msgType': 'response', + }.jsify()); + return; + } + var keyProviderOptions = keyProvider.keyProviderOptions; + if (keyProviderOptions.sharedKey) { + logger.config('Set SharedKey keyIndex $keyIndex'); + keyProvider.setSharedKey(key, keyIndex: keyIndex); + } else { + var participantId = msg['participantId'] as String; + logger.config( + 'Set key for participant $participantId, keyIndex $keyIndex'); + await keyProvider + .getParticipantKeyHandler(participantId) + .setKey(key, keyIndex: keyIndex); + } - self.postMessage({ - 'type': 'setKey', - 'participantId': msg['participantId'], - 'sharedKey': keyProviderOptions.sharedKey, - 'keyIndex': keyIndex, - 'msgId': msgId, - 'msgType': 'response', - }); - } - break; - case 'ratchetKey': - case 'ratchetSharedKey': - { - var keyIndex = msg['keyIndex']; - var participantId = msg['participantId'] as String; - var keyProviderId = msg['keyProviderId'] as String; - var keyProvider = keyProviders[keyProviderId]; - if (keyProvider == null) { - logger.warning('KeyProvider not found for $keyProviderId'); self.postMessage({ 'type': 'setKey', - 'error': 'KeyProvider not found', + 'participantId': msg['participantId'], + 'sharedKey': keyProviderOptions.sharedKey, + 'keyIndex': keyIndex, 'msgId': msgId, 'msgType': 'response', - }); - return; - } - var keyProviderOptions = keyProvider.keyProviderOptions; - Uint8List? newKey; - if (keyProviderOptions.sharedKey) { - logger.config('RatchetKey for SharedKey, keyIndex $keyIndex'); - newKey = - await keyProvider.getSharedKeyHandler().ratchetKey(keyIndex); - } else { - logger.config( - 'RatchetKey for participant $participantId, keyIndex $keyIndex'); - newKey = await keyProvider - .getParticipantKeyHandler(participantId) - .ratchetKey(keyIndex); + }.jsify()); } + break; + case 'ratchetKey': + case 'ratchetSharedKey': + { + var keyIndex = msg['keyIndex']; + var participantId = msg['participantId'] as String; + var keyProviderId = msg['keyProviderId'] as String; + var keyProvider = keyProviders[keyProviderId]; + if (keyProvider == null) { + logger.warning('KeyProvider not found for $keyProviderId'); + self.postMessage({ + 'type': 'setKey', + 'error': 'KeyProvider not found', + 'msgId': msgId, + 'msgType': 'response', + }.jsify()); + return; + } + var keyProviderOptions = keyProvider.keyProviderOptions; + Uint8List? newKey; + if (keyProviderOptions.sharedKey) { + logger.config('RatchetKey for SharedKey, keyIndex $keyIndex'); + newKey = + await keyProvider.getSharedKeyHandler().ratchetKey(keyIndex); + } else { + logger.config( + 'RatchetKey for participant $participantId, keyIndex $keyIndex'); + newKey = await keyProvider + .getParticipantKeyHandler(participantId) + .ratchetKey(keyIndex); + } - self.postMessage({ - 'type': 'ratchetKey', - 'sharedKey': keyProviderOptions.sharedKey, - 'participantId': participantId, - 'newKey': newKey != null ? base64Encode(newKey) : '', - 'keyIndex': keyIndex, - 'msgId': msgId, - 'msgType': 'response', - }); - } - break; - case 'setKeyIndex': - { - var keyIndex = msg['index']; - var trackId = msg['trackId'] as String; - logger.config('Setup key index for track $trackId'); - var cryptors = - participantCryptors.where((c) => c.trackId == trackId).toList(); - for (var c in cryptors) { - logger.config('Set keyIndex for trackId ${c.trackId}'); - c.setKeyIndex(keyIndex); + self.postMessage({ + 'type': 'ratchetKey', + 'sharedKey': keyProviderOptions.sharedKey, + 'participantId': participantId, + 'newKey': newKey != null ? base64Encode(newKey) : '', + 'keyIndex': keyIndex, + 'msgId': msgId, + 'msgType': 'response', + }.jsify()); } + break; + case 'setKeyIndex': + { + var keyIndex = msg['index']; + var trackId = msg['trackId'] as String; + logger.config('Setup key index for track $trackId'); + var cryptors = + participantCryptors.where((c) => c.trackId == trackId).toList(); + for (var c in cryptors) { + logger.config('Set keyIndex for trackId ${c.trackId}'); + c.setKeyIndex(keyIndex); + } - self.postMessage({ - 'type': 'setKeyIndex', - 'keyIndex': keyIndex, - 'msgId': msgId, - 'msgType': 'response', - }); - } - break; - case 'exportKey': - case 'exportSharedKey': - { - var keyIndex = msg['keyIndex'] as int; - var participantId = msg['participantId'] as String; - var keyProviderId = msg['keyProviderId'] as String; - var keyProvider = keyProviders[keyProviderId]; - if (keyProvider == null) { - logger.warning('KeyProvider not found for $keyProviderId'); self.postMessage({ - 'type': 'setKey', - 'error': 'KeyProvider not found', + 'type': 'setKeyIndex', + 'keyIndex': keyIndex, 'msgId': msgId, 'msgType': 'response', - }); - return; - } - var keyProviderOptions = keyProvider.keyProviderOptions; - Uint8List? key; - if (keyProviderOptions.sharedKey) { - logger.config('Export SharedKey keyIndex $keyIndex'); - key = await keyProvider.getSharedKeyHandler().exportKey(keyIndex); - } else { - logger.config( - 'Export key for participant $participantId, keyIndex $keyIndex'); - key = await keyProvider - .getParticipantKeyHandler(participantId) - .exportKey(keyIndex); + }.jsify()); } - self.postMessage({ - 'type': 'exportKey', - 'participantId': participantId, - 'keyIndex': keyIndex, - 'exportedKey': key != null ? base64Encode(key) : '', - 'msgId': msgId, - 'msgType': 'response', - }); - } - break; - case 'setSifTrailer': - { - var sifTrailer = - Uint8List.fromList(base64Decode(msg['sifTrailer'] as String)); - var keyProviderId = msg['keyProviderId'] as String; - var keyProvider = keyProviders[keyProviderId]; - if (keyProvider == null) { - logger.warning('KeyProvider not found for $keyProviderId'); + break; + case 'exportKey': + case 'exportSharedKey': + { + var keyIndex = msg['keyIndex'] as int; + var participantId = msg['participantId'] as String; + var keyProviderId = msg['keyProviderId'] as String; + var keyProvider = keyProviders[keyProviderId]; + if (keyProvider == null) { + logger.warning('KeyProvider not found for $keyProviderId'); + self.postMessage({ + 'type': 'setKey', + 'error': 'KeyProvider not found', + 'msgId': msgId, + 'msgType': 'response', + }.jsify()); + return; + } + var keyProviderOptions = keyProvider.keyProviderOptions; + Uint8List? key; + if (keyProviderOptions.sharedKey) { + logger.config('Export SharedKey keyIndex $keyIndex'); + key = await keyProvider.getSharedKeyHandler().exportKey(keyIndex); + } else { + logger.config( + 'Export key for participant $participantId, keyIndex $keyIndex'); + key = await keyProvider + .getParticipantKeyHandler(participantId) + .exportKey(keyIndex); + } self.postMessage({ - 'type': 'setKey', - 'error': 'KeyProvider not found', + 'type': 'exportKey', + 'participantId': participantId, + 'keyIndex': keyIndex, + 'exportedKey': key != null ? base64Encode(key) : '', 'msgId': msgId, 'msgType': 'response', - }); - return; - } - keyProvider.setSifTrailer(sifTrailer); - logger.config('SetSifTrailer = $sifTrailer'); - for (var c in participantCryptors) { - c.setSifTrailer(sifTrailer); + }.jsify()); } + break; + case 'setSifTrailer': + { + var sifTrailer = + Uint8List.fromList(base64Decode(msg['sifTrailer'] as String)); + var keyProviderId = msg['keyProviderId'] as String; + var keyProvider = keyProviders[keyProviderId]; + if (keyProvider == null) { + logger.warning('KeyProvider not found for $keyProviderId'); + self.postMessage({ + 'type': 'setKey', + 'error': 'KeyProvider not found', + 'msgId': msgId, + 'msgType': 'response', + }.jsify()); + return; + } + keyProvider.setSifTrailer(sifTrailer); + logger.config('SetSifTrailer = $sifTrailer'); + for (var c in participantCryptors) { + c.setSifTrailer(sifTrailer); + } - self.postMessage({ - 'type': 'setSifTrailer', - 'msgId': msgId, - 'msgType': 'response', - }); - } - break; - case 'updateCodec': - { - var codec = msg['codec'] as String; - var trackId = msg['trackId'] as String; - logger.config('Update codec for trackId $trackId, codec $codec'); - var cryptor = - participantCryptors.firstWhereOrNull((c) => c.trackId == trackId); - cryptor?.updateCodec(codec); - - self.postMessage({ - 'type': 'updateCodec', - 'msgId': msgId, - 'msgType': 'response', - }); - } - break; - case 'dispose': - { - var trackId = msg['trackId'] as String; - logger.config('Dispose for trackId $trackId'); - var cryptor = - participantCryptors.firstWhereOrNull((c) => c.trackId == trackId); - if (cryptor != null) { - cryptor.lastError = CryptorError.kDisposed; self.postMessage({ - 'type': 'cryptorDispose', - 'participantId': cryptor.participantIdentity, - 'trackId': trackId, + 'type': 'setSifTrailer', 'msgId': msgId, 'msgType': 'response', - }); - } else { + }.jsify()); + } + break; + case 'updateCodec': + { + var codec = msg['codec'] as String; + var trackId = msg['trackId'] as String; + logger.config('Update codec for trackId $trackId, codec $codec'); + var cryptor = participantCryptors + .firstWhereOrNull((c) => c.trackId == trackId); + cryptor?.updateCodec(codec); + self.postMessage({ - 'type': 'cryptorDispose', - 'error': 'cryptor not found', + 'type': 'updateCodec', 'msgId': msgId, 'msgType': 'response', - }); + }.jsify()); } - } - break; - default: - logger.warning('Unknown message kind $msg'); - } - }); + break; + case 'dispose': + { + var trackId = msg['trackId'] as String; + logger.config('Dispose for trackId $trackId'); + var cryptor = participantCryptors + .firstWhereOrNull((c) => c.trackId == trackId); + if (cryptor != null) { + cryptor.lastError = CryptorError.kDisposed; + self.postMessage({ + 'type': 'cryptorDispose', + 'participantId': cryptor.participantIdentity, + 'trackId': trackId, + 'msgId': msgId, + 'msgType': 'response', + }.jsify()); + } else { + self.postMessage({ + 'type': 'cryptorDispose', + 'error': 'cryptor not found', + 'msgId': msgId, + 'msgType': 'response', + }.jsify()); + } + } + break; + default: + logger.warning('Unknown message kind $msg'); + } + }(e); + }.toJS; }