I've been implementing a webrtc videochat.
Everything is working smoothly except for the case when the peer closes the browser.
I've been trying to handle this event by implementing an onended callback on the remote mediastream. Though, this callback does not seem to ever be called.
How can I detect that the peer's browser has been closed or that the connection was finished on the other side?
You can use the ICE connection status to determine this. If you disconnect one peer it takes some seconds (~5?) to recoginize it, but it works even without a signalling server.
(assuming you called your peer connection pc)
pc.oniceconnectionstatechange = function() {
if(pc.iceConnectionState == 'disconnected') {
console.log('Disconnected');
}
}
Use signaling gateway to send message to all connected peers that you're leaving; like this:
window.addEventListener('beforeunload', function () {
userLeft();
}, false);
window.addEventListener('keyup', function (e) {
if (e.keyCode == 116)
userLeft();
}, false);
function userLeft() {
signalingGateway.send({
userLeft: true,
whoLeft: 'user-id'
});
}
signalingGateway.on('message', function (signal) {
if (signal.userLeft && signal.whoLeft != 'current-user-id') {
var relevantPeer = listOfPeers[signal.whoLeft];
if (relevantPeer) {
relevantPeer.close();
relevantPeer = null;
}
var relevantLocalStreams = listOfLocalStreams[signal.whoLeft];
if (relevantLocalStreams.length) {
for (var i = 0; i < relevantLocalStreams.length; i++) {
if (relevantLocalStreams[i].stop) {
relevantLocalStreams[i].stop();
}
// it is suggested to stop media tracks instead!
}
}
}
});
I am using a solution like this:
const connection = new RTCPeerConnection(configuration);
connection.onconnectionstatechange = () => {
const connectionStatus = connection.connectionState;
if (["disconnected", "failed", "closed"].includes(connectionStatus)) {
console.log("disconnected");
}
};
I also had this issue. I've solved this by closing the connection at the desired moment(eg. on destructor).
A <- connected -> B
...
Close(A); // after this, B's ice status will go almost instantly to 'kDisconnected'.
Just Use
connection.onclose = function(event){
event.useid // gives the connection closed userid
}
Related
In web3-provider-ws node_module's code I find this reconnect function:
WebsocketProvider.prototype.reconnect = function () {
var _this = this;
this.reconnecting = true;
if (this.responseQueue.size > 0) {
this.responseQueue.forEach(function (request, key) {
request.callback(errors.PendingRequestsOnReconnectingError());
_this.responseQueue.delete(key);
});
} //and so on...
Here errors.PendingRequestsOnReconnectingError() will return error : "provider started to reconnect before response got received.
As i am using public RPC, it will break websocket connection after some random period of time(i dont know why) even if we have set keepAlive to true.
I am not getting use case of condition
if (this.responseQueue.size > 0)
Like why its checking for responsequeue every time on reconnect.
i tried to comment out "request.callback(errors.PendingRequestsOnReconnectingError());" and it was working proper i was getting receipt of tx, even if tx is in pending state on reconnect.i dont know how as "_this.responseQueue.delete(key)" will delete key.
here is full function of reconnect.
WebsocketProvider.prototype.reconnect = function () {
var _this = this;
this.reconnecting = true;
if (this.responseQueue.size > 0) {
this.responseQueue.forEach(function (request, key) {
request.callback(errors.PendingRequestsOnReconnectingError());
_this.responseQueue.delete(key);
});
}
if (!this.reconnectOptions.maxAttempts ||
this.reconnectAttempts < this.reconnectOptions.maxAttempts) {
setTimeout(function () {
_this.reconnectAttempts++;
_this._removeSocketListeners();
_this.emit(_this.RECONNECT, _this.reconnectAttempts);
_this.connect();
}, this.reconnectOptions.delay);
return;
}
this.emit(this.ERROR, errors.MaxAttemptsReachedOnReconnectingError());
this.reconnecting = false;
if (this.requestQueue.size > 0) {
this.requestQueue.forEach(function (request, key) {
request.callback(errors.MaxAttemptsReachedOnReconnectingError());
_this.requestQueue.delete(key);
});
}
};
I dont want to handle error, i just want to know use-case of the condition.I mean its totally RPC provider-side issue as they are breaking websocket connection. and i know that dedicated RPC-node
may solve or reduce this type of errors.
I am using a wrapper of Web Speech API for Angular6. I am trying to implement a system of starting-stopping after each 3.5s in order to be able to manipulate the results for these small parts.
Even though I stop the recognition, before starting it again, I keep getting this error Failed to execute 'start' on 'SpeechRecognition': recognition has already started.
As suggested in this post, I first verify whether the speech recognition is active or not and only if not active, I try to start it. https://stackoverflow.com/a/44226843/6904971
Here is the code:
constructor( private http: Http, private service: SpeechRecognitionService, private links: LinksService) {
var recognizing; // will get bool values to verify if recognition is active
this.service.onresult = (e) => {
this.message = e.results[0].item(0).transcript;
};
this.service.onstart = function () {
recognizing = true;
};
this.service.onaudiostart = function () {
recognizing = true;
};
this.service.onerror = function (event) {
recognizing = false;
};
this.service.onsoundstart = function () {
recognizing = true;
};
this.service.onsoundstart = function () {
recognizing = true;
};
this.record = () => {
this.service.start();
setInterval(root.ongoing_recording(), 3500);
};
var root = this;
var speech = '';
this.stop_recording = () => {
this.service.stop();
};
this.ongoing_recording = ()=> {
setTimeout(function(){
if( recognizing === true){
root.service.stop();
root.service.onend = (e) => {
recognizing = false;
speech = root.message;
var sentence = document.createElement('span');
sentence.innerHTML = speech + " ";
document.body.appendChild(sentence);
}
}
}, 3500);
setTimeout(function(){
if(recognizing === false){
root.service.start();
}
}, 3510);
};
}
start() {
this.service.start();
}
stop() {
this.service.stop();
}
record(){
this.record();
}
stop_recording(){
this.stop_recording();
}
ongoing_recording(){
this.ongoing_recording();
}
I think that the timing might not be good (with the setTimeout and interval). Any help would be much appreciated. Thank you! :)
I used Web Speech API for voice search functionality in my site and I was facing a similar sort of situation. It has one microphone icon which toggles the speech recognition on and off. It was working fine in the normal on and off of the button that started speech recognition but was breaking only if you test it rigorously with a continuous button toggle.
Solution:
The thing that worked for me is:
try{
//line of code to start the speech recognition
}
catch{
//line of code to stop the speech recognition
}
So I wrapped the .start() method which was breaking the application in a try block and then added the catch block to stop it. And even if it comes across this problem, on the next button click to turn on the speech recognition, it works. I hope you would be able to extract something from it.
one observation:
you run setInterval() every 3500 ms to invoke ongoing_recording(), but then use setTimeout() with 3500 ms again within ongoing_recording().
Besides that, maybe logging the error handler --where recognizing is also set to false-- could help finding a solution:
in past versions of the SpeechRecognition implementation, not every error did actually stop the recognition (I don't know if that is still the case).
So it might be the case, that recognizing is reset due to an error that did not actually stop the recognition; if this is really the cause of the error when restarting recognition, it could be just catched & ignored.
Also it might be worth trying to re-start the recognition in the onend handler (and onerror).
I am not sure what is the reason that is causing it in your code, but i had the same error and what caused it in my case was that I was calling start() twice in a row, so what fixed it was adding a variable to check if the recognition has started or stopped, so if it has started and I clicked it again it would return speach.stop() to avoid using start() again.
let recognition = new SpeechRecognition();
let status = 0;
document.querySelector(".mic").addEventListener("click",() => {
if (status == 1) {
status = 0;
return recognition.stop();
}
recognition.start();
status = 1;
recognition.onresult = function (event) {
status=0;
var text = event.results[0][0].transcript;
recognition.stop();
};
recognition.onspeechend = function () {
status = 0;
recognition.stop();
};
});
I gotta a companion script for a serviceworker and I'm trialling right now.
The script works like so:
((n, d) => {
if (!(n.serviceWorker && (typeof Cache !== 'undefined' && Cache.prototype.addAll))) return;
n.serviceWorker.register('/serviceworker.js', { scope: './book/' })
.then(function(reg) {
if (!n.serviceWorker.controller) return;
reg.onupdatefound = () => {
let installingWorker = reg.installing;
installingWorker.onstatechange = () => {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
updateReady(reg.waiting);
} else {
// This is the initial serviceworker…
console.log('May be skipwaiting here?');
}
break;
case 'waiting':
updateReady(reg.waiting);
break;
case 'redundant':
// Something went wrong?
console.log('[Companion] new SW could not install…')
break;
}
};
};
}).catch((err) => {
//console.log('[Companion] Something went wrong…', err);
});
function updateReady(worker) {
d.getElementById('swNotifier').classList.remove('hidden');
λ('refreshServiceWorkerButton').on('click', function(event) {
event.preventDefault();
worker.postMessage({ 'refreshServiceWorker': true } );
});
λ('cancelRefresh').on('click', function(event) {
event.preventDefault();
d.getElementById('swNotifier').classList.add('hidden');
});
}
function λ(selector) {
let self = {};
self.selector = selector;
self.element = d.getElementById(self.selector);
self.on = function(type, callback) {
self.element['on' + type] = callback;
};
return self;
}
let refreshing;
n.serviceWorker.addEventListener('controllerchange', function() {
if (refreshing) return;
window.location.reload();
refreshing = true;
});
})(navigator, document);
I'm a bit overwhelmed right now by the enormity of the service workers api and unable to "see" what one would do with reg.installing returning a redundant state?
Apologies if this seems like a dumb question but I'm new to serviceworkers.
It's kinda difficult to work out what your intent is here so I'll try and answer the question generally.
A service worker will become redundant if it fails to install or if it's superseded by a newer service worker.
What you do when this happens is up to you. What do you want to do in these cases?
Based on the definition here https://www.w3.org/TR/service-workers/#service-worker-state-attribute I am guessing just print a log in case it comes up in debugging otherwise do nothing.
You should remove any UI prompts you created that ask the user to do something in order to activate the latest service worker. And be patient a little longer.
You have 3 service workers, as you can see on the registration:
active: the one that is running
waiting: the one that was downloaded, and is ready to become active
installing: the one that we just found, being downloaded, after which it becomes waiting
When a service worker reaches #2, you may display a prompt to the user about the new version of the app being just a click away. Let's say they don't act on it.
Then you publish a new version. Your app detects the new version, and starts to download it. At this point, you have 3 service workers. The one at #2 changes to redundant. The one at #3 is not ready yet. You should remove that prompt.
Once #3 is downloaded, it takes the place of #2, and you can show that prompt again.
Write catch function to see the error. It could be SSL issue.
/* In main.js */
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js')
.then(function(registration) {
console.log("Service Worker Registered", registration);
})
.catch(function(err) {
console.log("Service Worker Failed to Register", err);
})
}
I am trying to implement CometD in our application. But it is taking more time compared to the existing implementation in our project. The existing system is taking time in milliseconds where as CometD is taking 2 seconds to push the message.
I am not sure where I am going wrong. Any guidance will help me lot.
My code:
Java script at client side
(function($)
{
var cometd = $.cometd;
$(document).ready(function()
{
function _connectionEstablished()
{
$('#body').append('<div>CometD Connection Established</div>');
}
function _connectionBroken()
{
$('#body').append('<div>CometD Connection Broken</div>');
}
function _connectionClosed()
{
$('#body').append('<div>CometD Connection Closed</div>');
}
// Function that manages the connection status with the Bayeux server
var _connected = false;
function _metaConnect(message)
{
if (cometd.isDisconnected())
{
_connected = false;
_connectionClosed();
return;
}
var wasConnected = _connected;
_connected = message.successful === true;
if (!wasConnected && _connected)
{
_connectionEstablished();
}
else if (wasConnected && !_connected)
{
_connectionBroken();
}
}
// Function invoked when first contacting the server and
// when the server has lost the state of this client
function _metaHandshake(handshake)
{
if (handshake.successful === true)
{
cometd.batch(function()
{
cometd.subscribe('/java/test', function(message)
{
$('#body').append('<div>Server Says: ' + message.data.eventID + ':'+ message.data.updatedDate + '</div>');
});
});
}
}
// Disconnect when the page unloads
$(window).unload(function()
{
cometd.disconnect(true);
});
var cometURL = "http://localhost:8080/cometd2/cometd";
cometd.configure({
url: cometURL,
logLevel: 'debug'
});
cometd.addListener('/meta/handshake', _metaHandshake);
cometd.addListener('/meta/connect', _metaConnect);
cometd.handshake();
});
})(jQuery);
Comet service class
#Listener("/service/java/*")
public void processMsgFromJava(ServerSession remote, ServerMessage.Mutable message)
{
Map<String, Object> input = message.getDataAsMap();
String eventId = (String)input.get("eventID");
//setting msg id
String channelName = "/java/test";
// Initialize the channel, making it persistent and lazy
bayeux.createIfAbsent(channelName, new ConfigurableServerChannel.Initializer()
{
public void configureChannel(ConfigurableServerChannel channel)
{
channel.setPersistent(true);
channel.setLazy(true);
}
});
// Publish to all subscribers
ServerChannel channel = bayeux.getChannel(channelName);
channel.publish(serverSession, input, null);
}
Is there any thing I need to change in server side code.
You have made your channel lazy, so a delay in message broadcasting is expected (that is what lazy channels are all about).
Please have a look at the documentation for lazy channels.
If you want immediate broadcasting don't set the channel as lazy.
I have problems with simple push on ffos. Here is my code:
var endpoint;
$(document).ready(function() {
registerPush();
navigator.mozSetMessageHandler("push", function(message) {
if(message.pushEndpoint == endpoint) {
console.log("push notification: "+message.version);
}
});
navigator.mozSetMessageHandler("push-register", function() {
registerPush();
});
});
function registerPush() {
var req = navigator.push.register();
req.onsuccess = function(e) {
endpoint = e.target.result;
console.log(endpoint);
}
}
It is working ok when i start the app, but after few minutes if i try to send a push to the endpoint, nothing happens. But when i call the registerPush(); function, everithing works again for few minutes.
It is said that there may be a bug. Some routers may are filtering the push notifications, so that may be the problem. I think It may be bypassed using a 3G connection.