RTCPeerConnection.iceConnectionState changed from checking to closed - webrtc

Following the method here I'm trying to answer an audio call initiated with a Chrome browser from an iPhone simulator(with React Native).
A summary of the event sequence:
received call signal
got local stream
sent join call signal
received remote description(offer),
created PeerConnection
added local stream
received candidate
added candidate
7 and 8 repeated 15 times (that is 16 times in total)
onnegotiationneeded triggered
signalingState changed into have-remote-offer
onaddstream triggered
the callback function of setRemoteDescription was triggered, created answer.
signalingState changed into stable
iceconnectionstate changed into checking
onicecandidate triggered for the first time.
emited the candidate from 15
onicecandidate triggered for the 2nd time. The candidate is null
iceconnectionstate changed into closed
Step 7,8,9 may appear at different places after 6 and before 19.
I have been stuck on this problem for quite a while. I don't even know what to debug at this time. What are the possible causes of the closing of connection? I can post more logs if needed.
One observation is that the two RTCEvent corresponding to iceconnectionstatechange has the following properties:
isTrusted:false
The target RTCPeerConnection has
iceConnectionState:"closed"
iceGatheringState:"complete"
Here are my functions to handle remoteOffer and remoteCandidates:
WebRTCClass.prototype.onRemoteOffer = function(data) {
var ref;
if (this.active !== true) {
return;
}
var peerConnection = this.getPeerConnection(data.from);
console.log('onRemoteOffer', data,peerConnection.signalingState);
if (peerConnection.iceConnectionState !== 'new') {
return;
}
var onSuccess = (function(_this){
return function(){
console.log("setRemoteDescription onSuccess function");
_this.getLocalUserMedia((function(_this) {
return function(onSuccess,stream) {
peerConnection.addStream(_this.localStream);
var onAnswer = (function(_this) {
return function(answer) {
var onLocalDescription = function() {
return _this.transport.sendDescription({
to: data.from,
type: 'answer',
ts: peerConnection.createdAt,
description: {
sdp: answer.sdp,
type: answer.type
}
});
};
return peerConnection.setLocalDescription(new RTCSessionDescription(answer), onLocalDescription, _this.onError);
};
})(_this);
return peerConnection.createAnswer(onAnswer, _this.onError);
}
})(_this)
);
}
})(this);
return peerConnection.setRemoteDescription(new RTCSessionDescription(data.description),onSuccess,console.warn);
};
WebRTCClass.prototype.onRemoteCandidate = function(data) {
var peerConnection, ref;
if (this.active !== true) {
return;
}
if (data.to !== this.selfId) {
return;
}
console.log('onRemoteCandidate', data);
peerConnection = this.getPeerConnection(data.from);
if ((ref = peerConnection.iceConnectionState) !== "closed" && ref !== "failed" && ref !== "disconnected" && ref !== "completed") {
peerConnection.addIceCandidate(new RTCIceCandidate(data.candidate));
}
};

I found that if I call the following two functions one by one, then it will work.
peerConnection.setRemoteDescription(new RTCSessionDescription(data.description),onSuccess,console.warn);
(...definition of onAnswer ...)
peerConnection.createAnswer(onAnswer, this.onError);
My previous codes called createAnswer within the onSuccess callback of setRemoteDescription. That did work for the react-native-webrtc demo, but not with Rocket.Chat. Still don't fully understand it. But my project can move on now.

Related

Angularjs - what are the possible reasons for duplicate records being inserted by the following code?

The following code is called on the click of a button
$scope.someFunction = function () {
$scope.submitting = true; // the button is disabled if submitting is true
var query = { query: { id: $scope.employeeID } };
// this api call inserts a record in a table
httpFactory.patch("/someURL", query).then(function (data) {
$scope.submitting = false;
if (data.error) {
// display error message
}
else {
// display success message
}
$scope.submitting = false;
}, function () {
$scope.submitting = false;
});
};
can duplicate records be inserted from the call above if a user has poor connectivity or if the server is slow and the request is not completed and soon another same request is received?
If so.. could any one please suggest a suitable way to handle this?

React-Navigation and creation/destroy os screens/components

