WebRTC - RTCPeerConnection.onconnectionstatechange fires with RTCPeerConnection.connectionState = 'disconnected' without any reason - webrtc

I am working on the WebRTC application for video chatting. On my local network everything works well. But when I try it to test through internet RTCPeerConnection.onconnectionstatechange fires with RTCPeerConnection.connectionState = 'disconnected' without any reason after some 20-30 seconds of communication. Another very confusing thing is that for example I have peer2 and peer3 started in the same browser in different tabs connected to peer1 and peer1 videostreaming to them. And after 20-30 seconds RTCPeerConnection.connectionState = 'disconnected' can fire on peer2 and at the same time peer3 continues to receive video stream from peer1. I have googled a bit and found this solution (which doesnt work in my case):
this.myRTCMediaMediatorConnections[id][hash].onconnectionstatechange=async function(e){
log('onSignalingServerMediaMediatorOfferFunc.myRTCMediaMediatorConnections['+id+']['+hash+'].onconnectionstatechange('+This.myRTCMediaMediatorConnections[id][hash].connectionState+')',10,true)
switch(This.myRTCMediaMediatorConnections[id][hash].connectionState){
case "failed":
This.disconnectMeFromMediatorConnection(targetId,logicGroupName,streamerId,streamerHash,id,hash)
break
case "closed":
This.disconnectMeFromMediatorConnection(targetId,logicGroupName,streamerId,streamerHash,id,hash)
break
case "disconnected":
if(await This.confirmPeerDisconnection(This.myRTCMediaMediatorConnections[id][hash]))This.disconnectMeFromConnection(targetId,logicGroupName,streamerId,streamerHash,id,hash)
break
}
log('onSignalingServerMediaMediatorOfferFunc.myRTCMediaMediatorConnections['+id+']['+hash+'].onconnectionstatechange',10,false)
}
this.confirmPeerDisconnection=async function(connectionObject){
log('confirmPeerDisconnection',10,true)
var b1=await this.confirmPeerDisconnectionFunc(connectionObject);
await new Promise(resolve=>setTimeout(resolve,2000));
var b2=await this.confirmPeerDisconnectionFunc(connectionObject);
log('confirmPeerDisconnection=>'+(b2-b1),10,false)
if(b2-b1>0)return false
return true;
}
this.confirmPeerDisconnectionFunc=async function(connectionObject){
var b=0
await connectionObject.getStats(null).then(function(stats){
stats.forEach((report)=>{if(report.type=='transport')Object.keys(report).forEach((statName)=>{if(statName==='bytesReceived')b=parseInt(report[statName])})})
})
return b
}
b2-b1 always equals 0 or less than 0. Can anyone give me an advise why RTCPeerConnection.onconnectionstatechange fires and how I can get rid of this bug.
Any help appriciated!

Related

how to get the previous state of the player in videojs

I'm using "videojs" to play videos on my site and i want to know how can i get the previous state of the player (playing, paused, etc).
I used to word with "jwplayer" and there was a variable called "oldstate" that did the work.
I was able to handle the state of the player with this code snippet:
var was_paying= false;
player.on(['waiting', 'pause'], function () {
was_playing= true;
});
player.on('playing', function () {
was_playing= false;
});
The was_playing variable acts as a flag to check whether the player was in a playing state or it was paused by the user or it was in a waiting mode because of the poor network connection.
If you know any better way to do this I'd be glad to know your solutions.

WebRTC: Detecting muted track faster post warm-up

