I have taken over a WebRTC project from someone and though I'm starting to wrap my head around the concepts, I continue to be stumped by a specific problem: getting the WebRTC connection to move from new to checking/completed, etc...
Here is the extent of the output from chrome://webrtc-internals:
Our code calls connect():
connect(mediaStream, interviewUid, applicantUid) {
return new Promise((resolve, reject) => {
this.connectRtcPeerConnection()
.then(() => {
this.connectionState = Socket.CONNECTION_STATES.connected;
this.rtcPeer.addStream(mediaStream);
return this.rtcPeer.createOffer({ offerToReceiveAudio: 1, offerToReceiveVideo: 1 });
}).then((offer) => {
console.log('offer created', offer);
return this.rtcPeer.setLocalDescription(offer);
}).then(() => {
const message = {
id: SENDABLE_MESSAGES.connect,
sdpOffer: this.rtcPeer.localDescription,
interviewUid,
applicantUid,
};
this.sendMessageToServer(message);
resolve();
})
.catch((error) => {
console.error(error);
reject();
});
});
}
which in turns calls connectRtcPeerConnection():
connectRtcPeerConnection(
) {
return new Promise((resolve, reject) => {
if (this.rtcPeer) {
resolve();
}
console.log('started connecting');
const rtcPeerOptions = {
iceServers: [TRUNCATED],
};
console.log('rtcPeerOptions', rtcPeerOptions);
this.rtcPeer = new RTCPeerConnection(rtcPeerOptions);
console.log('rtcPeer object: ', this.rtcPeer);
this.rtcPeer.onerror = reject;
this.rtcPeer.onicecandidate = (candidate) => { this.handleIceCandidateEvent(candidate); };
this.rtcPeer.oniceconnectionstatechange = () => {
this.handleIceConnectionStateChangeEvent();
};
this.rtcPeer.onaddstream = () => { console.log('handleAddStreamEvent'); };
this.rtcPeer.onremovestream = () => { console.log('handleRemoveStreamEvent'); };
this.rtcPeer.onicegatheringstatechange = () => { console.log('handleIceGatheringStateChangeEvent'); };
this.rtcPeer.onsignalingstatechange = () => { console.log('handleSignalingStateChangeEvent'); };
this.rtcPeer.onnegotiationneeded = () => { console.log('handleNegotiationNeededEvent'); };
resolve();
});
}
This chunk of code never gets executed:
this.rtcPeer.oniceconnectionstatechange = () => {
this.handleIceConnectionStateChangeEvent();
};
I've followed every conditional and code path and don't currently see what the issue might be. Has anyone encountered this and is able to shed some light on potential things to look at/consider?
Thanks!
When I was implementing Kurento library for iOS, tried something like this:
Generated SDPOffer
Set LocalDescription at our end
WebRTC started generating IceCandidate
Sent Ice Candidate through WebSocket
At this point, other party sent SDPAnswer.
Processed SDPAnswer at our end.
Set RemoteDescription at our end.
Server started sending IceCandidate gathered at their end.
Added these IceCandidate in array at our end.
Here received change in connection state to "Checking"
Received RemoteStream at this point.
Here received change in connection state to "Connected"
Hope it helps!
well, you never call setRemoteDescription or add a remote ice candidate via addIceCandidate. Without that there is no-one to talk to
Related
I am new in react js development and try to integrate WebSocket un my app.
but got an error when I send messages during connection.
my code is
const url = `${wsApi}/ws/chat/${localStorage.getItem("sID")}/${id}/`;
const ws = new WebSocket(url);
ws.onopen = (e) => {
console.log("connect");
};
ws.onmessage = (e) => {
const msgRes = JSON.parse(e.data);
setTextMessage(msgRes.type);
// if (msgRes.success === true) {
// setApiMessagesResponse(msgRes);
// }
console.log(msgRes);
};
// apiMessagesList.push(apiMessagesResponse);
// console.log("message response", apiMessagesResponse);
ws.onclose = (e) => {
console.log("disconnect");
};
ws.onerror = (e) => {
console.log("error");
};
const handleSend = () => {
console.log(message);
ws.send(message);
};
and got this error
Failed to execute 'send' on 'WebSocket': Still in CONNECTING state
Sounds like you're calling ws.send before the socket has completed the connection process. You need to wait for the open event/callback, or check the readyState per docs and queue the send after the readyState changes i.e after the open callback has fired.
Not suggesting you do this, but it might help:
const handleSend = () => {
if (ws.readyState === WebSocket.OPEN) {
ws.send()
} else {
// Queue a retry
setTimeout(() => { handleSend() }, 1000)
}
};
As Logan has mentioned my first example is lazy. I just wanted to get OP unblocked and I trusted readers were intelligent enough to understand how to take it from there. So, make sure to handle the available states appropriately, e.g if readyState is WebSocket.CONNECTING then register a listener:
const handleSend = () => {
if (ws.readyState === WebSocket.OPEN) {
ws.send()
} else if (ws.readyState == WebSocket.CONNECTING) {
// Wait for the open event, maybe do something with promises
// depending on your use case. I believe in you developer!
ws.addEventListener('open', () => handleSend())
} else {
// etc.
}
};
I guess you can only send data with ws only if it's already open, and you do not check when it's open or not.
Basically you ask for an openning but you send a message before the server said it was open (it's not instant and you do not know how many time it can take ;) )
I think you should add a variable somithing like let open = false;
and rewrite the onopen
ws.onopen = (e) => {
open = true;
console.log("connect");
};
and then in your logic you can only send a message if open is equal to true
don't forget the error handling ;)
I have an application stored in IBM server where the same application is being used by multiple people. We have a feature where we need to execute that particular piece of code exactly at same time for multiple people. How can I achieve this? The feature is that, a particular person will initiate a recording feature which will open the microphones of every person. This is achieved by maintaining a variable whose value I am setting and de setting, but when I am ending the recording the person who ends immediately the code gets executed but for other people it takes few milliseconds and even seconds to execute that same piece of code? Any help how can I tackle to this issue.
render () {
this.props.meetingData.isMeetingDiscussionStarted ? this.recordFullAudio() : null;
!this.props.meetingData.isMeetingDiscussionStarted ? this.stopRecordingAudio() : null;
}
recordFullAudio = () => {
const constraints = {
audio: {
channels: 1
},
video: false
};
getUserMedia(constraints)
.then(stream => {
this.stream = stream;
const mimeType = 'audio/mpeg';
const mime = ['audio/wav', 'audio/mpeg', 'audio/webm', 'audio/ogg']
.filter(MediaRecorder.isTypeSupported)[0];
const options = {
mimeType: 'audio/webm',
};
this.mediaRecorder = new MediaRecorder(stream);
this.mediaRecorder.addEventListener('dataavailable', event => {
audioChunks.push(event.data);
});
this.mediaRecorder.addEventListener('stop', () => { });
}).catch(error => { });
}
stopRecordingAudio = () => {
if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
this.mediaRecorder.stop();
this.stream.getTracks().forEach((track) => {
track.stop();
});
}
// Cleanup
this.mediaRecorder = null;
this.stream = null;
}
isMeetingDiscussionStarted is that variable maintained in NodeJS sharedb doc which is used to start and end recording.
I’m using Firestore real time updates to create realtime chats in my React Native app. I read this may not be the best way to build a chat, but I decided to do so cause I’m using Firebase already and the chat is not the main purpose of the app.
So, in the context of a real time chat, how would I optimize the Firestore connection?
It usually works really well but I have experienced a few problems so far:
message comes in slowly
message doesn’t show after being sent
Push notification arrives before the message
These problems usually occur when internet connection is not great (though Whatsapp messages still work fine), but sometimes also on a good connection…
Here is how I query the data (real-time listener added in componentDidMount, removed in componenWillUnmount):
onLogUpdate = (querySnapshot) => {
allMessages = this.state.allMessages
cb = (allMsgs) => {
allMsgs.sort((a,b) => {
a = new Date(a.timestamp);
b = new Date(b.timestamp);
return a>b ? -1 : a<b ? 1 : 0;
})
messages = _.takeRight(allMsgs, this.state.messageLimit)
this.setState({ loading: false, messages, allMessages: allMsgs })
}
async.map(querySnapshot._changes, (change, done) => {
if (change.type === "added") {
const msgData = change._document.data()
if (msgData.origin == 'user') {
this.usersRef.doc(msgData.byWhom).get()
.then(usr => {
msgData.user = usr.data()
done(null, msgData)
})
.catch(err => { console.log('error getting user in log:', err) })
} else {
done(null, msgData)
}
} else {
done(null, 0)
}
}, (err, results) => {
const res = results.filter(el => { return el != 0 })
allMessages = _.concat(allMessages, res)
cb(allMessages)
})
}
And this is how I add new messages:
// in utils.js
exports.addLogMessage = (msgObj, moment_id, callback) => {
logMessagesRef.add(msgObj)
.then(ref => {
momentsRef.doc(moment_id).get()
.then(doc => {
const logMsgs = doc.data().logMessages ? doc.data().logMessages : []
logMsgs.push(ref.id)
momentsRef.doc(moment_id).update({ logMessages: logMsgs })
})
.then(() => {
if (callback) {
callback()
}
})
})
.catch(err => { console.log('error sending logMessage:', err) })
}
// in chat Screen
sendLogMessage = () => {
if (this.state.newMessage.length > 0) {
firebase.analytics().logEvent('send_log_message')
this.setState({ newMessage: '' })
const msgObj = {
text: this.state.newMessage,
origin: 'user',
timestamp: Date.now(),
byWhom: this.state.user._id,
toWhichMoment: this.state.moment._id
}
addLogMessage(msgObj, this.state.moment._id)
}
}
Any suggestions would be highly appreciated :)
I was working on something similar and after i submitted the data it wouldnt show in the ListView. I solved it by pushing the new entry into the preexisting state that was being mapped, and since setState() calls the render() method, it worked just fine. I did something like this:
sendLogMessage().then(newData => {
joined = this.state.data.concat(newData);
this.setState({ data: joined})
})
This is ofcourse assuming that you're using this.state.data.map to render the chat log. This will work when the message that I send cant be seen by me and as for the messages updating as the database updates, you may wanna make use of .onDataChange callback provided by Firebase API. I hope I helped.
I am using below code in my project to open realm asynchronously and use it with the services.
RmProfile.js:
import Realm from 'realm';
const PrflSchema = {
name: 'Profile',
primaryKey: 'id',
properties: {
id : {type: 'int'},
mob : {type: 'int'},
name : {type: 'string'},
}
};
let RmPrfl;
Realm.open({
schema: [PrflSchema],
schemaVersion: 0
}).then(realm => {
RmPrfl = realm;
})
.catch(error => {
console.log('Error in Opening PrflSchema');
console.log(error);
});
let ProfileServices= {
getName: function(id) {
let PrflInfo=RmPrfl.objectForPrimaryKey('Profile', id);
if(PrflInfo){
return PrflInfo.name;
}
else{
return false;
}
}
}
module.exports = ProfileServices;
Now to use the realm services in other files i am simply trying to export
Profile.js:
import PrflSrvcs from './ProfileServices'
console.log(PrflSrvcs.getName('1253'));
here the services getting exported but the error is coming like RmPrfl is undefined. This happens as the Realm.Open() is asynchronously executed and before its execution ends, the ProfileServices is executed.
So as I am new to Realm, can anyone guide me, How to asynchronous transactions using Realm JavaScript.
Any example will be good to understand.
Thank you..
Why don't you just wrap it all in a promise e.g.
let ProfileServices = () => {
getName: function(id) {
return new Promise((resolve, reject) => {
Realm.open({
schema: [PrflSchema],
schemaVersion: 0
})
.then(realm => {
let PrflInfo = realm.objectForPrimaryKey('Profile', id);
resolve(PrflInfo || false)
})
.catch(error => {
console.log('Error in Opening PrflSchema');
console.log(error);
reject(error)
});
})
}
}
module.exports = ProfileServices
import PrflSrvcs from './ProfileServices'
PrflSrvcs.getName('1253')
.then(name => { console.log(name) });
You probably don't want to be opening the realm for every query though, you can cache it and check for the existence before running the open.
It's not clear in the documentation, but it seems Realm.open(config) caches the promised realm itself, so you can actually just use that API for every query.
# Initial Ream.open(): took 176.50000000139698 milliseconds.
# Manually Cached: took 0.5999999993946403 milliseconds.
# Second Realm.open(): took 0.19999999494757503 milliseconds.
So manually caching just creates a little extra overhead without simplifying the API for accessing the realm.
Here's the caching function I used:
private realm(): Promise<Realm> {
return new Promise((resolve, reject) => {
if (this.cachedRealm) {
resolve(this.cachedRealm)
} else {
Realm.open(this.config)
.then(realm => {
this.cachedRealm = realm
resolve(realm)
})
.catch(error => {
console.error(error);
reject(error)
});
}
})
}
How can I check if port is busy for localhost?
Is there any standard algorithm? I am thinking at making a http request to that url and check if response status code is not 404.
You could attempt to start a server, either TCP or HTTP, it doesn't matter. Then you could try to start listening on a port, and if it fails, check if the error code is EADDRINUSE.
var net = require('net');
var server = net.createServer();
server.once('error', function(err) {
if (err.code === 'EADDRINUSE') {
// port is currently in use
}
});
server.once('listening', function() {
// close the server if listening doesn't fail
server.close();
});
server.listen(/* put the port to check here */);
With the single-use event handlers, you could wrap this into an asynchronous check function.
Check out the amazing tcp-port-used node module!
//Check if a port is open
tcpPortUsed.check(port [, host])
//Wait until a port is no longer being used
tcpPortUsed.waitUntilFree(port [, retryTimeMs] [, timeOutMs])
//Wait until a port is accepting connections
tcpPortUsed.waitUntilUsed(port [, retryTimeMs] [, timeOutMs])
//and a few others!
I've used these to great effect with my gulp watch tasks for detecting when my Express server has been safely terminated and when it has spun up again.
This will accurately report whether a port is bound or not (regardless of SO_REUSEADDR and SO_REUSEPORT, as mentioned by #StevenVachon).
The portscanner NPM module will find free and used ports for you within ranges and is more useful if you're trying to find an open port to bind.
Thank to Steven Vachon link, I made a simple example:
const net = require("net");
const Socket = net.Socket;
const getNextPort = async (port) =>
{
return new Promise((resolve, reject) =>
{
const socket = new Socket();
const timeout = () =>
{
resolve(port);
socket.destroy();
};
const next = () =>
{
socket.destroy();
resolve(getNextPort(++port));
};
setTimeout(timeout, 10);
socket.on("timeout", timeout);
socket.on("connect", () => next());
socket.on("error", error =>
{
if (error.code !== "ECONNREFUSED")
reject(error);
else
resolve(port);
});
socket.connect(port, "0.0.0.0");
});
};
getNextPort(8080).then(port => {
console.log("port", port);
});
this is what im doing, i hope it help someone
const isPortOpen = async (port: number): Promise<boolean> => {
return new Promise((resolve, reject) => {
let s = net.createServer();
s.once('error', (err) => {
s.close();
if (err["code"] == "EADDRINUSE") {
resolve(false);
} else {
resolve(false); // or throw error!!
// reject(err);
}
});
s.once('listening', () => {
resolve(true);
s.close();
});
s.listen(port);
});
}
const getNextOpenPort = async(startFrom: number = 2222) => {
let openPort: number = null;
while (startFrom < 65535 || !!openPort) {
if (await isPortOpen(startFrom)) {
openPort = startFrom;
break;
}
startFrom++;
}
return openPort;
};
you can use isPortOpen if you just need to check if a port is busy or not.
and the getNextOpenPort finds next open port after startFrom. for example :
let startSearchingFrom = 1024;
let port = await getNextOpenPort(startSearchingFrom);
console.log(port);