Skip to content

Commit

Permalink
[1]. config-> add default TURN servers, [2]. exchange ice candidate a…
Browse files Browse the repository at this point in the history
…fter 2 seconds after sdp offer anser
  • Loading branch information
Mamena2020 committed Dec 2, 2022
1 parent 2196370 commit a783abf
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 80 deletions.
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ Online meeting app like google meet, build with flutter for all platforms.
this app uses <a href="https://webrtc.org">WebRTC</a> for media real-time communication, and <a href="https://socket.io">socket.io</a> for signaling & messaging.
<a href="https://github.com/Mamena2020/zomie-server"> Server<a> running on nodejs with SFU architecture which features the following data transmission processes between the media server and the endpoints (client).

This app also using <a href="https://github.com/Mamena2020/zomie-turn-server">Zomie TURN Server </a> as relays media,
<a href="https://github.com/Mamena2020/zomie-turn-server"> TURN Server </a> work as a backup if STUN Server won't work because client device behind of symmetric NAT
This app also using TURN Server as relays media, work as a backup plan if STUN Server won't work because client device behind of symmetric NAT. TURN server is already end-to-end encrypted by the peers and the TURN Server cannot decode/read the encrypted packet, it just relays the packet to other peers. By default TURN already setup in file (lib/Services/WebRTC/Config/WRTCConfig.dart)
using free TURN Servers from <a href="https://www.metered.ca/tools/openrelay/">OPEN RELAY</a>.
but you can add your own Turn server using <a href="https://github.com/Mamena2020/zomie-turn-server">Zomie TURN Server </a> as relays media.


- Features
Expand Down Expand Up @@ -62,14 +63,14 @@ This app also using <a href="https://github.com/Mamena2020/zomie-turn-server">Zo
#How to use
- create dotenv file
- cmd: cp dotenv.example dotenv
- fill in credentials
- fill in credentials, you can ignore (TURN_SERVER_HOST, TURN_SERVER_USERNAME, TURN_SERVER_PASSWORD), because by default its already setup, but its okay if you want to add more your own TURN server.
```
MEDIA_SERVER_HOST = "localhost:5000"
ALLOW_TURN_SERVER = "true"
ALLOW_TURN_SERVER = "false"
TURN_SERVER_HOST = "turn:ip:port" #example: "turn:192.168.1.9:3478"
TURN_SERVER_USERNAME = "zomie"
TURN_SERVER_PASSWORD = "password"
TURN_SERVER_USERNAME = "" #exampe: "zomie"
TURN_SERVER_PASSWORD = "" #example: "password"
```
Expand Down Expand Up @@ -116,7 +117,8 @@ This app also using <a href="https://github.com/Mamena2020/zomie-turn-server">Zo
- All Platform
- STUNT/TURN server
- STUNT: "urls": "stun:stun.stunprotocol.org"
- TURN: <a href="https://github.com/Mamena2020/zomie-turn-server">Zomie TURN Server </a>
- Stunt will not working if client is under symmetric NAT.
- TURN: by default TURN server using from <a href="https://www.metered.ca/tools/openrelay">Open Relay</a>, or you can add more using <a href="https://github.com/Mamena2020/zomie-turn-server">Zomie TURN Server </a>
- Socket io
Expand Down
8 changes: 4 additions & 4 deletions dotenv.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
MEDIA_SERVER_HOST = "http://localhost:5000"


