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

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.

Related

Nestjs - redis communication between 2 microservices with ClientProxy

I need a help from somebody experienced. I've built 2 microservices recently (let's call them Amber and Boris) which are communicating between each other using ClientProxy and REDIS. From time to time, when Amber is asking for data from Boris, it gets timeout Error.
This is Amber config:
constructor(companyName: string, userId: number) {
this.companyName = companyName;
this.userId = userId;
this.client = ClientProxyFactory.create({
transport: Transport.REDIS,
options: {
retryAttempts: 0,
retryDelay: 0,
url: 'redis://<some_url>:<some_port>,
},
});
}
Then request-response:
private async sendRequest(pattern: string, payload?: object): Promise<any[]> {
payload = payload || {};
try {
const result = await this.client.send(
{ type: pattern },
{ userId: this.userId, companyName: this.companyName, ...payload}
)
.pipe(
timeout(30000),
map((response: any) => { // Success...
return response;
}),
catchError((error) => { // Error...
return throwError(error);
}),
)
.toPromise();
return result;
} catch (err) {
Logger.error('Couldn\'t get data from Boris service: ' + err.message)
}
}
Then on Boris service, I have basically just Controller set with #MessagePattern and I'm just returning data:
#MessagePattern({type: 'getAvailableCases'})
findAll(#Payload() data: object): Promise<object> {
this.assignPayload(data);
return this.getData();
}
Important to say, Boris service is doing queries to database in order to return data. But on db side seems there is no problem.
What I'm interested in the most is:
whether I have ClientProxy set up properly
whether I have answer processing set up properly with pipe() and toPromise(), as I'm not well familiarized with ClientProxy and RxJs.
Thank you a hundred times for any response!
Turned out, the ClientProxy wasn't releasing connections to Redis after the communication is done. This way, the number of connections was increasing until there was no connection left.
The solution is to close connection after the data are returned:
this.client.close();

RabbitMQ StompJs recieving just one Message

I'm trying to recieve a Message from the RabbitMq-Que with the Stomp-plugin. This works fine but the Problem is that I get every Message from the Queue. So if the Queue has 13 Messages, I get 13. The Key is that I just want to get 1 Message and after sending Ack or Nack the next one. Do somebody got any Idea how to get only one Message? Thanks for Help.
Here the code I got:
GetMessage()
{
this.GetRabbitMqClient().then((client)=>
{
var headers ={ack:'client', 'x-max-priority': '10'};
var subscription = client.subscribe("/queue/TestQue",this.messageCallback,headers);
});
}
private messageCallback = function(Message :IMessage)
{
console.log(Message.body);
setTimeout(()=> {Message.ack();},100000000000000);
}
private GetRabbitMqClient( ):Promise<Client> {
var promise = new Promise<Client>((resolve,reject)=>{
var client = new Client(
{
brokerURL: "ws://localhost:15674/ws",
connectHeaders:
{
login: "guest",
passcode: "guest"
},
// debug: function (str) {
// console.log(str);
// },
reconnectDelay: 5000,
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000
});
client.onConnect = function (frame) {
resolve(client);
};
client.onStompError = function (frame) {
// Will be invoked in case of error encountered at Broker
// Bad login/passcode typically will cause an error
// Complaint brokers will set `message` header with a brief message. Body may contain details.
// Compliant brokers will terminate the connection after any error
reject(frame);
console.log('Broker reported error: ' + frame.headers['message']);
console.log('Additional details: ' + frame.body);
};
client.activate();
});
return promise;
}
I found the solution:
you just need to set in the headers the attribute:
'prefetch-count':'1'

RTCPeerConnection.iceConnectionState changed from checking to closed

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.

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)