chore: Use webrtc interface to build the voip module.
This commit is contained in:
parent
56d9ba7d4a
commit
93b623f2d5
|
|
@ -1,11 +1,57 @@
|
|||
import 'dart:async';
|
||||
import 'dart:core';
|
||||
|
||||
import 'voip_abstract.dart';
|
||||
import 'package:webrtc_interface/webrtc_interface.dart';
|
||||
import 'package:sdp_transform/sdp_transform.dart' as sdp_transform;
|
||||
|
||||
import '../matrix.dart';
|
||||
|
||||
MediaDevices mediaDevices = Null as MediaDevices;
|
||||
RTCFactory factory = Null as RTCFactory;
|
||||
|
||||
class RTCVideoRenderer extends VideoRenderer {
|
||||
RTCVideoRenderer() : super() {
|
||||
muted = true;
|
||||
}
|
||||
|
||||
@override
|
||||
late bool muted;
|
||||
|
||||
@override
|
||||
MediaStream? srcObject;
|
||||
|
||||
@override
|
||||
Future<bool> audioOutput(String deviceId) {
|
||||
// TODO: implement audioOutput
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> initialize() {
|
||||
// TODO: implement initialize
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
// TODO: implement renderVideo
|
||||
bool get renderVideo => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
// TODO: implement textureId
|
||||
int? get textureId => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
// TODO: implement videoHeight
|
||||
int get videoHeight => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
// TODO: implement videoWidth
|
||||
int get videoWidth => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<void> dispose() => throw UnimplementedError();
|
||||
}
|
||||
|
||||
/// The default life time for call events, in millisecond.
|
||||
const lifetimeMs = 10 * 1000;
|
||||
|
||||
|
|
@ -393,7 +439,7 @@ class CallSession {
|
|||
try {
|
||||
await pc!.setRemoteDescription(description);
|
||||
if (description.type == 'offer') {
|
||||
final answer = await pc!.createAnswer();
|
||||
final answer = await pc!.createAnswer({});
|
||||
await room.sendCallNegotiate(
|
||||
callId, lifetimeMs, localPartyId, answer.sdp!,
|
||||
type: answer.type!);
|
||||
|
|
@ -555,7 +601,8 @@ class CallSession {
|
|||
|
||||
if (purpose == SDPStreamMetadataPurpose.Usermedia) {
|
||||
speakerOn = type == CallType.kVideo;
|
||||
if (!kIsWeb && !voip.background) {
|
||||
//TODO: Confirm that the platform is not Web.
|
||||
if (/*!kIsWeb && */ !voip.background) {
|
||||
final audioTrack = stream.getAudioTracks()[0];
|
||||
audioTrack.enableSpeakerphone(speakerOn);
|
||||
}
|
||||
|
|
@ -658,13 +705,16 @@ class CallSession {
|
|||
speakerOn = !speakerOn;
|
||||
}
|
||||
|
||||
//TODO: move to the app.
|
||||
Future<void> switchCamera() async {
|
||||
if (localUserMediaStream != null) {
|
||||
/*
|
||||
await Helper.switchCamera(
|
||||
localUserMediaStream!.stream!.getVideoTracks()[0]);
|
||||
if (kIsMobile) {
|
||||
facingMode == 'user' ? facingMode = 'environment' : facingMode = 'user';
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -960,7 +1010,7 @@ class CallSession {
|
|||
: false,
|
||||
};
|
||||
try {
|
||||
return await navigator.mediaDevices.getUserMedia(mediaConstraints);
|
||||
return await mediaDevices.getUserMedia(mediaConstraints);
|
||||
} catch (e) {
|
||||
_getUserMediaFailed(e);
|
||||
}
|
||||
|
|
@ -973,7 +1023,7 @@ class CallSession {
|
|||
'video': true,
|
||||
};
|
||||
try {
|
||||
return await navigator.mediaDevices.getDisplayMedia(mediaConstraints);
|
||||
return await mediaDevices.getDisplayMedia(mediaConstraints);
|
||||
} catch (e) {
|
||||
_getUserMediaFailed(e);
|
||||
}
|
||||
|
|
@ -985,7 +1035,7 @@ class CallSession {
|
|||
'iceServers': opts.iceServers,
|
||||
'sdpSemantics': 'unified-plan'
|
||||
};
|
||||
final pc = await createPeerConnection(configuration);
|
||||
final pc = await factory.createPeerConnection(configuration);
|
||||
pc.onTrack = (RTCTrackEvent event) {
|
||||
if (event.streams.isNotEmpty) {
|
||||
final stream = event.streams[0];
|
||||
|
|
@ -1115,47 +1165,16 @@ class CallSession {
|
|||
}
|
||||
}
|
||||
|
||||
class VoIP with WidgetsBindingObserver {
|
||||
class VoIP {
|
||||
TurnServerCredentials? _turnServerCredentials;
|
||||
Map<String, CallSession> calls = <String, CallSession>{};
|
||||
String? currentCID;
|
||||
//ConnectivityResult _currentConnectivity;
|
||||
Function(CallSession session)? onIncomingCall;
|
||||
OverlayEntry? overlayEntry;
|
||||
Function(CallSession session)? onNewCall;
|
||||
Function(CallSession session)? onCallEnded;
|
||||
String? get localPartyId => client.deviceID;
|
||||
bool background = false;
|
||||
Client client;
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
Logs().v('AppLifecycleState = $state');
|
||||
background = !(state != AppLifecycleState.detached &&
|
||||
state != AppLifecycleState.paused);
|
||||
}
|
||||
|
||||
void addCallingOverlay(
|
||||
BuildContext context, String callId, CallSession call) {
|
||||
if (overlayEntry != null) {
|
||||
Logs().w('[VOIP] addCallingOverlay: The call session already exists?');
|
||||
overlayEntry?.remove();
|
||||
}
|
||||
/* TODO:
|
||||
final overlay = Overlay.of(context);
|
||||
overlayEntry = OverlayEntry(
|
||||
builder: (_) => Calling(
|
||||
context: famedly.context,
|
||||
client: client,
|
||||
callId: callId,
|
||||
call: call,
|
||||
onClear: () {
|
||||
overlayEntry?.remove();
|
||||
overlayEntry = null;
|
||||
}),
|
||||
);
|
||||
overlay.insert(overlayEntry);
|
||||
*/
|
||||
}
|
||||
|
||||
VoIP(this.client) : super() {
|
||||
client.onCallInvite.stream.listen(onCallInvite);
|
||||
client.onCallAnswer.stream.listen(onCallAnswer);
|
||||
|
|
@ -1168,19 +1187,19 @@ class VoIP with WidgetsBindingObserver {
|
|||
client.onSDPStreamMetadataChangedReceived.stream
|
||||
.listen(onSDPStreamMetadataChangedReceived);
|
||||
client.onAssertedIdentityReceived.stream.listen(onAssertedIdentityReceived);
|
||||
/*
|
||||
Connectivity().onConnectivityChanged.listen(_handleNetworkChanged);
|
||||
Connectivity()
|
||||
.checkConnectivity()
|
||||
.then((result) => _currentConnectivity = result)
|
||||
.catchError((e) => _currentConnectivity = ConnectivityResult.none);
|
||||
*/
|
||||
|
||||
if (!kIsWeb) {
|
||||
final wb = WidgetsBinding.instance;
|
||||
wb!.addObserver(this);
|
||||
didChangeAppLifecycleState(wb.lifecycleState!);
|
||||
}
|
||||
/* TODO: implement this in the fanedly-app.
|
||||
Connectivity().onConnectivityChanged.listen(_handleNetworkChanged);
|
||||
Connectivity()
|
||||
.checkConnectivity()
|
||||
.then((result) => _currentConnectivity = result)
|
||||
.catchError((e) => _currentConnectivity = ConnectivityResult.none);
|
||||
if (!kIsWeb) {
|
||||
final wb = WidgetsBinding.instance;
|
||||
wb!.addObserver(this);
|
||||
didChangeAppLifecycleState(wb.lifecycleState!);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
Future<void> onCallInvite(Event event) async {
|
||||
|
|
@ -1254,8 +1273,7 @@ class VoIP with WidgetsBindingObserver {
|
|||
.then((_) {
|
||||
// Popup CallingPage for incoming call.
|
||||
if (!background) {
|
||||
//TODO:
|
||||
//addCallingOverlay(famedly.context, callId, newCall);
|
||||
onNewCall?.call(newCall);
|
||||
}
|
||||
});
|
||||
currentCID = callId;
|
||||
|
|
@ -1264,8 +1282,7 @@ class VoIP with WidgetsBindingObserver {
|
|||
/// Forced to enable signaling synchronization until the end of the call.
|
||||
client.backgroundSync = true;
|
||||
|
||||
/// Handle incoming call for callkeep plugin.
|
||||
onIncomingCall?.call(newCall);
|
||||
///TODO: notify the callkeep that the call is incoming.
|
||||
}
|
||||
// Play ringtone
|
||||
playRingtone();
|
||||
|
|
@ -1352,9 +1369,7 @@ class VoIP with WidgetsBindingObserver {
|
|||
// hangup in any case, either if the other party hung up or we did on another device
|
||||
call.terminate(CallParty.kRemote,
|
||||
event.content['reason'] ?? CallErrorCode.UserHangup, true);
|
||||
|
||||
overlayEntry?.remove();
|
||||
overlayEntry = null;
|
||||
onCallEnded?.call(call);
|
||||
} else {
|
||||
Logs().v('[VOIP] onCallHangup: Session [$callId] not found!');
|
||||
}
|
||||
|
|
@ -1534,8 +1549,7 @@ class VoIP with WidgetsBindingObserver {
|
|||
currentCID = callId;
|
||||
await newCall.initOutboundCall(type).then((_) {
|
||||
if (!background) {
|
||||
//TODO:
|
||||
//addCallingOverlay(famdly.context, callId, newCall);
|
||||
onNewCall?.call(newCall);
|
||||
}
|
||||
});
|
||||
currentCID = callId;
|
||||
|
|
|
|||
|
|
@ -1,116 +0,0 @@
|
|||
import 'dart:io';
|
||||
|
||||
class MediaStreamTrack {
|
||||
bool get enabled => false;
|
||||
set enabled(bool value) {}
|
||||
String get kind => throw UnimplementedError();
|
||||
Future<void> stop() async {}
|
||||
Future<void> enableSpeakerphone(bool enable) async {}
|
||||
}
|
||||
|
||||
class MediaStream {
|
||||
String get id => throw UnimplementedError();
|
||||
List<MediaStreamTrack> getAudioTracks() => throw UnimplementedError();
|
||||
List<MediaStreamTrack> getVideoTracks() => throw UnimplementedError();
|
||||
List<MediaStreamTrack> getTracks() => throw UnimplementedError();
|
||||
Future<void> dispose() async {}
|
||||
}
|
||||
|
||||
class RTCPeerConnection {
|
||||
Function(RTCTrackEvent event)? onTrack;
|
||||
Function()? onRenegotiationNeeded;
|
||||
Function(RTCIceCandidate)? onIceCandidate;
|
||||
Function(dynamic state)? onIceGatheringState;
|
||||
Function(dynamic state)? onIceConnectionState;
|
||||
|
||||
Future<RTCSessionDescription> createOffer(Map<String, dynamic> constraints) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<RTCSessionDescription> createAnswer(Map<String, dynamic> constraints) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<void> setRemoteDescription(RTCSessionDescription description) async {}
|
||||
Future<void> setLocalDescription(RTCSessionDescription description) async {}
|
||||
|
||||
Future<RTCRtpSender> addTrack(
|
||||
MediaStreamTrack track, MediaStream stream) async {
|
||||
return RTCRtpSender();
|
||||
}
|
||||
|
||||
Future<void> removeTrack(RTCRtpSender sender) async {}
|
||||
|
||||
Future<void> close() async {}
|
||||
|
||||
Future<void> dispose() async {}
|
||||
|
||||
Future<void> addIceCandidate(RTCIceCandidate candidate) async {}
|
||||
|
||||
Future<void> addStream(MediaStream stream) async {}
|
||||
|
||||
Future<void> removeStream(MediaStream stream) async {}
|
||||
|
||||
Future<List<dynamic>> getTransceivers() async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<List<RTCRtpSender>> getSenders() async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<void> addCandidate(RTCIceCandidate candidate) async {}
|
||||
|
||||
dynamic get signalingState => throw UnimplementedError();
|
||||
}
|
||||
|
||||
class RTCIceCandidate {
|
||||
String get candidate => throw UnimplementedError();
|
||||
String get sdpMid => throw UnimplementedError();
|
||||
int get sdpMLineIndex => throw UnimplementedError();
|
||||
Map<String, dynamic> toMap() => throw UnimplementedError();
|
||||
RTCIceCandidate(String candidate, String sdpMid, int sdpMLineIndex);
|
||||
}
|
||||
|
||||
class RTCRtpSender {
|
||||
MediaStreamTrack? get track => throw UnimplementedError();
|
||||
DtmfSender get dtmfSender => throw UnimplementedError();
|
||||
}
|
||||
|
||||
class RTCSessionDescription {
|
||||
late String type;
|
||||
late String sdp;
|
||||
RTCSessionDescription(this.sdp, this.type);
|
||||
}
|
||||
|
||||
class RTCTrackEvent {
|
||||
late List<MediaStream> streams;
|
||||
}
|
||||
|
||||
enum TransceiverDirection {
|
||||
SendRecv,
|
||||
SendOnly,
|
||||
RecvOnly,
|
||||
Inactive,
|
||||
}
|
||||
|
||||
enum RTCSignalingState { RTCSignalingStateStable }
|
||||
|
||||
class RTCVideoRenderer {}
|
||||
|
||||
const kIsWeb = false;
|
||||
|
||||
bool get kIsMobile => !kIsWeb && (Platform.isAndroid || Platform.isIOS);
|
||||
|
||||
class Helper {
|
||||
static Future<void> switchCamera(MediaStreamTrack track) async {}
|
||||
}
|
||||
|
||||
class DtmfSender {
|
||||
Future<void> insertDTMF(String tones) async {}
|
||||
}
|
||||
|
||||
Future<MediaStream> createPeerConnection(
|
||||
Map<String, dynamic> constraints) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ dependencies:
|
|||
slugify: ^2.0.0
|
||||
html: ^0.15.0
|
||||
collection: ^1.15.0
|
||||
webrtc_interface: ^1.0.0
|
||||
sdp_transform: ^0.3.2
|
||||
|
||||
dev_dependencies:
|
||||
|
|
|
|||
Loading…
Reference in New Issue