I'm building a WebRTC app with a central electron app that browsers connect to. The scenario I'm testing is running the electron app on my computer (Ubuntu 16.04) and connecting from chrome (69) on Android (7.0). Following the debugging the offer, answer and candidate are passed, but fails on the last stop of generating the connection. The ice connection state switchs to "checking" then "failed". I'm able to load the browser app on my laptop and connect to an electron app on the same computer.
Should I be using a collection of ice servers? What do I need to make the WebRTC connection more robust? Should I not write my own signaling process and use something pre-made? Is their anyway to debug the reason why the connection failed? For debugging I've tried the webrtc debugging tab on chrome but all it says is connection failled at the bottom of the stack.
The configuration for my RTCPeerConnection is:
const configuration = {
"iceServers": [{ "urls": "stun:stun.1.google.com:19302" }]
};
This is the code I use to form iniate the connection to the electron app from the browser app (The function are attached to a ts class which are called in order: setupPeerConnection, setupDataChannel, makeOffer):
setupPeerConnection(gameID:string) {
this.gameID = gameID;
let configuration:RTCConfiguration = {
"iceServers": [{ "urls": "stun:stun.1.google.com:19302" }]
};
this.connection = new RTCPeerConnection(configuration);
const that:PeerConnection = this;
//Definition of the data channel
this.connection.ondatachannel = function(ev:RTCDataChannelEvent) {
};
//When we get our own ICE Candidate, we provide it to the other Peer.
this.connection.onicecandidate = function(event:RTCPeerConnectionIceEvent) {
if (event.candidate && that.connectedToGame) {
that.serverConnection.send({
type: "candidate",
candidate: event.candidate
});
}
};
this.connection.oniceconnectionstatechange = function(e:Event) {
let iceState:RTCIceConnectionState = this.iceConnectionState;
console.log("Changing connection state:", iceState)
if (iceState == "connected") {
console.log("Connection established with server");
} else if (iceState =="disconnected" || iceState == "closed") {
// We lost the user
that.connectedToGame = false;
that.onLeave();
}
};
}
setupDataChannel(onopen:(error:ErrorEvent)=>void, onmessage:(message:MessageEvent)=>void) {
let dataChannelOptions:RTCDataChannelInit = <RTCDataChannelInit>{
reliable: true
};
this.dataChannel = this.connection.createDataChannel(this.gameID + "-dataChannel", dataChannelOptions);
this.dataChannel.onerror = function (error:ErrorEvent) {
console.log("Error on data channel:", error);
};
this.dataChannel.onmessage = onmessage.bind(this);
this.dataChannel.onopen = onopen;
this.dataChannel.onclose = function() {
console.log("Channel closed.");
};
}
makeOffer() {
let that:PeerConnection = this;
this.connection.createOffer().then(function (offer:RTCSessionDescriptionInit) {
that.serverConnection.send({
gameID: that.gameID,
type: "offer",
offer: offer
});
that.connection.setLocalDescription(offer);
}, function (error) {
console.log("Error contacting remote peer: ", error);
});
}
Related
So I searched here about this problem and I saw many experienced it but still didn't find solution.
FCM Push notifications arrive twice if the browser is in background
Ty for your help.
My nuxt config firebase serviced:
services: {
auth: true, // Just as example. Can be any other service.,
messaging: {
createServiceWorker: true,
fcmPublicVapidKey: "###", // OPTIONAL : Sets vapid key for FCM after initialization
inject: fs.readFileSync("./serviceWorker.js")
}
}
my service worker:
messaging.setBackgroundMessageHandler(function(payload) {
console.log("[firebase-messaging-sw.js] Received background message ");
self.registration.hideNotification();
return null;
});
self.addEventListener("push", function(e) {
data = e.data.json();
const options = {
tag: "notification-1",
body: data.notification.body,
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: "1"
}
};
self.registration.showNotification(data.notification.title, options);
});
self.addEventListener(
"notificationclick",
function(event) {
console.log("test", event);
event.notification.close();
const url = "home";
event.waitUntil(
self.clients.matchAll({ type: "window" }).then(windowClients => {
// Check if there is already a window/tab open with the target URL
for (let i = 0; i < windowClients.length; i++) {
const client = windowClients[i];
// If so, just focus it.
if (client.url === url && "focus" in client) {
return client.focus();
}
}
if (self.clients.openWindow) {
console.log("open window");
}
})
);
},
false
);
Add
self.registration.hideNotification();
On top of line
self.registration.showNotification(
This allows your app to hide the default notification in which case you will only have one notification.
I'm trying to get my "click_action" to take users to specific URLs on notifications that I'm sending to clients, but whatever I do it either does nothing (desktop) or just opens the PWA (android). The messages are coming through fine (checked in Chrome console) but clicking just doesn't seem to work.
I have the following in my service worker, cribbed from various places including other answers provided on this site:
importScripts('https://www.gstatic.com/firebasejs/7.14.3/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.14.3/firebase-messaging.js');
// importScripts('/__/firebase/init.js');
/* An empty service worker! */
self.addEventListener('fetch', function(event) {
/* An empty fetch handler! */
});
var firebaseConfig = {
//REDACTED
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
notificationTitle = payload.notification.title;
notificationOptions = {
body: payload.notification.body,
icon: payload.notification.icon,
click_action: payload.notification.click_action
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
self.addEventListener('notificationclick', function(event) {
let url = event.notification.click_action;
// I've also added a data.click_action field in my JSON notification, and have tried using that
// instead, but that didn't work either
console.log('On notification click: ', event.notification.tag);
event.notification.close(); // Android needs explicit close.
event.waitUntil(
clients.matchAll({ includeUncontrolled: true, type: 'window' }).then( windowClients => {
// Check if there is already a window/tab open with the target URL
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
// If so, just focus it.
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
// If not, then open the target URL in a new window/tab.
if (clients.openWindow) {
return clients.openWindow(url);
}
})
);
});
self.onnotificationclick = function(event) {
let url = event.notification.click_action;
console.log('On notification click: ', event.notification.tag);
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.waitUntil(clients.matchAll({ includeUncontrolled: true, type: 'window' }).then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == url && 'focus' in client)
return client.focus();
}
if (clients.openWindow)
return clients.openWindow(url);
}));
};
The notifications come through fine on both android (installed PWA) and chrome, and the message payload in the developer console is well formatted and received fine. In the message I'm sending from the server I have a URL with a custom parameter on the end (e.g. https://[domain]/list.php?userid=123) but, as above, clicking on the notification doesn't do anything on windows/chrome, and on the android it opens the PWA successfully but then doesn't go to the URL in the payload, it just goes to wherever the PWA was when last open. The "userid" changes depending on the message trigger.
Sample JSON of message payload:
{data: {…}, from: "xxx", priority: "high", notification: {…}, collapse_key: "do_not_collapse"}
collapse_key: "do_not_collapse"
data: {gcm.notification.badge: "[logo URL]", click_action: "https://[URL]/list.php?userid=33"}
from: "xxx"
notification:
body: "'5' has just been added"
click_action: "https://[URL]/list.php?userid=33"
icon: "https://[logo URL]"
title: "alert "
I also saw something about "webpush": { "fcm_options": { "link": "https://dummypage.com"}} on https://firebase.google.com/docs/cloud-messaging/js/receive but couldn't figure out if that was relevant or needed also.
Am very surprised just providing a URL in the click_action doesn't seem to just do that action when you click the notificaiton! Is anything needed in the service worker at all?!?!
Could one of the problems be that the PWA doesn't update the SW regularly, and so if my code above should work (a big if!) then i just need to wait for the SW to update on the installed android app? If so, is there a way to speed up its updating?!?
Thanks so much in advance for any assistance. Am tying myself in knots here!
I spent a lot of time looking for a solution for the same problem. Maybe this can help :
if you send notification with firebase messaging, you can use webpush field. firebase messaging client library execute self.registration.showNotification() ... No more need messaging.onBackgroundMessage in your service worker.
// firebabse-coud-function.js
app.messaging().send({
webpush: {
notification: {
title: notification?.title || "Default title",
icon: notification?.icon || "/icon.png",
badge: notification?.icon || "/icon.png",
},
fcmOptions: {
link: `${BASE_URL || ""}${notification?.clickAction || "/"}`,
}
},
data: {
userID: notification.userID,
link: notification?.clickAction || "/",
},
topic
});
Most importantly, in your service worker add a 'notificationclick' event listener before calling firebase.messaging()
so my service worker looks like:
// firebase-messaging-sw.js
// ...
self.addEventListener('notificationclick', function (event) {
console.debug('SW notification click event', event)
const url = event.notification?.data?.FCM_MSG?.data?.link;
// ...
})
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) {
// received others messages
})
For me, clicking on the event does not go to the correct url. So i add this:
// background client - service worker
const channel = new BroadcastChannel('sw-messages');
self.addEventListener('notificationclick', function (event) {
console.debug('SW notification click event', event)
const url = event.notification?.data?.FCM_MSG?.data?.link;
channel.postMessage({
type: 'notification_clicked',
data: {
title: event.notification.title,
clickAction: url
}
});
})
// foreground client
const channel = new BroadcastChannel('sw-messages');
channel.addEventListener("message", function (event) {
// go the page
})
I hope this helps someone.
This question and other answers seems to be related to the legacy FCM API, not the v1.
In those case, I needed the SW to open any url sent by FCM, which is by default not possible because host differs (see here).
Also, the notification object as changed, and the url for the webpush config is there now: event.notification.data.FCM_MSG.notification.click_action
So adapting others answers to get the correct field and open the url by only editing the firebase-messaging-sw.js:
importScripts('https://www.gstatic.com/firebasejs/8.2.10/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.10/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in
// your app's Firebase config object.
// https://firebase.google.com/docs/web/setup#config-object
firebase.initializeApp({
...
})
self.addEventListener('notificationclick', function(event) {
event.notification.close();
// fcp_options.link field from the FCM backend service goes there, but as the host differ, it not handled by Firebase JS Client sdk, so custom handling
if (event.notification && event.notification.data && event.notification.data.FCM_MSG && event.notification.data.FCM_MSG.notification) {
const url = event.notification.data.FCM_MSG.notification.click_action;
event.waitUntil(
self.clients.matchAll({type: 'window'}).then( windowClients => {
// Check if there is already a window/tab open with the target URL
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
// If so, just focus it.
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
// If not, then open the target URL in a new window/tab.
if (self.clients.openWindow) {
console.log("open window")
return self.clients.openWindow(url);
}
})
)
}
}, false);
const messaging = firebase.messaging();
(register the addEventListener before initializing messaging)
Just add addeventlistner notification click event before calling firebase.messaging()
Everything will work fine.
importScripts('https://www.gstatic.com/firebasejs/8.4.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.4.1/firebase-messaging.js');
self.onnotificationclick = function(event) {
console.log('On notification click: ', event.notification.tag);
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.waitUntil(clients.matchAll({
type: "window"
}).then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == '/index' && 'focus' in client)
return client.focus();
}
if (clients.openWindow)
return clients.openWindow('/index');
}));
};
var firebaseConfig = {
apiKey: "xcxcxcxcxcxc",
authDomain: "xcxcxc.firebaseapp.com",
projectId: "fdsfdsdfdf",
storageBucket: "dfsdfs",
messagingSenderId: "sdfsdfsdf",
appId: "sdfsdfsdfsdfsdfsdf"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();
I am moving from sipjs 0.7x to sipjs 0.11
After reading the Git issue https://github.com/onsip/SIP.js/pull/426#issuecomment-312065734
and
https://sipjs.com/api/0.8.0/sessionDescriptionHandler/
I have found that the ice options (coturn, turn, stun) is not in User Agent anymore,
but the problem is that I am not quite understand where should I use the
setDescription(sessionDescription, options, modifiers)
I have seen that the ice is set in options, using
options.peerConnectionOptions.rtcConfiguration.iceServers
below is what I haved tried
session.on('trackAdded', function () {
// We need to check the peer connection to determine which track was added
var modifierArray = [
SIP.WebRTC.Modifiers.stripTcpCandidates,
SIP.WebRTC.Modifiers.stripG722,
SIP.WebRTC.Modifiers.stripTelephoneEvent
];
var options = {
peerConnectionOptions:{
rtcConfiguration:{
iceServers : {
[{urls: 'turn:35.227.67.199:3478',
username: 'leon',
credential: 'leon_pass'}]
}
}
}
}
session.setDescription('trackAdded', options,modifierArray);
var pc = session.sessionDescriptionHandler.peerConnection;
// Gets remote tracks
var remoteStream = new MediaStream();
pc.getReceivers().forEach(function (receiver) {
remoteStream.addTrack(receiver.track);
});
remoteAudio.srcObject = remoteStream;
remoteAudio.play();
// Gets local tracks
// var localStream = new MediaStream();
// pc.getSenders().forEach(function(sender) {
// localStream.addTrack(sender.track);
// });
// localVideo.srcObject = localStream;
// localVideo.play();
});
}
I have tried this and it seems that the traffic is not going to the coturn server.
I have used Trickle Ice "https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/" to test and it is fine, but I have found there is not traffic going through the coturn server. You could also use this one and I do not mind.
There is even no demo on the official website to show how could we use the setDescription(sessionDescription, options, modifiers). In this case can I please ask some recommendations?
Configure STUN/TURN servers in the parameters passed to new UserAgent.
Here is sample, it seems to be working on v0.17.1:
const userAgentOptions = {
...
sessionDescriptionHandlerFactoryOptions: {
peerConnectionConfiguration: {
iceServers: [{
urls: "stun:stun.l.google.com:19302"
}, {
urls: "turn:TURN_SERVER_HOST:PORT",
username: "USERNAME",
credential: "PASSWORD"
}]
},
},
...
};
const userAgent = new SIP.UserAgent(userAgentOptions);
When using SimpleUser - pass it inside SimpleUserOptions:
const simpleUser = new Web.SimpleUser(url, { userAgentOptions })
Has anyone tried using Stomp protocol over the WebSocket implementation in react native? We are using Stomp for the web application, and it would be great if I did not have build a separate back end for the web and mobile applications.
I haven't found a good way to integrate Stomp with the react native Web Sockets.
I have wrapped the latest typescript / js #stomp/stompjs STOMP client into a React HOC, making use of SockJS library to simulate the websocket. feel free to check it out
var connected =false;
var socket ='';
var stompClient = '';
const send = (param)=> {
let send_message =param;
if (stompClient && stompClient.connected) {
const msg = { name: send_message };
stompClient.send("/app/hello", JSON.stringify(msg), {});
}
}
const connect =()=> {
socket = new SockJS("your endpoint");
stompClient = Stomp.over(socket);
stompClient.connect(
{},
frame => {
connected = true;
stompClient.subscribe("/topic/greetings", tick => {
});
},
error => {
console.log(error);
connected = false;
}
);
}
const disconnect =()=> {
if (stompClient) {
stompClient.disconnect();
}
connected = false;
}
const tickleConnection =()=> {
connected ? disconnect() : connect();
}
I'm facing a problem where createAnswer() is not being called.
I'm looking for a way where a new user connects to the website and all other peers get an offer from the new peer and should create an answer. Where is my problem?
function onMessage (topic, event) {
console.log(event);
if(event.session_id == session._session_id)
return;
if(event.sdp) {
console.log(event.sdp.type);
if(event.type == "offer"){
pc.setRemoteDescription(new RTCSessionDescription(event.sdp));
pc.createAnswer(function(offer) {
debugger;
pc.setLocalDescription(offer);
session.publish("http://example.com/webrtc", { session_id: session._session_id ,sdp: offer, type: offer.type});
});
}
}
if(event.candidate)
pc.addIceCandidate(new RTCIceCandidate(event.candidate));
}
For the signalling channel I use Web-sockets an the wamp protocol.
I have deployed the application here,
the source code is available here.