I have my app with some screens. One of these screens is called "Race". This race produce a ListView with current race ranking (came from Redux state). This same screen has a TCP connection component which connect to my sensor and get data (async).
When user click on rank item, a new screen is opened with current lap's from clicked racer (item on ListView). At this point, previous screen still working (which is fine), because I can see in my log when data is received by TCP component.
The problem is when user hit back buttom (or navigate to "Race" using side menu), the screen itself is re-created and my TCP component is re-created, which I don't want. So my question is: how can I prevent this screen to be re-constructed OR make this TCP component works like a singleton globally? I'm not sure if is possible or how to make it work with Redux.
Update 1: This is part of code. In this Race screen, I have this internal function that connect my TCP socket.
_connectSensor() {
console.log("Running connectSensor function...");
const { lap_info, dispatchAddLap, dispatchSetSensorStatus } = this.props;
const { race, sensor } = this.props;
if (sensor.sensor_status == SENSOR_ONLINE) {
console.log("Already connected!");
return;
}
dispatchSetSensorStatus(SENSOR_CONNECTING);
//var serverHost = sensor.host;
//var serverPort = sensor.port;
var serverHost = "mydomain.com";
var serverPort = 1212;
console.log("Sensor host: ",serverHost);
console.log("Sensor port: ",serverPort);
this.client = net.createConnection(serverPort, serverHost, () => {
// client.write('Hello, server! Love, Client.');
dispatchSetSensorStatus(SENSOR_ONLINE);
});
this.client.on('data', (data) => {
var obj = JSON.parse(data);
dispatchAddLap(obj);
});
This function is called by a button and it's working fine. When I go to another screen, this TCP socket still running and feeding data to my Redux, which is the desirable state. But when I got back to this screen (from another one), I can't access this socket anymore.....they still running and I can't stop. When I click "Start", "this.client" is a new object because react-navigation re-create my entire screen (my guess....).
I could put some code to force disconnect when this screen is ummounted....but is not what I need. I need to keep socket receiveing data (if user doesn't stop), even if this screen is not active.
Update 2: I have tried to add static client = null; to my class object, but doesn't work.
In my "Stop" button, I've added to debug console.log("Client object:",this.client); and this is the result:
1) When I open race screen and click 'start', then 'stop', object is returned for 'client'.
2) When I open race screen, click start, goe to another screen, go back to race screen and click "Stop": undefined is returned for 'client' object.
Solved. The 'key' for this problem is to export my class as new object, and not just class.
But I had another problem: if I need to use 'connect' from 'react-redux', this doesn't work....simple because 'connect' always return a new object, not the same.
So I made my own ocmponent and create functions to assign dispatchFunctions coming from parent object. This is my final TCP componente:
import React, { Component } from "react";
import { SENSOR_ONLINE, SENSOR_OFFLINE, SENSOR_CONNECTING } from "../constants";
var net = require("react-native-tcp");
class tcpCon extends Component {
static myId;
static dispatchAddLap;
static dispatchSetSensorStatus;
static client;
static sensor_status = SENSOR_OFFLINE;
constructor(props) {
super(props);
console.log("Componente TCP criado! Dados passados: ", props);
var RandomNumber = Math.floor(Math.random() * 10000 + 1);
this.myId = RandomNumber;
}
setDispatchAddLapFunc(func) {
this.dispatchAddLap = func;
}
setDispatchSetSensorStatusFunc(func) {
this.dispatchSetSensorStatus = func;
}
disconnectSensor() {
console.log("Disconnecting....Status no componente TCP: ", this.sensor_status);
if (this.sensor_status == SENSOR_ONLINE) {
this.sensor_status = SENSOR_OFFLINE;
// Dispatch sensor offline
if (this.client) {
console.log("Client object exists. Need to destroy it....");
this.client.destroy();
this.client = null;
} else {
console.log("Client doesn't exists");
}
}
}
displayObj() {
console.log("Client nesta conexao:",this.client);
}
connectSensor(hostname, port) {
console.log("Running connectSensor function...myId: ", this.myId);
console.log("Meu status local:", this.sensor_status);
if (this.sensor_status == SENSOR_ONLINE || this.client) {
console.log("Connection already exists! Returning....");
return;
}
//var con = net.createConnection(port, hostname, () => {
this.client = net.createConnection(port, hostname, () => {
// client.write('Hello, server! Love, Client.');
// dispatchSetSensorStatus(SENSOR_ONLINE);
//this.client = con;
this.sensor_status = SENSOR_ONLINE;
this.dispatchSetSensorStatus(SENSOR_ONLINE);
});
this.client.on("data", data => {
var obj = JSON.parse(data);
//console.log("Data arrived: ",obj);
this.dispatchAddLap(obj);
});
this.client.on("error", error => {
console.log("Erro conectando ao sensor: " + error);
// dispatchSetSensorStatus(SENSOR_OFFLINE);
this.sensor_status = SENSOR_OFFLINE;
this.dispatchSetSensorStatus(SENSOR_OFFLINE);
});
this.client.on("close", () => {
console.log("Conexão fechada no componente TCP.");
// dispatchSetSensorStatus(SENSOR_OFFLINE);
this.sensor_status = SENSOR_OFFLINE;
this.dispatchSetSensorStatus(SENSOR_OFFLINE);
});
//console.log("Client object na connection:", this.client);
//this.dispatchAddLap('Hello world!ID '+this.myId.toString());
}
}
export default new tcpCon();
And this is how I call to connect:
Con.setDispatchAddLapFunc(dispatchAddLap);
Con.setDispatchSetSensorStatusFunc(dispatchSetSensorStatus)
Con.connectSensor("address",1212);
To disconnect, just use this:
Con.setDispatchAddLapFunc(dispatchAddLap); // Just to be sure
Con.setDispatchSetSensorStatusFunc(dispatchSetSensorStatus); // Just to be sure
Con.disconnectSensor();
With this, anytime that I call this component, the same object is returned.