I'm warming up my transceiver like so:
pc.addTranceiver('video')
This creates a dummy track in the transceiver's receiver. Soon after, the unmute event fires on that track.
Then, ~3 seconds later, the mute event fires.
My goal is to detect that a track is a dummy track as fast as possible.
ideas
send a message via the data channel telling the peer that the track is void. this is a pain since i'll have to send another message when I later call replaceTrack
write a frame of the track to canvas & see if it's an image. This seems really barbaric, but it's faster than 3 seconds.
anything better? it feels like this should be pretty simple.
This is a bug in Chrome (please ★ it so they'll fix it).
The spec says receiver tracks must start out muted and should stay that way until packets arrive. But Chrome fires the unmute event immediately, followed a few seconds later by a mute event due to inactivity (another bug):
const config = {sdpSemantics: "unified-plan"};
const pc1 = new RTCPeerConnection(), pc2 = new RTCPeerConnection();
pc1.addTransceiver("video");
pc2.ontrack = ({track}) => {
console.log(`track starts out ${track.muted? "muted":"unmuted"}`);
track.onmute = () => console.log("muted");
track.onunmute = () => console.log("unmuted");
};
pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
pc1.onnegotiationneeded = async e => {
await pc1.setLocalDescription(await pc1.createOffer());
await pc2.setRemoteDescription(pc1.localDescription);
await pc2.setLocalDescription(await pc2.createAnswer());
await pc1.setRemoteDescription(pc2.localDescription);
}
In Chrome you'll see incorrect behavior:
track starts out muted
unmuted
muted
In Firefox you'll see correct behavior:
track starts out muted
Chrome workaround:
Until Chrome fixes this, I'd use this workaround:
const video = document.createElement("video");
video.srcObject = new MediaStream([track]);
video.onloadedmetadata = () => log("unmuted workaround!");
Until this fires, assume the track to be muted.

How do you poll for a condition in Intern / Leadfoot (not browser / client side)?

I'm trying to verify that an account was created successfully, but after clicking the submit button, I need to wait until the next page has loaded and verify that the user ended up at the correct URL.
I'm using pollUntil to check the URL client side, but that results in Detected a page unload event; script execution does not work across page loads. in Safari at least. I can add a sleep, but I was wondering if there is a better way.
Questions:
How can you poll on something like this.remote.getCurrentUrl()? Basically I want to do something like this.remote.waitForCurrentUrlToEqual(...), but I'm also curious how to poll on anything from Selenium commands vs using pollUntil which executes code in the remote browser.
I'm checking to see if the user ended up at a protected URL after logging in here. Is there a better way to check this besides polling?
Best practices: do I need to make an assertion with Chai or is it even possible when I'm polling and waiting for stuff as my test? For example, in this case, I'm just trying to poll to make sure we ended up at the right URL within 30 seconds and I don't have an explicit assertion. I'm just assuming the test will fail, but it won't say why. If the best practice is to make an assertion here, how would I do it here or any time I'm using wait?
Here's an example of my code:
'create new account': function() {
return this.remote
// Hidden: populate all account details
.findByClassName('nextButton')
.click()
.end()
.then(pollUntil('return location.pathname === "/protected-page" ? true : null', [], 30000));
}
The pollUntil helper works by running an asynchronous script in the browser to check a condition, so it's not going to work across page loads (because the script disappears when a page loads). One way to poll the current remote URL would be to write a poller that would run as part of your functional test, something like (untested):
function pollUrl(remote, targetUrl, timeout) {
return function () {
var dfd = new Deferred();
var endTime = Number(new Date()) + timeout;
(function poll() {
remote.getCurrentUrl().then(function (url) {
if (url === targetUrl) {
dfd.resolve();
}
else if (Number(new Date()) < endTime) {
setTimeout(poll, 500);
}
else {
var error = new Error('timed out; final url is ' + url);
dfd.reject(error);
}
});
})();
return dfd.promise;
}
}
You could call it as:
.then(pollUrl(this.remote, '/protected-page', 30000))
When you're using something like pollUntil, there's no need (or place) to make an assertion. However, with your own polling function you could have it reject its promise with an informative error.

Push Notifications. How to know if the app was in foreground or background

When a notification arrives the app executes the callback configured for receiving the notifications.
In case the notification arrives with the app in background I want the application to move to a specific view. But if the notification arrives with the app in foreground I just want to print an alert.
How can I know in the callback function the status of the application when the notification arrived?
Thank you.
I don't know how dependable this is, but it seems to work when the app is running, but just in the background:
var sleeping = false;
document.addEventListener("pause", function() {sleeping = true;}, false);
document.addEventListener("resume", function() {sleeping = false;}, false);
and then:
function pushNotificationReceived(props, payload) {
if (sleeping) {
alert("caught me napping");
} else {
alert("I've been waiting for you.");
}
}
The tricky case is when the application is completely stopped. You need to log in before the notification callback gets called, and the resume event is fired long before this. If you want to handle that case, you will probably need something like the following in your WL.Client.Push.onReadyToSubscribe function:
sleeping = true;
setTimeout(function(){sleeping = false;}, 1000);
(anything that arrives within 1 second of being ready to subscribe, probably arrived when we were asleep, and is just getting delivered now)
It is a bit of a haCk, and I'm sure there are all sorts of odd timing cases but it seems to cover many of the cases.

Sencha Touch: Prevent multiple concurrent transitions

QA just filed a real doozy of a bug, and I'm scratching my head how to fix it.
If two buttons, e.g. back, and search are pressed at the same time, each will invoke Ext.dispatch, causing two simultaneous opposing transitions! This totally !##$s up the layout, rendering the app unusable.
This is really a general problem with touch-enabled apps... with multiple fingers hovering over the screen, the user can easily trigger weird and totally incompatible action combinations, and the app needs to accept only one at a time. Is there any way to handle this situation gracefully in Sencha Touch?
I fixed it by listening to the before-dispatch event, and canceling it if there is a dispatch already in progress.
Ext.regApplication(...
this._isDispatching = false,
launch: function() {
Ext.Dispatcher.on('before-dispatch', function () {
var me;
if (this._isDispatching)
return false;
else {
this._isDispatching = true;
me = this;
setTimeout(function () {
me._isDispatching = false;
}, 500);
return true;
}
}, this);
}
Yes, the 500ms delay is definitely hacky, but I couldn't think of a more robust way of detecting when the transition has completed. There is no after-dispatch event, and the dispatch event fires before the transition has completed.
Hope this helps someone.