ALLOW_TURN_SERVER = "true" # "true" or "false"
TURN_SERVER_HOST = "turn:ip:port" #example: "turn:192.168.1.9:3478"
TURN_SERVER_USERNAME = "zomie"
TURN_SERVER_PASSWORD = "password"
ALLOW_TURN_SERVER = "false"
TURN_SERVER_HOST = "" #example: "turn:192.168.1.9:3478"
TURN_SERVER_USERNAME = "" #exampe: "zomie"
TURN_SERVER_PASSWORD = "" #example: "password"
2 changes: 1 addition & 1 deletion lib/Services/WebRTC/Audio/WRTCAudio.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class WRTCAudio {
// _player.play(DeviceFileSource('assets/assets/audio/notif_1.wav'));
} else {
// _player.play(DeviceFileSource('asset/audio/notif_1.wav'));
_player.play(DeviceFileSource('asset/audio/' + audioName));
_player.play(DeviceFileSource('audio/' + audioName));
}
} catch (e) {
print(e);
Expand Down
106 changes: 51 additions & 55 deletions lib/Services/WebRTC/Config/WRTCConfig.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,66 @@ import 'package:flutter_dotenv/flutter_dotenv.dart';
class WRTCCOnfig {
static String host = dotenv.get("MEDIA_SERVER_HOST", fallback: "");

// static const configurationPeerConnection = {
// "sdpSemantics": "unified-plan", // Add this line for support windows
// "iceServers": [
// {
// "urls": "stun:stun.stunprotocol.org"
// // urls: "stun:stun.l.google.com:19302?transport=tcp"
// },
// // {
// // // "urls": "stun:stun.stunprotocol.org"
// // "urls": "stun:stun.l.google.com:19302?transport=tcp"
// // }
// ]
// };

static Map<String, dynamic> configurationPeerConnection() {
// Map<String, String> stun2 = {"urls": "stun:stun.l.google.com:19302"};

var iceServers = [
// //---------------------------- open relay
{"urls": "stun:stun.stunprotocol.org"},
{
"urls": "stun:openrelay.metered.ca:80",
"username": "openrelayproject",
"credential": "openrelayproject",
},
{
"urls": "turn:openrelay.metered.ca:80",
"username": "openrelayproject",
"credential": "openrelayproject",
},
{
"urls": "turn:openrelay.metered.ca:443",
"username": "openrelayproject",
"credential": "openrelayproject",
},
{
"urls": "turn:openrelay.metered.ca:80?transport=tcp",
"username": "openrelayproject",
"credential": "openrelayproject",
},
{
"urls": "turn:openrelay.metered.ca:443?transport=tcp",
"username": "openrelayproject",
"credential": "openrelayproject",
},
{
"urls": "turns:openrelay.metered.ca:443",
"username": "openrelayproject",
"credential": "openrelayproject",
},
];

String dotenvAllowTurnServer =
dotenv.get("ALLOW_TURN_SERVER", fallback: "false");
bool allowTurnServer =
dotenv.get("ALLOW_TURN_SERVER", fallback: "false") == "true"
dotenvAllowTurnServer == "true" || dotenvAllowTurnServer == true
? true
: false;
Map<String, String> stun = {"urls": "stun:stun.stunprotocol.org"};

if (allowTurnServer) {
print("using turn server");
String turnServerHost = dotenv.get("TURN_SERVER_HOST", fallback: "");
String turnServerUsername =
dotenv.get("TURN_SERVER_USERNAME", fallback: "");
String turnServerPassword =
dotenv.get("TURN_SERVER_PASSWORD", fallback: "");
print(turnServerHost);
print(turnServerUsername);
print(turnServerPassword);

return {
"sdpSemantics": "unified-plan",
'iceServers': [
stun,
{
'url': turnServerHost,
'username': turnServerUsername,
'credential': turnServerPassword
},
]
String turnServerHost = dotenv.get("TURN_SERVER_HOST");
String turnServerUsername = dotenv.get("TURN_SERVER_USERNAME");
String turnServerPassword = dotenv.get("TURN_SERVER_PASSWORD");
if (turnServerHost != "" && turnServerHost.length > 3 && allowTurnServer) {
print("using turn server costume");
var turn = {
'urls': turnServerHost,
'username': turnServerUsername,
'credential': turnServerPassword
};
iceServers.add(turn);
}
return {
"sdpSemantics": "unified-plan",
'iceServers': [stun]
};
return {"sdpSemantics": "unified-plan", 'iceServers': iceServers};
}

// static const configurationPeerConnection = {
// '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'
// },
// */
// ]
// };

static const offerSdpConstraints = {
"mandatory": {
"OfferToReceiveAudio": true,
Expand Down
53 changes: 41 additions & 12 deletions lib/Services/WebRTC/RTCConnection/WRTCProducer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class WRTCProducer {
this.videoRenderer.initialize();
}

List<Candidate> localCandidates = [];
List<RTCIceCandidate> remoteCandidates = [];

Future<void> MuteUnMute() async {
if (this.stream != null) {
this.stream!.getAudioTracks()[0].enabled =
Expand Down Expand Up @@ -103,8 +106,6 @@ class WRTCProducer {
}
}

List<Candidate> candidates = [];

bool firstConnect = false;

GetUserMedia() async {
Expand All @@ -131,7 +132,8 @@ class WRTCProducer {
* create peer connection to server
*/
Future<void> CreateConnection() async {
candidates.clear();
localCandidates.clear();
remoteCandidates.clear();
if (this.stream == null) {
if (this.callType == CallType.screenSharing) {
await GetDisplayMedia();
Expand All @@ -158,6 +160,7 @@ class WRTCProducer {

await _setTrack();

_onIceCandidate();
//----------------- process handshake
// renegitiaion needed allways call wen setTrack it trigger to addtrack
await _sdpProccess();
Expand All @@ -169,8 +172,6 @@ class WRTCProducer {
// };
onStopLocalStream();

_onIceCandidate();

this.peer!.onIceConnectionState = (e) {
try {
if (this.peer != null) {
Expand Down Expand Up @@ -247,14 +248,13 @@ class WRTCProducer {
await WRTCUtils.SetRemoteDescriptionFromJson(
peer: peer!, sdpRemote: body["data"]["sdp"]);
print("p-@@@ success set remote producer");

ExchangeIceCandidate();
await WRTCUtils.setBitrate(
peer: this.peer!,
bitrate: this.producerType == ProducerType.user
? this.room.video_bitrate
: this.room.screen_bitrate);

await _AddCandidatesToServer();
WRTCSocketFunction.NotifyServer(
type: NotifyType.join,
producer_id: this.producer.id,
Expand All @@ -266,6 +266,16 @@ class WRTCProducer {
// }
}

ExchangeIceCandidate() async {
Future.delayed(
Duration(
seconds: 3,
), () async {
await _AddRemoteCandidateToLocal();
_SendLocalCandidatesToServer();
});
}

// onTrack() {
// try {
// // this.peer!.onTrack = (e) {
Expand Down Expand Up @@ -328,22 +338,41 @@ class WRTCProducer {
_onIceCandidate() {
this.peer!.onIceCandidate = (e) {
if (e.candidate != null) {
print("p-fire candidate to stored in candidates");
candidates.add(Candidate(
print("p- add local candidate");
print(e.candidate);
localCandidates.add(Candidate(
candidate: e.candidate!,
sdpMid: e.sdpMid!,
sdpMLineIndex: e.sdpMLineIndex!));
}
};
}

_AddCandidatesToServer() async {
for (var c in this.candidates) {
_SendLocalCandidatesToServer() async {
print("p=> SEND ALL CANDIDATE TO SERVER:" +
localCandidates.length.toString());
for (var c in this.localCandidates) {
print("p-add candidate to server");
await WRTCSocketFunction.addCandidateToServer(
producer_id: this.producer.id, candidate: c);
this.candidates.clear();
}
this.localCandidates.clear();
}

//1
storeRemoteCandidateFromServer(RTCIceCandidate c) async {
remoteCandidates.add(c);
}

//2
_AddRemoteCandidateToLocal() async {
print("p=> ADD ALL CANDIDATE TO LOCAL: " +
remoteCandidates.length.toString());
for (var c in this.remoteCandidates) {
print("p-add candidate to local");
this.peer!.addCandidate(c);
}
this.remoteCandidates.clear();
}

bool _isMinimizeMedia = false;
Expand Down
7 changes: 6 additions & 1 deletion lib/Services/WebRTC/Signaling/WRTCSocketEvent.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ class WRTCSocketEvent {
WRTCService.instance().wrtcProducer!.peer != null &&
WRTCService.instance().wrtcProducer!.producer.id ==
data["producer_id"]) {
WRTCService.instance().wrtcProducer!.peer!.addCandidate(_candidate);
print("<<<<<~ incoming candidate from server");
print(data["candidate"]);
print("<<<<<| incoming candidate from server");
WRTCService.instance()
.wrtcProducer!
.storeRemoteCandidateFromServer(_candidate);
}
if (data["type"] == "screen" &&
WRTCService.instance().wrtcShareScreen != null &&
Expand Down

0 comments on commit a783abf

Please sign in to comment.