WebRTC - Failed to set remote answer sdp: Called in wrong state: STATE_INPROGRESS

I'm following the example here: https://www.w3.org/TR/webrtc/#simple-peer-to-peer-example
I've modified the code because I only need one-way streaming:
var configuration = null; //{ "iceServers": [{ "urls": "stuns:stun.example.org" }] };
var peerConnection;
var outboundPeerStream = null;
var outboundPeerStreamSessionId = null;
var createPeerConnection = function () {
if (peerConnection)
return;
peerConnection = new RTCPeerConnection(configuration);
// send any ice candidates to the other peer
peerConnection.onicecandidate = function (event) {
signalrModule.sendClientNotification(JSON.stringify({ "candidate": event.candidate }));
};
// let the "negotiationneeded" event trigger offer generation
peerConnection.onnegotiationneeded = peerStreamingModule.sendOfferToPeers;
// once remote track arrives, show it in the remote video element
peerConnection.ontrack = function (event) {
var inboundPeerStream = event.streams[0];
remoteStreamHelper.pushStreamToDom(inboundPeerStream, foo);
}
}
// this gets called either on negotiationNeeded and every 30s to ensure all peers have the offer from the stream originator
peerStreamingModule.sendOfferToPeers = function () {
peerConnection.createOffer().then(function (offer) {
return peerConnection.setLocalDescription(offer);
}).then(function () {
// send the offer to the other peer
signalrModule.sendClientNotification(JSON.stringify({ "desc": peerConnection.localDescription}));
}).catch(logger.internalLog);
};
// this gets called by the stream originator when the stream is available to initiate streaming to peers
peerStreamingModule.initializeWithStream = function (outboundStream, sessionId) {
outboundPeerStream = outboundStream;
outboundPeerStreamSessionId = sessionId;
createPeerConnection();
peerConnection.addStream(outboundStream);
//peerStreamingModule.sendOfferToPeers(); I don't think I need this...
}
peerStreamingModule.handleP2PEvent = function (notification) {
if (!peerConnection)
createPeerConnection();
if (notification.desc) {
var desc = notification.desc;
// if we get an offer, we need to reply with an answer
if (desc.type == "offer") {
peerConnection.setRemoteDescription(desc).then(function () {
return peerConnection.createAnswer();
}).then(function (answer) {
return peerConnection.setLocalDescription(answer);
}).then(function () {
signalrModule.sendClientNotification(JSON.stringify({ "desc": peerConnection.localDescription, "sessionId": sessionManager.thisSession().deviceSessionId() }), app.username());
}).catch(logger.internalLog);
} else if (desc.type == "answer") {
peerConnection.setRemoteDescription(desc).catch(logger.internalLog);
} else {
logger.internalLog("Unsupported SDP type. Your code may differ here.");
}
} else
pc.addIceCandidate(notification.candidate).catch(logger.internalLog);
}
This seems to be working, but I'm stumped with two parts:
1) WebRTC - Failed to set remote answer sdp: Called in wrong state: STATE_INPROGRESS - this is appearing in my logs from time to time - am I doing something wrong in the above that is causing this?
2) Am I correctly implementing sendOfferToPeers and initializeWithStream? I'm afraid that the sendOfferToPeers getting triggered on interval from the originator isn't how the spec is intended to be used; my goal is to ensure that all peers eventually receive an offer no matter when they join or whether or not they're facing connectivity issues that drop the original offer / negotiation.
// this gets called either on negotiationNeeded and every 30s to ensure all peers have the offer
You can't send the same offer to multiple peers. It's peer-to-peer, not peer-to-peers. One-to-many requires at minimum a connection per participant, and probably a media server to scale.
Also, SDP is not for discovery. The offer/answer exchange is a fragile state-machine negotiation between two end-points only, to set up a single connection.
You should solve who's connecting with whom ahead of establishing the WebRTC connection.

