From cc470d345343ceac25624bb3835e93065f6411b9 Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Mon, 8 Apr 2024 13:58:46 +0800 Subject: [PATCH 1/3] fix/migrate-dart-html-to-web-pkg. --- lib/src/factory.dart | 5 ++ lib/src/frame_cryptor.dart | 22 +++++ lib/src/media_constraints.dart | 150 ++++++++++++++++++++++++++++++++ lib/src/mediadevices.dart | 18 ---- lib/src/rtc_configuration.dart | 120 +++++++++++++------------ lib/src/rtc_peerconnection.dart | 11 +++ lib/webrtc_interface.dart | 2 + 7 files changed, 256 insertions(+), 72 deletions(-) create mode 100644 lib/src/media_constraints.dart diff --git a/lib/src/factory.dart b/lib/src/factory.dart index 2ea1e22..b23e5e3 100644 --- a/lib/src/factory.dart +++ b/lib/src/factory.dart @@ -2,15 +2,20 @@ import 'frame_cryptor.dart'; import 'media_recorder.dart'; import 'media_stream.dart'; import 'navigator.dart'; +import 'rtc_configuration.dart'; import 'rtc_peerconnection.dart'; import 'rtc_rtp_capabilities.dart'; import 'rtc_video_renderer.dart'; abstract class RTCFactory { + @Deprecated('use newPeerConnection() instead') Future createPeerConnection( Map configuration, [Map constraints]); + Future newPeerConnection(RTCConfiguration configuration) => + throw UnimplementedError(); + Future createLocalMediaStream(String label); Future getRtpSenderCapabilities(String kind); diff --git a/lib/src/frame_cryptor.dart b/lib/src/frame_cryptor.dart index 4308dd1..5268124 100644 --- a/lib/src/frame_cryptor.dart +++ b/lib/src/frame_cryptor.dart @@ -16,12 +16,19 @@ class KeyProviderOptions { required this.ratchetWindowSize, this.uncryptedMagicBytes, this.failureTolerance = -1, + this.key_ring_size = 16, + this.discard_frame_when_cryptor_not_ready = false, }); bool sharedKey; Uint8List ratchetSalt; Uint8List? uncryptedMagicBytes; int ratchetWindowSize; int failureTolerance; + + /// key ring size should be between 1 and 255 + /// default is 16 + int key_ring_size; + bool discard_frame_when_cryptor_not_ready; Map toJson() { return { 'sharedKey': sharedKey, @@ -30,6 +37,8 @@ class KeyProviderOptions { 'uncryptedMagicBytes': uncryptedMagicBytes, 'ratchetWindowSize': ratchetWindowSize, 'failureTolerance': failureTolerance, + 'keyRingSize': key_ring_size, + 'discardFrameWhenCryptorNotReady': discard_frame_when_cryptor_not_ready, }; } } @@ -84,6 +93,19 @@ enum FrameCryptorState { FrameCryptorStateInternalError, } +class FrameCryptorOptions { + FrameCryptorOptions({ + this.discardUnableDecryptedFrames = false, + }); + + /// Discard frames when frame crypto is disabled. + /// Because of the wrong key or decoding the encrypted frame or outputting + /// garbled audio + /// when called FrameCryptor.setEnabled(false); if this parameter is true, the + /// frame will discarded + final bool discardUnableDecryptedFrames; +} + /// Frame encryption/decryption. /// abstract class FrameCryptor { diff --git a/lib/src/media_constraints.dart b/lib/src/media_constraints.dart new file mode 100644 index 0000000..10f18b8 --- /dev/null +++ b/lib/src/media_constraints.dart @@ -0,0 +1,150 @@ +class MediaTrackConstraints { + MediaTrackConstraints({this.deviceId, this.groupId}); + factory MediaTrackConstraints.fromMap(Map map) { + return MediaTrackConstraints( + deviceId: map['deviceId'] as String?, + groupId: map['groupId'] as String?, + ); + } + final String? deviceId; + final String? groupId; + + Map toMap() { + return { + if (deviceId != null) 'deviceId': deviceId, + if (groupId != null) 'groupId': groupId, + }; + } +} + +class AudioTrackConstraints extends MediaTrackConstraints { + AudioTrackConstraints({ + required String deviceId, + required String groupId, + this.autoGainControl, + this.channelCount, + this.echoCancellation, + this.latency, + this.noiseSuppression, + this.sampleRate, + this.sampleSize, + this.volume, + }) : super(deviceId: deviceId, groupId: groupId); + + factory AudioTrackConstraints.fromMap(Map map) { + return AudioTrackConstraints( + deviceId: map['deviceId'] as String, + groupId: map['groupId'] as String, + autoGainControl: map['autoGainControl'] as bool?, + channelCount: map['channelCount'] as bool?, + echoCancellation: map['echoCancellation'] as bool?, + latency: map['latency'] as bool?, + noiseSuppression: map['noiseSuppression'] as bool?, + sampleRate: map['sampleRate'] as bool?, + sampleSize: map['sampleSize'] as bool?, + volume: map['volume'] as bool?, + ); + } + + bool? autoGainControl; + bool? channelCount; + bool? echoCancellation; + bool? latency; + bool? noiseSuppression; + bool? sampleRate; + bool? sampleSize; + bool? volume; + + @override + Map toMap() { + return { + if (deviceId != null) 'deviceId': deviceId, + if (groupId != null) 'groupId': groupId, + if (autoGainControl != null) 'autoGainControl': autoGainControl, + if (channelCount != null) 'channelCount': channelCount, + if (echoCancellation != null) 'echoCancellation': echoCancellation, + if (latency != null) 'latency': latency, + if (noiseSuppression != null) 'noiseSuppression': noiseSuppression, + if (sampleRate != null) 'sampleRate': sampleRate, + if (sampleSize != null) 'sampleSize': sampleSize, + if (volume != null) 'volume': volume, + }; + } +} + +class VideoTrackConstraints extends MediaTrackConstraints { + VideoTrackConstraints({ + required String deviceId, + required String groupId, + this.aspectRatio, + this.frameRate, + this.facingMode, + this.height, + this.width, + }) : super(deviceId: deviceId, groupId: groupId); + + factory VideoTrackConstraints.fromMap(Map map) { + return VideoTrackConstraints( + deviceId: map['deviceId'] as String, + groupId: map['groupId'] as String, + aspectRatio: map['aspectRatio'] as bool?, + frameRate: map['frameRate'] as bool?, + facingMode: map['facingMode'] as bool?, + height: map['height'] as bool?, + width: map['width'] as bool?, + ); + } + + bool? aspectRatio; + bool? frameRate; + bool? facingMode; + bool? height; + bool? width; + + @override + Map toMap() { + return { + if (deviceId != null) 'deviceId': deviceId, + if (groupId != null) 'groupId': groupId, + if (aspectRatio != null) 'aspectRatio': aspectRatio, + if (frameRate != null) 'frameRate': frameRate, + if (facingMode != null) 'facingMode': facingMode, + if (height != null) 'height': height, + if (width != null) 'width': width, + }; + } +} + +class MediaStreamConstraints { + MediaStreamConstraints({ + this.audio, + this.video, + }); + + factory MediaStreamConstraints.fromMap(Map map) { + return MediaStreamConstraints( + audio: map['audio'] is bool + ? map['audio'] + : AudioTrackConstraints.fromMap(map['audio']), + video: map['video'] is bool + ? map['video'] + : VideoTrackConstraints.fromMap(map['video']), + ); + } + + // bool or AudioTrackConstraints + dynamic audio; + // bool or VideoTrackConstraints + dynamic video; + + Map toMap() { + return { + if (audio != null) + 'audio': + audio is bool ? audio : (audio as AudioTrackConstraints).toMap(), + if (video != null) + 'video': + video is bool ? video : (video as VideoTrackConstraints).toMap(), + }; + } +} diff --git a/lib/src/mediadevices.dart b/lib/src/mediadevices.dart index 9e43527..000f965 100644 --- a/lib/src/mediadevices.dart +++ b/lib/src/mediadevices.dart @@ -1,23 +1,5 @@ import 'media_stream.dart'; -class MediaStreamConstraints { - MediaStreamConstraints({this.audio, this.video}); - - /// Either a bool (which indicates whether or not an audio track is requested) - /// or a MediaTrackConstraints object providing the constraints which must be - /// met by the audio track included in the returned MediaStream. - /// - /// If constraints are specified, an audio track is inherently requested. - dynamic audio; - - /// Either a bool (which indicates whether or not a video track is requested) - /// or a MediaTrackConstraints object providing the constraints which must be - /// met by the video track included in the returned MediaStream. - /// - /// If constraints are specified, a video track is inherently requested. - dynamic video; -} - /// [MediaTrackSupportedConstraints] represents the list of constraints /// controlling the capabilities of a [MediaStreamTrack]. class MediaTrackSupportedConstraints { diff --git a/lib/src/rtc_configuration.dart b/lib/src/rtc_configuration.dart index 0cac1a2..1297636 100644 --- a/lib/src/rtc_configuration.dart +++ b/lib/src/rtc_configuration.dart @@ -1,54 +1,66 @@ -// abstract class RTCOfferOptions { -// RTCOfferOptions({ -// bool iceRestart, -// bool offerToReceiveAudio, -// bool offerToReceiveVideo, -// bool voiceActivityDetection, -// }); -// bool get iceRestart; -// bool get offerToReceiveAudio; -// bool get offerToReceiveVideo; -// bool get voiceActivityDetection; -// } - -// abstract class RTCAnswerOptions { -// RTCAnswerOptions({bool voiceActivityDetection}); -// bool get voiceActivityDetection; -// } - -// abstract class RTCConfiguration { -// RTCConfiguration({ -// List iceServers, -// String rtcpMuxPolicy, -// String iceTransportPolicy, -// String bundlePolicy, -// String peerIdentity, -// int iceCandidatePoolSize, -// }); -// List get iceServers; - -// ///Optional: 'negotiate' or 'require' -// String get rtcpMuxPolicy; - -// ///Optional: 'relay' or 'all' -// String get iceTransportPolicy; - -// /// A DOMString which specifies the target peer identity for the -// /// RTCPeerConnection. If this value is set (it defaults to null), -// /// the RTCPeerConnection will not connect to a remote peer unless -// /// it can successfully authenticate with the given name. -// String get peerIdentity; - -// int get iceCandidatePoolSize; - -// ///Optional: 'balanced' | 'max-compat' | 'max-bundle' -// String get bundlePolicy; -// } - -// abstract class RTCIceServer { -// RTCIceServer({String urls, String username, String credential}); -// // String or List -// dynamic get urls; -// String get username; -// String get credential; -// } +class RTCOfferOptions { + bool? iceRestart; + bool? offerToReceiveAudio; + bool? offerToReceiveVideo; + bool? voiceActivityDetection; +} + +class RTCAnswerOptions { + bool? voiceActivityDetection; +} + +class RTCConfiguration { + RTCConfiguration( + {this.iceServers, + this.rtcpMuxPolicy, + this.iceTransportPolicy, + this.peerIdentity, + this.iceCandidatePoolSize, + this.bundlePolicy}); + factory RTCConfiguration.fromMap(Map map) { + return RTCConfiguration( + iceServers: map['iceServers'] != null + ? (map['iceServers'] as List) + .map((e) => RTCIceServer.fromMap(e)) + .toList() + : null, + rtcpMuxPolicy: map['rtcpMuxPolicy'], + iceTransportPolicy: map['iceTransportPolicy'], + peerIdentity: map['peerIdentity'], + iceCandidatePoolSize: map['iceCandidatePoolSize'], + bundlePolicy: map['bundlePolicy'], + ); + } + List? iceServers; + + ///Optional: 'negotiate' or 'require' + String? rtcpMuxPolicy; + + ///Optional: 'relay' or 'all' + String? iceTransportPolicy; + + /// A DOMString which specifies the target peer identity for the + /// RTCPeerConnection. If this value is set (it defaults to null), + /// the RTCPeerConnection will not connect to a remote peer unless + /// it can successfully authenticate with the given name. + String? peerIdentity; + + int? iceCandidatePoolSize; + + ///Optional: 'balanced' | 'max-compat' | 'max-bundle' + String? bundlePolicy; +} + +class RTCIceServer { + RTCIceServer({this.urls, this.username, this.credential}); + factory RTCIceServer.fromMap(Map map) { + return RTCIceServer( + urls: map['urls'] != null ? List.from(map['urls']) : null, + username: map['username'], + credential: map['credential'], + ); + } + List? urls; + String? username; + String? credential; +} diff --git a/lib/src/rtc_peerconnection.dart b/lib/src/rtc_peerconnection.dart index a7d26d0..dd7c7a1 100644 --- a/lib/src/rtc_peerconnection.dart +++ b/lib/src/rtc_peerconnection.dart @@ -22,10 +22,16 @@ abstract class RTCPeerConnection { Function(RTCIceGatheringState state)? onIceGatheringState; Function(RTCIceConnectionState state)? onIceConnectionState; Function(RTCIceCandidate candidate)? onIceCandidate; + + @Deprecated('Deprecated API') Function(MediaStream stream)? onAddStream; + @Deprecated('Deprecated API') Function(MediaStream stream)? onRemoveStream; + @Deprecated('Deprecated API') Function(MediaStream stream, MediaStreamTrack track)? onAddTrack; + @Deprecated('Deprecated API') Function(MediaStream stream, MediaStreamTrack track)? onRemoveTrack; + Function(RTCDataChannel channel)? onDataChannel; Function()? onRenegotiationNeeded; @@ -67,8 +73,10 @@ abstract class RTCPeerConnection { Future createAnswer( [Map constraints]); + @Deprecated('Deprecated API') Future addStream(MediaStream stream); + @Deprecated('Deprecated API') Future removeStream(MediaStream stream); Future getLocalDescription(); @@ -83,8 +91,10 @@ abstract class RTCPeerConnection { Future> getStats([MediaStreamTrack? track]); + @Deprecated('Deprecated API') List getLocalStreams(); + @Deprecated('Deprecated API') List getRemoteStreams(); Future createDataChannel( @@ -94,6 +104,7 @@ abstract class RTCPeerConnection { Future close(); + @Deprecated('Deprecated API, use RTCRtpSender.dtmf instead') RTCDTMFSender createDtmfSender(MediaStreamTrack track); /// Unified-Plan. diff --git a/lib/webrtc_interface.dart b/lib/webrtc_interface.dart index 0f90fca..7308a58 100644 --- a/lib/webrtc_interface.dart +++ b/lib/webrtc_interface.dart @@ -3,11 +3,13 @@ library webrtc_interface; export 'src/enums.dart'; export 'src/factory.dart'; export 'src/frame_cryptor.dart'; +export 'src/media_constraints.dart'; export 'src/media_recorder.dart'; export 'src/media_stream.dart'; export 'src/media_stream_track.dart'; export 'src/mediadevices.dart'; export 'src/navigator.dart'; +export 'src/rtc_configuration.dart'; export 'src/rtc_data_channel.dart'; export 'src/rtc_dtmf_sender.dart'; export 'src/rtc_ice_candidate.dart'; From e31af0a93f618dcaf5ddcea240637748c6654fae Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Mon, 8 Apr 2024 13:59:57 +0800 Subject: [PATCH 2/3] cleanup. --- lib/src/frame_cryptor.dart | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/lib/src/frame_cryptor.dart b/lib/src/frame_cryptor.dart index 5268124..a28addb 100644 --- a/lib/src/frame_cryptor.dart +++ b/lib/src/frame_cryptor.dart @@ -93,19 +93,6 @@ enum FrameCryptorState { FrameCryptorStateInternalError, } -class FrameCryptorOptions { - FrameCryptorOptions({ - this.discardUnableDecryptedFrames = false, - }); - - /// Discard frames when frame crypto is disabled. - /// Because of the wrong key or decoding the encrypted frame or outputting - /// garbled audio - /// when called FrameCryptor.setEnabled(false); if this parameter is true, the - /// frame will discarded - final bool discardUnableDecryptedFrames; -} - /// Frame encryption/decryption. /// abstract class FrameCryptor { From ac08b9dbb103db44073707b01d2d495a6dd93fb7 Mon Sep 17 00:00:00 2001 From: cloudwebrtc Date: Mon, 8 Apr 2024 14:32:02 +0800 Subject: [PATCH 3/3] revert changes. --- lib/src/frame_cryptor.dart | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/src/frame_cryptor.dart b/lib/src/frame_cryptor.dart index a28addb..4308dd1 100644 --- a/lib/src/frame_cryptor.dart +++ b/lib/src/frame_cryptor.dart @@ -16,19 +16,12 @@ class KeyProviderOptions { required this.ratchetWindowSize, this.uncryptedMagicBytes, this.failureTolerance = -1, - this.key_ring_size = 16, - this.discard_frame_when_cryptor_not_ready = false, }); bool sharedKey; Uint8List ratchetSalt; Uint8List? uncryptedMagicBytes; int ratchetWindowSize; int failureTolerance; - - /// key ring size should be between 1 and 255 - /// default is 16 - int key_ring_size; - bool discard_frame_when_cryptor_not_ready; Map toJson() { return { 'sharedKey': sharedKey, @@ -37,8 +30,6 @@ class KeyProviderOptions { 'uncryptedMagicBytes': uncryptedMagicBytes, 'ratchetWindowSize': ratchetWindowSize, 'failureTolerance': failureTolerance, - 'keyRingSize': key_ring_size, - 'discardFrameWhenCryptorNotReady': discard_frame_when_cryptor_not_ready, }; } }