You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
510 lines
16 KiB
Dart
510 lines
16 KiB
Dart
// import 'dart:convert';
|
|
// import 'dart:async';
|
|
// import 'package:flutter/cupertino.dart';
|
|
// import 'package:flutter_webrtc/flutter_webrtc.dart';
|
|
//
|
|
// import '../utils/websocket.dart' if (dart.library.js) '../utils/websocket_web.dart';
|
|
// import '../utils/turn.dart' if (dart.library.js) '../utils/turn_web.dart';
|
|
//
|
|
//
|
|
// enum SignalingState { Open, Closed, Error }
|
|
// enum CallState { Calling, Ringing, Invite, Connected, Bye }
|
|
//
|
|
//
|
|
// const JsonEncoder _encoder = JsonEncoder();
|
|
// const JsonDecoder _decoder = JsonDecoder();
|
|
//
|
|
// class SessionOneToOne {
|
|
// late String id;
|
|
// late SocketUser local_user;
|
|
// late SocketUser remote_user;
|
|
// SessionOneToOne({required this.id, required this.local_user, required this.remote_user});
|
|
//
|
|
//
|
|
// late RTCPeerConnection pc;
|
|
// late RTCDataChannel dc;
|
|
// List<RTCIceCandidate> remoteCandidates = [];
|
|
// }
|
|
//
|
|
// class SocketUser{
|
|
// String? id;
|
|
// String? name;
|
|
// String? userAgent;
|
|
// Map? moreInfo;
|
|
//
|
|
// SocketUser({required this.id, required this.name, required this.userAgent, required this.moreInfo});
|
|
//
|
|
// SocketUser.from(dynamic json){
|
|
// id = json['id'];
|
|
// name = json['name'];
|
|
// userAgent = json['user_agent'];
|
|
// moreInfo = json['more_info'];
|
|
// }
|
|
//
|
|
// Map toJson() => {
|
|
// "id": id,
|
|
// "name": name,
|
|
// "user_agent": userAgent,
|
|
// "more_info": moreInfo
|
|
// };
|
|
// }
|
|
//
|
|
// class Signaling {
|
|
// var _host;
|
|
// var _turnCredential;
|
|
//
|
|
// SimpleWebSocket? _socket;
|
|
// SessionOneToOne? session;
|
|
//
|
|
// Signaling(this._host, {required this.session});
|
|
//
|
|
// late MediaStream? localStream;
|
|
// late final List<MediaStream> remoteStreams = <MediaStream>[];
|
|
//
|
|
// late Function(SignalingState state) onSignalingStateChange;
|
|
// late Function(SessionOneToOne session, CallState state) onCallStateChange;
|
|
// late Function(MediaStream stream) onLocalStream;
|
|
// late Function(SessionOneToOne session, MediaStream stream) onAddRemoteStream;
|
|
// late Function(SessionOneToOne session, MediaStream stream) onRemoveRemoteStream;
|
|
// late Function(dynamic event) onPeersUpdate;
|
|
// late Function(dynamic event) onConnected;
|
|
// late Function(dynamic event) onRemoteConnected;
|
|
// late Function(SessionOneToOne session, RTCDataChannel dc, RTCDataChannelMessage data) onDataChannelMessage;
|
|
// late Function(SessionOneToOne session, RTCDataChannel dc) onDataChannel;
|
|
//
|
|
// String? get sdpSemantics => WebRTC.platformIsWindows ? 'plan-b' : 'unified-plan';
|
|
//
|
|
// Map<String, dynamic> _iceServers = {
|
|
// 'iceServers': [
|
|
// {'url': 'stun:stun.l.google.com:19302'},
|
|
// /*
|
|
// * turn server configuration example.
|
|
// {
|
|
// 'url': 'turn:123.45.67.89:3478',
|
|
// 'username': 'change_to_real_user',
|
|
// 'credential': 'change_to_real_secret'
|
|
// },
|
|
// */
|
|
// ]
|
|
// };
|
|
//
|
|
// final Map<String, dynamic> _config = {
|
|
// 'mandatory': {},
|
|
// 'optional': [
|
|
// {'DtlsSrtpKeyAgreement': true},
|
|
// ]
|
|
// };
|
|
//
|
|
// final Map<String, dynamic> _dcConstraints = {
|
|
// 'mandatory': {
|
|
// 'OfferToReceiveAudio': false,
|
|
// 'OfferToReceiveVideo': false,
|
|
// },
|
|
// 'optional': [],
|
|
// };
|
|
//
|
|
//
|
|
// close() async {
|
|
// await finishSessions();
|
|
// _socket?.close();
|
|
// }
|
|
//
|
|
// void switchCamera() {
|
|
// if (localStream != null) {
|
|
// Helper.switchCamera(localStream! .getVideoTracks()[0]);
|
|
// }
|
|
// }
|
|
//
|
|
// void muteMic() {
|
|
// if (localStream != null) {
|
|
// bool enabled = localStream!.getAudioTracks()[0].enabled;
|
|
// localStream!.getAudioTracks()[0].enabled = !enabled;
|
|
// }
|
|
// }
|
|
//
|
|
//
|
|
// callAccepted(SessionOneToOne session){
|
|
// _send('call_accepted', {
|
|
// 'to': session.remote_user?.id,
|
|
// 'from': session.local_user.id,
|
|
// 'session_id': session.id,
|
|
// });
|
|
// }
|
|
//
|
|
// void offer(String media, bool useScreen) async {
|
|
// if(session == null)
|
|
// return;
|
|
//
|
|
// if (media == 'data') {
|
|
// _createDataChannel(session!);
|
|
// }
|
|
// _createOffer(session!, media);
|
|
// onCallStateChange?.call(session!, CallState.Calling);
|
|
// }
|
|
//
|
|
// void bye(SessionOneToOne session) {
|
|
// _send('bye', {
|
|
// 'session_id': session.id,
|
|
// 'from': session.local_user.id,
|
|
// });
|
|
// _closeSession(session);
|
|
// }
|
|
//
|
|
// void onMessage(message) async {
|
|
// Map<String, dynamic> mapData = message;
|
|
// var data = mapData['data'];
|
|
//
|
|
// switch (mapData['type']) {
|
|
// case 'call_accepted':
|
|
// {
|
|
// onRemoteConnected?.call(data);
|
|
// }
|
|
// break;
|
|
//
|
|
// case 'connected':
|
|
// {
|
|
// if (onConnected != null) {
|
|
// onConnected?.call(data);
|
|
// }
|
|
// }
|
|
// break;
|
|
//
|
|
// case 'offer':
|
|
// {
|
|
//
|
|
// var peerId = data['from'];
|
|
// var description = data['description'];
|
|
// var media = data['media'];
|
|
//
|
|
// await _initPeerConnection(session!, media: media, screenSharing: false);
|
|
// await session!.pc?.setRemoteDescription(RTCSessionDescription(description['sdp'], description['type']));
|
|
// await _createAnswer(session!, media);
|
|
//
|
|
// if (session!.remoteCandidates!.isNotEmpty) {
|
|
// session!.remoteCandidates!.forEach((candidate) async {
|
|
// await session!.pc?.addCandidate(candidate);
|
|
// });
|
|
// session!.remoteCandidates.clear();
|
|
// }
|
|
// onCallStateChange?.call(session!, CallState.Calling);
|
|
// }
|
|
// break;
|
|
// case 'answer':
|
|
// {
|
|
//
|
|
// var description = data['description'];
|
|
// var sessionId = data['session_id'];
|
|
// session!.pc?.setRemoteDescription(
|
|
// RTCSessionDescription(description['sdp'], description['type']));
|
|
// }
|
|
// break;
|
|
// case 'candidate':
|
|
// {
|
|
//
|
|
// var peerId = data['from'];
|
|
// var candidateMap = data['candidate'];
|
|
// var sessionId = data['session_id'];
|
|
// RTCIceCandidate candidate = RTCIceCandidate(candidateMap['candidate'],
|
|
// candidateMap['sdpMid'], candidateMap['sdpMLineIndex']);
|
|
//
|
|
// if (session != null) {
|
|
// if (session!.pc != null) {
|
|
// await session!.pc?.addCandidate(candidate);
|
|
// } else {
|
|
// session!.remoteCandidates.add(candidate);
|
|
// }
|
|
// } else {
|
|
// // _sessions[sessionId] = SessionOneToOne(pid: peerId, sid: sessionId)
|
|
// // ..remoteCandidates.add(candidate);
|
|
// }
|
|
// }
|
|
// break;
|
|
// case 'leave':
|
|
// {
|
|
// var peerId = data as String;
|
|
// _closeSessionById(peerId);
|
|
// }
|
|
// break;
|
|
// case 'bye':
|
|
// {
|
|
//
|
|
// var sessionId = data['session_id'];
|
|
// print('bye: ' + sessionId);
|
|
// if (session != null) {
|
|
// onCallStateChange?.call(session!, CallState.Bye);
|
|
// _closeSession(session!);
|
|
// }
|
|
// }
|
|
// break;
|
|
// case 'keepalive':
|
|
// {
|
|
// print('keepalive response!');
|
|
// }
|
|
// break;
|
|
// default:
|
|
// break;
|
|
// }
|
|
// }
|
|
//
|
|
// Future<void> connect() async {
|
|
// _socket = SimpleWebSocket('$_host/ws');
|
|
//
|
|
// if (_turnCredential == null) {
|
|
// try {
|
|
// _turnCredential = await getTurnCredential(_host);
|
|
// /*
|
|
// {
|
|
// "username": "1584195784:mbzrxpgjys",
|
|
// "password": "isyl6FF6nqMTB9/ig5MrMRUXqZg",
|
|
// "ttl": 86400,
|
|
// "uris": ["turn:127.0.0.1:19302?transport=udp"]
|
|
// }
|
|
// */
|
|
// _iceServers = {
|
|
// 'iceServers': [
|
|
// {
|
|
// 'urls': _turnCredential['uris'][0],
|
|
// 'username': _turnCredential['username'],
|
|
// 'credential': _turnCredential['password']
|
|
// },
|
|
// ]
|
|
// };
|
|
// } catch (e) {}
|
|
// }
|
|
//
|
|
// _socket?.onOpen = () {
|
|
// print('onOpen');
|
|
// onSignalingStateChange?.call(SignalingState.Open);
|
|
// _send('connect', session!.local_user!.toJson());
|
|
// };
|
|
//
|
|
// _socket?.onMessage = (message) {
|
|
// print('Received data: ' + message);
|
|
// onMessage(_decoder.convert(message));
|
|
// };
|
|
//
|
|
// _socket?.onClose = (int code, String reason) {
|
|
// print('Closed by server [$code => $reason]!');
|
|
// onSignalingStateChange?.call(SignalingState.Closed);
|
|
// };
|
|
//
|
|
// await _socket?.connect();
|
|
// }
|
|
//
|
|
// Future<MediaStream> createStream(String media, bool userScreen) async {
|
|
// final Map<String, dynamic> mediaConstraints = {
|
|
// 'audio': userScreen ? false : true,
|
|
// 'video': userScreen
|
|
// ? true
|
|
// : {
|
|
// 'mandatory': {
|
|
// 'minWidth': '640', // Provide your own width, height and frame rate here
|
|
// 'minHeight': '480',
|
|
// 'minFrameRate': '30',
|
|
// },
|
|
// 'facingMode': 'user',
|
|
// 'optional': [],
|
|
// }
|
|
// };
|
|
//
|
|
// MediaStream stream = userScreen
|
|
// ? await navigator.mediaDevices.getDisplayMedia(mediaConstraints)
|
|
// : await navigator.mediaDevices.getUserMedia(mediaConstraints);
|
|
// onLocalStream?.call(stream);
|
|
// return stream;
|
|
// }
|
|
//
|
|
// Future<SessionOneToOne> _initPeerConnection(SessionOneToOne session, {required String media, required bool screenSharing}) async {
|
|
//
|
|
// if (media != 'data')
|
|
// localStream = await createStream(media, screenSharing);
|
|
// print(_iceServers);
|
|
// RTCPeerConnection pc = await createPeerConnection({
|
|
// ..._iceServers,
|
|
// ...{'sdpSemantics': sdpSemantics}
|
|
// }, _config);
|
|
// if (media != 'data') {
|
|
// switch (sdpSemantics) {
|
|
// case 'plan-b':
|
|
// pc.onAddStream = (MediaStream stream) {
|
|
// onAddRemoteStream?.call(session, stream);
|
|
// remoteStreams.add(stream);
|
|
// };
|
|
// await pc.addStream(localStream!);
|
|
// break;
|
|
// case 'unified-plan':
|
|
// // Unified-Plan
|
|
// pc.onTrack = (event) {
|
|
// if (event.track.kind == 'video') {
|
|
// onAddRemoteStream?.call(session, event.streams[0]);
|
|
// }
|
|
// };
|
|
// localStream! .getTracks().forEach((track) {
|
|
// pc.addTrack(track, localStream!);
|
|
// });
|
|
// break;
|
|
// }
|
|
//
|
|
// // Unified-Plan: Simuclast
|
|
// /*
|
|
// await pc.addTransceiver(
|
|
// track: _localStream.getAudioTracks()[0],
|
|
// init: RTCRtpTransceiverInit(
|
|
// direction: TransceiverDirection.SendOnly, streams: [_localStream]),
|
|
// );
|
|
//
|
|
// await pc.addTransceiver(
|
|
// track: _localStream.getVideoTracks()[0],
|
|
// init: RTCRtpTransceiverInit(
|
|
// direction: TransceiverDirection.SendOnly,
|
|
// streams: [
|
|
// _localStream
|
|
// ],
|
|
// sendEncodings: [
|
|
// RTCRtpEncoding(rid: 'f', active: true),
|
|
// RTCRtpEncoding(
|
|
// rid: 'h',
|
|
// active: true,
|
|
// scaleResolutionDownBy: 2.0,
|
|
// maxBitrate: 150000,
|
|
// ),
|
|
// RTCRtpEncoding(
|
|
// rid: 'q',
|
|
// active: true,
|
|
// scaleResolutionDownBy: 4.0,
|
|
// maxBitrate: 100000,
|
|
// ),
|
|
// ]),
|
|
// );*/
|
|
// /*
|
|
// var sender = pc.getSenders().find(s => s.track.kind == "video");
|
|
// var parameters = sender.getParameters();
|
|
// if(!parameters)
|
|
// parameters = {};
|
|
// parameters.encodings = [
|
|
// { rid: "h", active: true, maxBitrate: 900000 },
|
|
// { rid: "m", active: true, maxBitrate: 300000, scaleResolutionDownBy: 2 },
|
|
// { rid: "l", active: true, maxBitrate: 100000, scaleResolutionDownBy: 4 }
|
|
// ];
|
|
// sender.setParameters(parameters);
|
|
// */
|
|
// }
|
|
// pc.onIceCandidate = (candidate) async {
|
|
// if (candidate == null) {
|
|
// print('onIceCandidate: complete!');
|
|
// return;
|
|
// }
|
|
// // This delay is needed to allow enough time to try an ICE candidate
|
|
// // before skipping to the next one. 1 second is just an heuristic value
|
|
// // and should be thoroughly tested in your own environment.
|
|
// await Future.delayed(
|
|
// const Duration(seconds: 1),
|
|
// () => _send('candidate', {
|
|
// 'to': session.remote_user?.id,
|
|
// 'from': session.local_user.id,
|
|
// 'candidate': {
|
|
// 'sdpMLineIndex': candidate.sdpMLineIndex, // sdpMlineIndex or sdpMLineIndex
|
|
// 'sdpMid': candidate.sdpMid,
|
|
// 'candidate': candidate.candidate,
|
|
// },
|
|
// 'session_id': session.id,
|
|
// }));
|
|
// };
|
|
//
|
|
// pc.onIceConnectionState = (state) {};
|
|
//
|
|
// pc.onRemoveStream = (stream) {
|
|
// onRemoveRemoteStream?.call(session, stream);
|
|
// remoteStreams.removeWhere((it) {
|
|
// return (it.id == stream.id);
|
|
// });
|
|
// };
|
|
//
|
|
// pc.onDataChannel = (channel) {
|
|
// _addDataChannel(session, channel);
|
|
// };
|
|
//
|
|
// session.pc = pc;
|
|
// return session;
|
|
// }
|
|
//
|
|
// void _addDataChannel(SessionOneToOne session, RTCDataChannel channel) {
|
|
// channel.onDataChannelState = (e) {};
|
|
// channel.onMessage = (RTCDataChannelMessage data) {
|
|
// onDataChannelMessage?.call(session, channel, data);
|
|
// };
|
|
// session.dc = channel;
|
|
// onDataChannel?.call(session, channel);
|
|
// }
|
|
//
|
|
// Future<void> _createDataChannel(SessionOneToOne session, {label= 'fileTransfer'}) async {
|
|
// RTCDataChannelInit dataChannelDict = RTCDataChannelInit()
|
|
// ..maxRetransmits = 30;
|
|
// RTCDataChannel channel =
|
|
// await session.pc .createDataChannel(label, dataChannelDict);
|
|
// _addDataChannel(session, channel);
|
|
// }
|
|
//
|
|
// Future<void> _createOffer(SessionOneToOne session, String media) async {
|
|
// try {
|
|
// RTCSessionDescription s =
|
|
// await session.pc .createOffer(media == 'data' ? _dcConstraints : {});
|
|
// await session.pc .setLocalDescription(s);
|
|
// _send('offer', {
|
|
// 'to': session.remote_user?.id,
|
|
// 'from': session.local_user.id,
|
|
// 'description': {'sdp': s.sdp, 'type': s.type},
|
|
// 'session_id': session.id,
|
|
// 'media': media,
|
|
// });
|
|
// } catch (e) {
|
|
// print(e.toString());
|
|
// }
|
|
// }
|
|
//
|
|
// Future<void> _createAnswer(SessionOneToOne session, String media) async {
|
|
// try {
|
|
// RTCSessionDescription s =
|
|
// await session.pc .createAnswer(media == 'data' ? _dcConstraints : {});
|
|
// await session.pc .setLocalDescription(s);
|
|
// _send('answer', {
|
|
// 'to': session.remote_user?.id,
|
|
// 'from': session.local_user.id,
|
|
// 'description': {'sdp': s.sdp, 'type': s.type},
|
|
// 'session_id': session.id,
|
|
// });
|
|
// } catch (e) {
|
|
// print(e.toString());
|
|
// }
|
|
// }
|
|
//
|
|
// _send(event, data) {
|
|
// var request = Map();
|
|
// request["type"] = event;
|
|
// request["data"] = data;
|
|
// _socket?.send(_encoder.convert(request));
|
|
// }
|
|
//
|
|
// Future<void> finishSessions() async {
|
|
// _closeSessionById(session!.id);
|
|
// }
|
|
//
|
|
// void _closeSessionById(String sessionId) {
|
|
// if (session != null && session!.id == sessionId) {
|
|
// _closeSession(session!);
|
|
// onCallStateChange?.call(session!, CallState.Bye);
|
|
// }
|
|
// }
|
|
//
|
|
// Future<void> _closeSession(SessionOneToOne session) async {
|
|
// localStream?.getTracks()?.forEach((element) async {
|
|
// await element.stop();
|
|
// });
|
|
// await localStream?.dispose();
|
|
// localStream = null;
|
|
//
|
|
// await session.pc?.close();
|
|
// await session.dc?.close();
|
|
// }
|
|
// }
|