Angular2 - Multiple dependent sequential http api calls

I am building an Angular2 app and one of the components needs to make multiple API calls which are dependent on the previous ones.
I currently have a service which makes an API call to get a list of TV shows. For each show, I then need to call a different API multiple times to step through the structure to determine if the show exists on a Plex server.
The API documentation is here
For each show, I need to make the following calls and get the correct data to determine if it exists: (Assume we have variables <TVShow>, <Season>, <Episode>)
http://baseURL/library/sections/?X-Plex-Token=xyz will tell me:
title="TV Shows" key="2"
http://baseURL/library/sections/2/all?X-Plex-Token=xyz&title=<TVShow> will tell me: key="/library/metadata/2622/children"
http://baseURL/library/metadata/2622/children?X-Plex-Token=xyz will tell me: index="<Season>" key="/library/metadata/14365/children"
http://baseURL/library/metadata/14365/children?X-Plex-Token=xyz will tell me: index="<Episode>" which implies that the episode I have exists.
The responses are in json, I have removed a lot of the excess text. At each stage I need to check that the right fields exist (<TVShow>, <Season>, <Episode>) so that they can be used for the next call. If not, I need to return that the show does not exist. If it does, I will probably want to return an id for the show.
I have looked at lots of examples including promise, async & flatmap, but am not sure how to solve this based on the other examples I have seen.
How to chain Http calls in Angular2
Angular 2.0 And Http
Angular 2 - What to do when an Http request depends on result of another Http request
Angular 2 chained Http Get Requests with Iterable Array
nodejs async: multiple dependant HTTP API calls
How to gather the result of Web APIs on nodeJS with 'request' and 'async'
Here is what I have for getting the list of shows. (shows.service.ts)
export class ShowsHttpService {
getShows(): Observable<Show[]> {
let shows$ = this._http
.get(this._showHistoryUrl)
.map(mapShows)
.catch(this.handleError);
return shows$;
}
}
function mapShows(response:Response): Show[] {
return response.json().data.map(toShow);
}
function toShow(r:any): Show {
let show = <Show>({
episode: r.episode,
show_name: r.show_name,
season: r.season,
available : false, // I need to fill in this variable if the show is available when querying the Plex API mentioned above.
});
// My best guess is here would be the right spot to call the Plex API as we are dealing with a single show at a time at this point, but I cannot see how.
return show;
}
Here is the relevant code from the component (shows.component.ts)
public getShows():any {
this._ShowsHttpService
.getShows()
.subscribe(w => this.shows = w);
console.log(this.shows);
}
Bonus points
Here are the obvious next questions that are interesting, but not necessary:
The first API query will be much faster than waiting for all of the other queries to take place (4 queries * ~10 shows). Can the initial list be returned and then updated with the available status when it is ready.
The first Plex call to get the key="2" only needs to be performed once. It could be hard coded, but instead, can it be performmed once and remembered?
Is there a way to reduce the number of API calls? I can see that I could remove the show filter, and search through the results on the client, but this doesn't seam ideal either.
The 4 calls for each show must be done sequentially, but each show can be queried in parallel for speed. Is this achievable?
Any thoughts would be much appreciated!
Not sure if I totally understand your question, but here is what I do:
I make the first http call, then when the subscribe fires, it calls completeLogin. I could then fire another http call with its own complete function and repeat the chain.
Here is the component code. The user has filled in the login information and pressed login:
onSubmit() {
console.log(' in on submit');
this.localUser.email = this.loginForm.controls["email"].value;
this.localUser.password = this.loginForm.controls["password"].value;
this.loginMessage = "";
this.checkUserValidation();
}
checkUserValidation() {
this.loginService.getLoggedIn()
.subscribe(loggedIn => {
console.log("in logged in user validation")
if(loggedIn.error != null || loggedIn.error != undefined || loggedIn.error != "") {
this.loginMessage = loggedIn.error;
}
});
this.loginService.validateUser(this.localUser);
}
This calls the loginservice ValidateUser method
validateUser(localUser: LocalUser) {
this.errorMessage = "";
this.email.email = localUser.email;
var parm = "validate~~~" + localUser.email + "/"
var creds = JSON.stringify(this.email);
var headers = new Headers();
headers.append("content-type", this.constants.jsonContentType);
console.log("making call to validate");
this.http.post(this.constants.taskLocalUrl + parm, { headers: headers })
.map((response: Response) => {
console.log("json = " + response.json());
var res = response.json();
var result = <AdminResponseObject>response.json();
console.log(" result: " + result);
return result;
})
.subscribe(
aro => {
this.aro = aro
},
error => {
console.log("in error");
var errorObject = JSON.parse(error._body);
this.errorMessage = errorObject.error_description;
console.log(this.errorMessage);
},
() => this.completeValidateUser(localUser));
console.log("done with post");
}
completeValidateUser(localUser: LocalUser) {
if (this.aro != undefined) {
if (this.aro.errorMessage != null && this.aro.errorMessage != "") {
console.log("aro err " + this.aro.errorMessage);
this.setLoggedIn({ email: localUser.email, password: localUser.password, error: this.aro.errorMessage });
} else {
console.log("log in user");
this.loginUser(localUser);
}
} else {
this.router.navigate(['/verify']);
}
}
In my login service I make a call to the authorization service which returns an observable of token.
loginUser(localUser: LocalUser) {
this.auth.loginUser(localUser)
.subscribe(
token => {
console.log('token = ' + token)
this.token = token
},
error => {
var errorObject = JSON.parse(error._body);
this.errorMessage = errorObject.error_description;
console.log(this.errorMessage);
this.setLoggedIn({ email: "", password: "", error: this.errorMessage });
},
() => this.completeLogin(localUser));
}
In the authorization service:
loginUser(localUser: LocalUser): Observable<Token> {
var email = localUser.email;
var password = localUser.password;
var headers = new Headers();
headers.append("content-type", this.constants.formEncodedContentType);
var creds:string = this.constants.grantString + email + this.constants.passwordString + password;
return this.http.post(this.constants.tokenLocalUrl, creds, { headers: headers })
.map(res => res.json())
}
The point here in this code, is to first call the validateUser method of the login service, upon response, based on the return information, if its valid, I call the loginUser method on the login service. This chain could continue as long as you need it to. You can set class level variables to hold the information that you need in each method of the chain to make decisions on what to do next.
Notice also that you can subscribe to the return in the service and process it there, it doesn't have to return to the component.
Okay, Here goes:
public getShows():any {
this._ShowsHttpService
.getShows()
.subscribe(
w => this.shows = w,
error => this.errorMessage = error,
() => this.completeGetShows());
}
completeGetShow() {
//any logic here to deal with previous get;
this.http.get#2()
.subscribe(
w => this.??? = w),
error => this.error = error,
() => this.completeGet#2);
}
completeGet#2() {
//any logic here to deal with previous get;
this.http.get#3()
.subscribe(
w => this.??? = w),
error => this.error = error,
() => this.completeGet#3);
}
completeGet#3() {
//any logic here to deal with previous get;
//another http: call like above to infinity....
}

Opening chrome should cause the extension to check the output of an API.How can this be possible?

Hi I am developing a website as my chrome extension.the extension periodically checks the output of a server file and then works according to the output of the server file.Now what I need is that presently it checks the output of the server file every 5 min.So what I need is that when the output of the server file changes at any time,at that moment I have to do some operations.How can I do this?
Here is my background.js
var myNotificationID = null;
var oldChromeVersion = !chrome.runtime;
chrome.windows.onCreated.addListener(function (){
updateIcon();
});
chrome.tabs.onCreated.addListener(function (){
updateIcon();
});
chrome.tabs.onUpdated.addListener(function (){
updateIcon();
});
function getGmailUrl() {
return "http://calpinemate.com/";
}
function isGmailUrl(url) {
return url.indexOf(getGmailUrl()) == 0;
}
chrome.windows.onCreated.addListener(function (){
updateIcon();
});
function onInit() {
updateIcon();
if (!oldChromeVersion) {
chrome.alarms.create('watchdog',{periodInMinutes:5,delayInMinutes: 0});
}
}
function onAlarm(alarm) {
if (alarm && alarm.name == 'watchdog') {
onWatchdog();
}
else {
updateIcon();
}
}
function onWatchdog() {
chrome.alarms.get('refresh', function(alarm) {
if (alarm) {
console.log('Refresh alarm exists. Yay.');
}
else {
updateIcon();
}
});
}
if (oldChromeVersion) {
updateIcon();
onInit();
}
else {
chrome.runtime.onInstalled.addListener(onInit);
chrome.alarms.onAlarm.addListener(onAlarm);
}
It is the updateIcon() which reads the server file.And when there is change in the output of server file,I have to call the updateIcon() itself.Presently only in every 5 min,the output is checked and updated in extension.But I need it to happen at the moment the status of server file changes.Anyone please help me.
In short,what I need is that when the output of API changes at any time at that time I have to call updateIcon().
Here is my updateIcon()
function updateIcon(){
var urlPrefix = 'http://www.calpinemate.com/employees/attendanceStatus/';
var urlSuffix = '/2';
var req = new XMLHttpRequest();
req.addEventListener("readystatechange", function() {
if (req.readyState == 4) {
if (req.status == 200) {
var item=req.responseText;
if(item==1){
.....//something
}
else{
// do something
}
else {
alert("ERROR: status code " + req.status);
}
}
});
var url = urlPrefix + encodeURIComponent(localStorage.username) + urlSuffix;
req.open("GET", url);
req.send(null);
}
I need to periodically monitor the API for output.
You could use a WebSocket to maintain a persistent, two-way channel between the server and your extension.
But, considering that PHP does not have native support for WebSockets (i.e. you would need to use an external library) and the fact that the interaction will be infrequent (only at log-in/-out), it might be unnecessary overhead.
I suggest you communicate the login-status directly from your web-page to your extension. For a more detailed description of the process, see my answer to one similar question of yours.
UPDATE:
Since it turned out you don't have control of the domain you need to "monitor", there is unfortunately no other option (that I know of) than polling at frequent intervals on that server-resource.
If you are using a non-persistent background-page (which is advisable due to its resource-friendliness) you must use the chrome.alarms API, which allows at most 1 triggering per minute: (in order to reduce the load on the user's machine - note: to help debug your extension, this limit is no imposed on unpacked extensions).
If you decide to use a persistent background-page, you can use setInterval() with an arbitrarily smal period (in milliseconds):
setInterval(function() {
/* Check up on the server */
...
}, 30000); // <-- trigger every 30.000 milliseconds (== 30 seconds)