How to implement APNS notifications through nodejs? - node.js

Does someone now a good npm module to implement Apple PUSH notifications?
A simple example would be great.
The solution I've found is the following which uses the apn module.
var apn = require('apn');
var ca = ['entrust_2048_ca.cer'];
/* Connection Options */
var options = {
cert: 'path to yuour cert.pem',
key: 'path to your key.pem',
ca: ca,
passphrase: 'your passphrase',
production: true,
connectionTimeout: 10000
};
var apnConnection = new apn.Connection(options);
/* Device */
var deviceToken = 'your device token';
var myDevice = new apn.Device(deviceToken);
/* Notification */
var note = new apn.Notification();
note.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires 1 hour from now.
note.badge = 3;
note.payload = {'message': 'hi there'};
apnConnection.pushNotification(note, myDevice);
If you need to use the APNS feedback service to avoid to send notifications to devices which cannot receive it (application uninstalled) you can add the following:
/* Feedback Options */
var feedbackOptions = {
cert: 'path to yuour cert.pem',
key: 'path to your key.pem',
ca: ca,
passphrase: 'your passphrase',
production: true,
interval: 10
};
var feedback = new apn.feedback(feedbackOptions);
feedback.on('feedback', handleFeedback);
feedback.on('feedbackError', console.error);
function handleFeedback(feedbackData) {
var time, device;
for(var i in feedbackData) {
time = feedbackData[i].time;
device = feedbackData[i].device;
console.log("Device: " + device.toString('hex') + " has been unreachable, since: " + time);
}
}
To handle the different events connected to the connection you can use the following:
apnConnection.on('connected', function(openSockets) {
//
});
apnConnection.on('error', function(error) {
//
});
apnConnection.on('transmitted', function(notification, device) {
//
});
apnConnection.on('transmissionError', function(errCode, notification, device) {
//
});
apnConnection.on('drain', function () {
//
});
apnConnection.on('timeout', function () {
//
});
apnConnection.on('disconnected', function(openSockets) {
//
});
apnConnection.on('socketError', console.error);

Related

Nodejs - Express - Socket.io How to send a message to a specific socket.id from an outside function?

I'm having problem finding the right answer for this. I'm trying to send a message to a specific socket.id given I'm handling multiple users but I need to do it from another function which does not have access to socket.io.
I need to send the message to the specific socket.id inside the function:
var authorizePublish = function(client, topic, payload, callback) {
//here
}
socketLib.js
/// Import Modules ///
const mosca = require('mosca');
const DeviceService = require('../services/device.service');
const config = require('../config');
const util = require('../modules/util.js');
module.exports = async function(httpServer, sessionParser) {
var io = require("socket.io")(httpServer); // For Sockets
io.use(function(socket, next) {
sessionParser(socket.request, socket.request.res, next);
});
io.sockets.on('connection', function(socket) {
const userSessionId = socket.request.session.userId;
const userId = socket.request.session.passport.user;
const socketId = socket.request.session.id;
if (userSessionId == '')
console.log('client connected');
console.log(`Client connected: ${userSessionId}, with userid: ${userId}, with socket: ${socketId} conectado`);
socket.on('msg:message', async function (data) {
socket.emit('message', data);
});
socket.on('disconnect', function(data) {
console.log('user disconnected');
});
});
/// Mosca Settings ///
var moscaServer = null;
var moscaSettings = {
interfaces: [ { type: "mqtt", port: 1884 }, { type: "http", port: 5000, bundle: true, static: './' }
],};
var debug = util.isDebug(),
isAuth = util.isAuth()
//// Mosca Server ////
var dbHost = config.dbHost;
moscaServer = new mosca.Server(moscaSettings);
moscaServer.on('ready', setup);
var authenticate = function(client, username, callback) {
console.log('-------- Authenticating MQTT user... --------');
callback(null, flag);
if(authenticate) client.user = username;
}
/// Mosca Events ///
moscaServer.on('published', function (packet, client) {
var arr = packet.topic.split('/');
if (arr.length !== 3) { return; }
});
/// Mosca Functions ///
var authorizePublish = function(client, topic, payload, callback) {
if (client.user == topic.split('/')[1]) {
// socket.emit('message', payload.toString()); (Here is where I need to get access to the client socket.id in order to send him a message with the payload.
callback(null, true);
}
else {
callback(null, false);
}
}
function setup() {
if (isAuth) {
moscaServer.authenticate = authenticate;
}
if (config.authPub === true) {
moscaServer.authorizePublish = authorizePublish;
}
if(config.authSubs == true) {
moscaServer.authorizeSubscribe = authorizeSubscribe;
}
console.log('Mosca server is up and running')
}
}
First off, the way you send to a specific socket id is with this:
io.to(id).emit(msg, data);
where id is the socket.id of the socket.io connection that you want to send to.
Or, you could put that into a function:
function sendToId(id, msg, data) {
io.to(id).emit(msg, data);
}
So, secondly to be able to do that from anywhere you have a couple options:
You can import the io instance and use it with the above line of code.
You can export a function called something like sendToId() from a module that does have access to the io instance and then you can import that function in order to be able to use it.
You can assign the io instance to a known object so that anyone with access to that more popularly known object can then retrieve it from there. An example of that is to set it as a properly on the Express app object as in app.set("io", io) and then anyone with access to the app object can you let io = app.get("io"); to get it and use it.

Create webrtc video, voice call and file transfer using node js step by step Q-A

If any one need to add information or edit some information they are welcome.
Hi,
First of all this questions main target is shear my experience to other developers who finding good tutorials about webrtc.i'm not going to explain about webrtc. In this i add scourse code that working webrtc Video, voice call and file transfer example tested by me.
I get webrtc information from https://webrtc.org/ and get nodejs
from https://nodejs.org/en/
Ok let get start
Is web rtc need ssl certificated?
If you doing experiment in you local PC server no need. but when you add the to live server Yes you need it.
How i get ssl certificate?
one of my friend help me to get that SSl. there plenty of tutorials for you read and watch
How i get turn and stun server?
if you go production level you need to setup those server , but for test your project you can get stun server and turn servers for free.
For Stun server - https://gist.github.com/zziuni/3741933
For Turn server - Use this link and create free one ( http://numb.viagenie.ca/ ).
I add my working code as a an answer below
This is Working code for webrtc with node js
This code and comment that in code not by me. They already there when i got the code. I cant find code original owner. but i thanks that developer.If some found that developer please edit this and add that developer link :)
var express = require('express');
var socket = require('socket.io');
var app = express();
var fs = require('fs');
var https = require('https');
// link your https certicate path
var options = {
key: fs.readFileSync('/../../etc/ssl/private/apache-selfsigned.key'),
cert: fs.readFileSync('/../../etc/ssl/certs/apache-selfsigned.crt')
};
var main = https.createServer(options, app);
var server = main.listen(8443, function() {
console.log('server up and running at %s port', 8443);
});
/*var server = app.listen(443, function () {
});*/
app.use(express.static('public'));
var io = socket(server);
/*************************/
/*** INTERESTING STUFF ***/
/*************************/
var channels = {};
var sockets = {};
/**
* Users will connect to the signaling server, after which they'll issue a "join"
* to join a particular channel. The signaling server keeps track of all sockets
* who are in a channel, and on join will send out 'addPeer' events to each pair
* of users in a channel. When clients receive the 'addPeer' even they'll begin
* setting up an RTCPeerConnection with one another. During this process they'll
* need to relay ICECandidate information to one another, as well as SessionDescription
* information. After all of that happens, they'll finally be able to complete
* the peer connection and will be streaming audio/video between eachother.
*/
io.on('connection', function (socket) {
var channel;
socket.channels = {};
sockets[socket.id] = socket;
console.log("[" + socket.id + "] connection accepted");
socket.on('disconnect', function () {
for (var channel in socket.channels) {
part(channel);
}
console.log("[" + socket.id + "] disconnected");
delete sockets[socket.id];
});
socket.on('join-room', function (config) {
if (config) {
channel = config.channel;
var userdata = config.userdata;
var userID = config.userdata.userID;
if (channel in socket.channels) {
console.log("[" + socket.id + "] ERROR: already joined ", channel);
return;
}
if (!(channel in channels)) {
channels[channel] = {};
}
for (id in channels[channel]) {
channels[channel][id].emit('addPeer-room', {'peer_id': socket.id, 'should_create_offer': false});
socket.emit('addPeer-room', {'peer_id': id, 'should_create_offer': true});
console.log("what is this id -> ", id);
}
console.log(config.userdata.name, ' joining room', config.channel);
socket.join(config.channel);
socket.broadcast.in(config.channel).emit('room-users', config);
channels[channel][socket.id] = socket;
socket.channels[channel] = channel;
}
});
function part(channel) {
console.log("[" + socket.id + "] part ");
if (!(channel in socket.channels)) {
console.log("[" + socket.id + "] ERROR: not in ", channel);
return;
}
delete socket.channels[channel];
delete channels[channel][socket.id];
for (id in channels[channel]) {
channels[channel][id].emit('removePeer', {'peer_id': socket.id});
socket.emit('removePeer', {'peer_id': id});
}
}
socket.on('part', part);
socket.on('relayICECandidate-room', function (config) {
var peer_id = config.peer_id;
var ice_candidate = config.ice_candidate;
console.log("[" + socket.id + "] relaying ICE candidate to [" + peer_id + "] ", ice_candidate);
if (peer_id in sockets) {
sockets[peer_id].emit('iceCandidate-room', {'peer_id': socket.id, 'ice_candidate': ice_candidate});
}
});
socket.on('relaySessionDescription-room', function (config) {
var peer_id = config.peer_id;
var session_description = config.session_description;
console.log("[" + socket.id + "] relaying session description to [" + peer_id + "] ", session_description);
if (peer_id in sockets) {
sockets[peer_id].emit('sessionDescription-room', {
'peer_id': socket.id,
'session_description': session_description
});
}
});
// this for file transfer
socket.on('file-send-room', function (file) {
console.log(file);
socket.to(channel).emit('file-out-room', file);
});
socket.on('file-send-room-result', function (file) {
console.log(file);
socket.to(channel).emit('file-out-room-result', file);
});
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
<script>
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
var fileInput = document.querySelector('input#fileInput');
var downloadAnchor = document.querySelector('a#download');
// this function use to get url parameters
var room = getParameterByName('room');
var userID = getParameterByName('userid');
var name = getParameterByName('name');
/** CONFIG **/
var SIGNALING_SERVER = "https://xxx.xx.xx.xxx:8443"; //your node server addres or IP adress
var USE_AUDIO = true;
var USE_VIDEO = true;
var MUTE_AUDIO_BY_DEFAULT = false;
/** You should probably use a different stun server doing commercial stuff **/
/** Also see: https://gist.github.com/zziuni/3741933 **/
var ICE_SERVERS = [
{urls: "stun:stun.l.google.com:19302"},{
urls: 'turn:numb.viagenie.ca:3478',
credential: '12344', //your password
username: 'your#email.com'
}
];
var socket = null;
/* our socket.io connection to our webserver */
var local_media_stream = null;
/* our own microphone / webcam */
var peers = {};
/* keep track of our peer connections, indexed by peer_id (aka socket.io id) */
var peer_media_elements = {};
/* keep track of our <video>/<audio> tags, indexed by peer_id */
$(document).ready(function (a) {
socket = io(SIGNALING_SERVER);
socket = io();
//----------------------------------------------------------------------->>>>> Files Send Start
const BYTES_PER_CHUNK = 1200;
var file;
var currentChunk;
var fileInput = $('input[type=file]');
var fileReader = new FileReader();
function readNextChunk() {
var start = BYTES_PER_CHUNK * currentChunk;
var end = Math.min(file.size, start + BYTES_PER_CHUNK);
fileReader.readAsArrayBuffer(file.slice(start, end));
}
fileReader.onload = function () {
socket.emit('file-send-room-result', fileReader.result);
//p2pConnection.send( fileReader.result );
currentChunk++;
if (BYTES_PER_CHUNK * currentChunk < file.size) {
readNextChunk();
}
};
fileInput.on('change', function () {
file = fileInput[0].files[0];
currentChunk = 0;
// send some metadata about our file
// to the receiver
socket.emit('file-send-room', JSON.stringify({
fileName: file.name,
fileSize: file.size
}));
readNextChunk();
});
var incomingFileInfo;
var incomingFileData;
var bytesReceived;
var downloadInProgress = false;
socket.on('file-out-room', function (data) {
startDownload(data);
console.log(data);
});
socket.on('file-out-room-result', function (data) {
progressDownload(data);
console.log(data); });
function startDownload(data) {
incomingFileInfo = JSON.parse(data.toString());
incomingFileData = [];
bytesReceived = 0;
downloadInProgress = true;
console.log('incoming file <b>' + incomingFileInfo.fileName + '</b> of ' + incomingFileInfo.fileSize + ' bytes');
}
function progressDownload(data) {
bytesReceived += data.byteLength;
incomingFileData.push(data);
console.log('progress: ' + ((bytesReceived / incomingFileInfo.fileSize ) * 100).toFixed(2) + '%');
if (bytesReceived === incomingFileInfo.fileSize) {
endDownload();
}
}
function endDownload() {
downloadInProgress = false;
var blob = new Blob(incomingFileData);
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
var blob = new Blob(incomingFileData);
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = incomingFileInfo.fileName;
a.click();
window.URL.revokeObjectURL(url);
}
//==================================================================<<< Filse Send End
//------------------------ Funtion
function join_chat_channel(channel, userdata) {
socket.emit('join-room', {"channel": channel, "userdata": userdata});
}
socket.on('connect', function (userID) {
console.log("Connected to signaling server");
setup_local_media(function () {
/* once the user has given us access to their
* microphone/camcorder, join the channel and start peering up */
join_chat_channel(room, {'name': name, 'userID': userID});
});
});
socket.on('room-user', function (data) {
console.log(data);
$("#online-user").append('<tr><td>Name = ' + data.userdata.name + ' <br> User ID= ' + data.userdata.userID + '</td><td><button class="call" id="' + data.userdata.userID + '">Call</button></td></tr>');
});
$('body').on('click', '.call', function () {
var callerID = $(this).attr('id');
socket.emit('call', {"callToId": callerID, "callFromId": userID});
});
/**
* When we join a group, our signaling server will send out 'addPeer' events to each pair
* of users in the group (creating a fully-connected graph of users, ie if there are 6 people
* in the channel you will connect directly to the other 5, so there will be a total of 15
* connections in the network).
*/
socket.on('addPeer-room', function (config) {
console.log('Signaling server said to add peer:', config);
var peer_id = config.peer_id;
if (peer_id in peers) {
/* This could happen if the user joins multiple channels where the other peer is also in. */
console.log("Already connected to peer ", peer_id);
return;
}
var peer_connection = new RTCPeerConnection(
{"iceServers": ICE_SERVERS},
{"optional": [{"DtlsSrtpKeyAgreement": true}]} /* this will no longer be needed by chrome
* eventually (supposedly), but is necessary
* for now to get firefox to talk to chrome */
);
peers[peer_id] = peer_connection;
peer_connection.onicecandidate = function (event) {
if (event.candidate) {
socket.emit('relayICECandidate-room', {
'peer_id': peer_id,
'ice_candidate': {
'sdpMLineIndex': event.candidate.sdpMLineIndex,
'candidate': event.candidate.candidate
}
});
}
}
peer_connection.onaddstream = function (event) {
console.log("onAddStream", event);
var remote_media = USE_VIDEO ? $("<video>") : $("<audio>");
remote_media.attr("autoplay", "autoplay");
if (MUTE_AUDIO_BY_DEFAULT) {
remote_media.attr("muted", "true");
}
remote_media.attr("controls", "");
peer_media_elements[peer_id] = remote_media;
$('body').append(remote_media);
attachMediaStream(remote_media[0], event.stream);
}
/* Add our local stream */
peer_connection.addStream(local_media_stream);
/* Only one side of the peer connection should create the
* offer, the signaling server picks one to be the offerer.
* The other user will get a 'sessionDescription' event and will
* create an offer, then send back an answer 'sessionDescription' to us
*/
if (config.should_create_offer) {
console.log("Creating RTC offer to ", peer_id);
peer_connection.createOffer(
function (local_description) {
console.log("Local offer description is: ", local_description);
peer_connection.setLocalDescription(local_description,
function () {
socket.emit('relaySessionDescription-room',
{'peer_id': peer_id, 'session_description': local_description});
console.log("Offer setLocalDescription succeeded");
},
function () {
Alert("Offer setLocalDescription failed!");
}
);
},
function (error) {
console.log("Error sending offer: ", error);
});
}
});
/**
* Peers exchange session descriptions which contains information
* about their audio / video settings and that sort of stuff. First
* the 'offerer' sends a description to the 'answerer' (with type
* "offer"), then the answerer sends one back (with type "answer").
*/
socket.on('sessionDescription-room', function (config) {
console.log('Remote description received: ', config);
var peer_id = config.peer_id;
var peer = peers[peer_id];
var remote_description = config.session_description;
console.log(config.session_description);
var desc = new RTCSessionDescription(remote_description);
var stuff = peer.setRemoteDescription(desc,
function () {
console.log("setRemoteDescription succeeded");
if (remote_description.type == "offer") {
console.log("Creating answer");
peer.createAnswer(
function (local_description) {
console.log("Answer description is: ", local_description);
peer.setLocalDescription(local_description,
function () {
socket.emit('relaySessionDescription-room',
{'peer_id': peer_id, 'session_description': local_description});
console.log("Answer setLocalDescription succeeded");
},
function () {
Alert("Answer setLocalDescription failed!");
}
);
},
function (error) {
console.log("Error creating answer: ", error);
console.log(peer);
});
}
},
function (error) {
console.log("setRemoteDescription error: ", error);
}
);
console.log("Description Object: ", desc);
});
/**
* The offerer will send a number of ICE Candidate blobs to the answerer so they
* can begin trying to find the best path to one another on the net.
*/
socket.on('iceCandidate-room', function (config) {
var peer = peers[config.peer_id];
var ice_candidate = config.ice_candidate;
peer.addIceCandidate(new RTCIceCandidate(ice_candidate));
});
/**
* When a user leaves a channel (or is disconnected from the
* signaling server) everyone will recieve a 'removePeer' message
* telling them to trash the media channels they have open for those
* that peer. If it was this client that left a channel, they'll also
* receive the removePeers. If this client was disconnected, they
* wont receive removePeers, but rather the
* signaling_socket.on('disconnect') code will kick in and tear down
* all the peer sessions.
*/
socket.on('removePeer-room', function (config) {
console.log('Signaling server said to remove peer:', config);
var peer_id = config.peer_id;
if (peer_id in peer_media_elements) {
peer_media_elements[peer_id].remove();
}
if (peer_id in peers) {
peers[peer_id].close();
}
delete peers[peer_id];
delete peer_media_elements[config.peer_id];
});
});
function setup_local_media(callback, errorback) {
if (local_media_stream != null) { /* ie, if we've already been initialized */
if (callback) callback();
return;
}
/* Ask user for permission to use the computers microphone and/or camera,
* attach it to an <audio> or <video> tag if they give us access. */
console.log("Requesting access to local audio / video inputs");
navigator.getUserMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
attachMediaStream = function (element, stream) {
console.log('DEPRECATED, attachMediaStream will soon be removed.');
element.srcObject = stream;
};
navigator.getUserMedia({"audio": USE_AUDIO, "video": USE_VIDEO},
function (stream) { /* user accepted access to a/v */
console.log("Access granted to audio/video");
local_media_stream = stream;
var local_media = USE_VIDEO ? $("<video>") : $("<audio>");
local_media.attr("autoplay", "autoplay");
local_media.attr("muted", "true");
/* always mute ourselves by default */
local_media.attr("controls", "");
$('body').append(local_media);
attachMediaStream(local_media[0], stream);
if (callback) callback();
},
function () { /* user denied access to a/v */
console.log("Access denied for audio/video");
alert("You chose not to provide access to the camera/microphone, demo will not work.");
if (errorback) errorback();
});
}
</script>
</head>
<body>
<form id="fileInfo">
<input type="file" id="fileInput" name="files"/>
</form>
<a id="download"></a>
</body>
</html>

how to send apns notification or Message to specific user in same channel using LRedis in laravel 5.4 and socket.io at nodejs?

I'm creating Chat app in Laravel 5.4 using socket.io at client side also sending APNS notification to a target user.
While doing this when user send Message to target user that Message send more than once means It depends upon number of connections connected with node socket.io server which I'm thinking it's mean's My channel ('messages') looping that message to connected number of clients so it's showing more than once at receiver side.
Here My Sender Side (Laravel):
$redis = LRedis::connection();
$data = [
'message' => $msg,
'user' => $auth,
'music'=>$music,
'target_user'=>$target_user
];
$redis->publish('message', json_encode($data));
At socket.js:
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var redis = require('redis');
var apn = require('apn');
var request = require('request');
var auth_user_id = null;
var mysql = require('mysql');
var con = mysql.createConnection({
host: "localhost",
user: "root",
password: "",
database: "database"
});
con.connect(function(err) {
if (err) throw err;
console.log("Database Connected!");
});
var options = {
token: {
key: "app-keys/APNsAuthKey_U3QV9H86BR.p8",
keyId: "keyId",
teamId: "teamid",
cert: "app-keys/musicChatAppDevelopment.pem",
},
development: true,
production: false,
};
var apnProvider = new apn.Provider(options);
server.listen(8890);
io.on('connection', (socket) => {
var auth_user_id = socket.handshake.query.auth_user_id;
console.log("client connected");
console.log("Client Socket id--"+socket.id+"-----User Id----"+auth_user_id);
var redisClient = redis.createClient();
redisClient.subscribe('message');
if(auth_user_id>0) {
var sql = "UPDATE users SET socket_id ='"+socket.id+"' WHERE id ="+auth_user_id;
con.query(sql, function (err, result) {
if (err) throw err;
console.log(result.affectedRows + " record(s) updated");
});
}
redisClient.on('message', (channel, data) => {
console.log(channel);
let data_= JSON.parse(data);
let message_ = data_.message;
let deviceToken = data_.target_user['ios_token'];
if(data_.user['id']!=data_.target_user['id']) {
console.log(data_.target_user['ios_token']);
var note = new apn.Notification();
note.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires 1 hour from now.
note.badge = 0;
note.sound = "ping.aiff";
note.alert = {
"title" : data_.user['first_name'],
"body" : message_,
};
note.payload = { "aps" : {
"alert" : {
"title" : data_.user['first_name'],
"body" : message_
}
}
}
note.topic = "com.charpixel.hellodemo";
console.log(note);
let socketId = data_.target_user['socket_id'];
apnProvider.send(note, deviceToken).then( (result) => {
console.log(JSON.stringify(result));
});
console.log("Target User Socket id "+data_.target_user['socket_id']);
socket.broadcast(data_.target_user['socket_id']).emit(channel, data);
io.to(socketId).emit(channel,data);
}
});
socket.on('disconnect', () => {
redisClient.quit();
});
});
At index.blade.php:
var ip = '<?php echo config('app.current_ip'); ?>:8890?auth_user_id='+JSON.parse(user_id)['id'];
console.log("Curent_URL"+ip);
var socket = io.connect(ip);
socket.on('message', function (data) {
});
Also getting apns notification multiple times on my IOS App.
For 1-1 chat you should create separate channel for each chat. Your code sends message on channel 'message' which is subscribed by every client. So there are multiple problems here. For example, if you have 3 clients:
redisClient.on('message') will be triggered for all 3 clients.
As the data remains same (which means device token remains same), no matter for which client on('message) was triggered, the notification will be sent to the same receiver and that's the reason why you are receiving multiple notifications.
The solution is that you should create a unique channel for each chat. See this stackoverflow answer as an example. Its not using redis but you can change it to work with reddis too.

WebRTC Video sharing app doesn't work

I have been trying to share a video stream between two clients over WebRTC for about a week now and I have no idea how to proceed any further. I'm frustrated and could really use some help from the more experienced. Please help me get this running.
I am using Websockets and NodeJS. I will post all of my code below:
Server Code ( on NodeJS )
"use strict";
/** Requires **/
var webSocketServer = require('websocket').server,
expr = require("express"),
xpress = expr(),
server = require('http').createServer(xpress);
// Configure express
xpress.configure(function() {
xpress.use(expr.static(__dirname + "/public"));
xpress.set("view options", {layout: false});
});
// Handle GET requests to root directory
xpress.get('/', function(req, res) {
res.sendfile(__dirname + '/public/index.html');
});
// WebSocket Server
var wsServer = new webSocketServer({
httpServer: server
});
// Set up the http server
server.listen(8000, function(err) {
if(!err) { console.log("Listening on port 8000"); }
});
var clients = [ ];
/** On connection established */
wsServer.on('request', function(request) {
// Accept connection - you should check 'request.origin' to make sure that client is connecting from your website
var connection = request.accept(null, request.origin);
var self = this;
// We need to know client index to remove them on 'close' event
var index = clients.push(connection) - 1;
// Event Listener for when Clients send a message to the Server
connection.on('message', function(message) {
var parsedMessage = JSON.parse(message.utf8Data);
if ( parsedMessage.kind == 'senderDescription' ) {
wsServer.broadcastUTF(JSON.stringify({ kind:'callersDescription', callerData: parsedMessage }));
}
});
});
Index.html loads and immediately runs VideoChatApp.js
function VideoChatApp() {
this.connection = null;
this.runConnection();
}
_p = VideoChatApp.prototype;
/** Initialize the connection and sets up the event listeners **/
_p.runConnection = function(){
// To allow event listeners to have access to the correct scope
var self = this;
// if user is running mozilla then use it's built-in WebSocket
window.WebSocket = window.WebSocket || window.MozWebSocket;
// if browser doesn't support WebSocket, just show some notification and exit
if (!window.WebSocket) { return; }
/** Where to make the connection **/
var host = location.origin.replace(/^http/, 'ws');
console.log(host);
this.connection = new WebSocket(host);
/** Once the connection is established **/
this.connection.onopen = function () {
console.log("Web Socket Connection Established");
self.onConnectionEstablished();
};
/** If there was a problem with the connection */
this.connection.onerror = function (error) {
console.log("ERROR with the connection *sadface*");
};
}; // end runConnection
_p.onConnectionEstablished = function() {
// My connection to the nodejs server
var websocketConnection = this.connection;
// Some local variables for use later
var mediaConstraints = {
optional: [],
mandatory: {
OfferToReceiveVideo: true
}
};
var offerer, answerer;
this.theLocalStream = null;
var amITheCaller = false;
var localVideoTag = document.getElementById('localVideoTag');
var remoteVideoTag = document.getElementById('remoteVideoTag');
window.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
window.RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
window.RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
window.URL = window.webkitURL || window.URL;
window.iceServers = {
iceServers: [{
url: 'stun:23.21.150.121'
}]
};
var callButton = document.getElementById("callButton");
callButton.onclick = callClicked;
function callClicked() {
amITheCaller = true;
setUpOffer();
}
offerer = new RTCPeerConnection(window.iceServers);
answerer = new RTCPeerConnection(window.iceServers);
/** Start Here - Set up my local stream **/
getUserMedia(function (stream) {
hookUpLocalStream(stream);
});
function getUserMedia(callback) {
navigator.getUserMedia({
video: true
}, callback, onerror);
function onerror(e) {
console.error(e);
}
}
function hookUpLocalStream(localStream) {
this.theLocalStream = localStream;
callButton.disabled = false;
localVideoTag.src = URL.createObjectURL(localStream);
localVideoTag.play();
};
/* When you click call, then we come here. Here I want to set up the offer and send it. */
function setUpOffer() {
var stream = theLocalStream;
offerer.addStream(stream);
offerer.onaddstream = function (event) {
console.log("onaddstream callback was called");
};
offerer.onicecandidate = function (event) {
if (!event || !event.candidate) return;
answerer.addIceCandidate(event.candidate);
};
offerer.createOffer(function (offer) {
offerer.setLocalDescription(offer);
console.log("------------------- What I am sending: -------------------------");
console.log(offer);
console.log(stream);
console.log("-----------------------------------------------------------------\n");
var jsonMsg = JSON.stringify( {kind:'senderDescription', streamInfo: offer, theStream: stream} );
websocketConnection.send( jsonMsg );
//answererPeer(offer, stream);
}, onSdpError, mediaConstraints);
}
/* Respond to a call */
function answererPeer(offer, stream) {
answerer.addStream(stream);
answerer.onaddstream = function (event) {
remoteVideoTag.src = URL.createObjectURL(event.stream);
remoteVideoTag.play();
};
answerer.onicecandidate = function (event) {
if (!event || !event.candidate) return;
offerer.addIceCandidate(event.candidate);
};
answerer.setRemoteDescription(offer, onSdpSucces, onSdpError);
answerer.createAnswer(function (answer) {
answerer.setLocalDescription(answer);
offerer.setRemoteDescription(answer, onSdpSucces, onSdpError);
}, onSdpError, mediaConstraints);
}
function onSdpError(e) {
console.error('onSdpError', e);
}
function onSdpSucces() {
console.log('onSdpSucces');
}
websocketConnection.onmessage = function (messageFromServer) {
console.log(" ------------------------ Message from server: -------------------- ");
var parsedMessage = JSON.parse(messageFromServer.data);
if(parsedMessage.callerData.kind = "senderDescription") {
console.log("Received a senderDescription");
console.log(parsedMessage.callerData.streamInfo);
console.log(parsedMessage.callerData.theStream);
console.log("-------------------------------------------------------------------\n");
answererPeer(parsedMessage.callerData.streamInfo, parsedMessage.callerData.theStream);
}
};
};// end onConnectionEstablished()
Finally, here are my errors:
I am not sure if this is still interesting for you, but I have some very good experience with WebRTC using PeerJS as a wrapper around it. It takes care of all the stuff you don't want to do (http://peerjs.com/). There is the client library as well as a very nice signaling server for nodejs (https://github.com/peers/peerjs-server). You can easy extend this server in your own node server.
This may not explain why your approach failed, but gets WebRTC running easily.
You can start with code that is already working and completely open. Check out easyrtc.com we have a client api, signalling server and working code. And if you have problems with that code ask us for help on Google Groups for easyrtc.

Increasing max_frame rate in amqp.js -- Hitting limits in buffer copy

I have a situation where I have about 50 listeners on 50 'direct' exchanges. The client and the server are in javascript (node.js) . It is using the node-amqp from postwait .
Things work fairly well at low frequency of messages. Once the message frequency increases ~ 5000 messages per minute then there is a buffer copy error being shown in amqp.js
From what I could trace the max_frame_size in amqp.js is fixed to 131072 .
I just tried to double the value from 128k to 256k . But doing so causes the node.js to silently fail without starting up. There is no error message. I am assuming that I also have to change the corresponding value (max_frame) in the rabbit.config file.
Do I have to do anything else to increase this value . Any other suggestions will also be appreciated.
I have attached the minimal code to simulate the error . Run the commands below in 2 windows to simulate the error
node engine-so-client.js -c 200 -p 12000
node server-so.js
File server-so.js
var util= require('util')
var amqp = require('amqp');
var express = require ('express')
function httpServer(exchange) {
console.log("In httpServer start %s",exchange.name);
var port;
app = express.createServer();
app.get('/message/:routingKey/:message',function(req,res) {
exchange.publish(req.params.routingKey,{'d' : req.params.message});
res.send('Published the message '+req.params.message+'\n');
});
app.get('/register/:socket/:routingKey',function(req,res) {
var queue1 = conn.queue('',
{autoDelete: false, durable: true, exclusive: true},
function() {
console.log("Queue1 Callback");
queue1.subscribe(
function(message) {
console.log("subscribe Callback for "+req.params.routingKey + " " + message.d);
});
console.log("Queue Callback Binding with "+req.params.routingKey);
queue1.bind(exchange.name,req.params.routingKey);
});
res.send('Started the socket at '+req.params.socket+'\n');
});
app.listen(3000);
app.use(express.logger());
console.log('Started server on port %s', app.address().port);
}
function setup() {
console.log("Setup");
var exchange = conn.exchange('cf2-demo',
{'type': 'direct', durable: false}, function() {
var queue = conn.queue('',
{autoDelete: false, durable: true, exclusive: true},
function() {
console.log("Queue Callback Binding with test key");
queue.bind(exchange.name,'testKey');
});
queue.on('queueBindOk',
function() { httpServer(exchange); });
});
console.log("Completed setup %s", exchange.name);
}
var conn = amqp.createConnection({host:'localhost',
login:'guest',
password:'guest'},
{defaultExchangeName: "cf2-demo"});
conn.on('ready',setup);
File engine-so-client.js
var program = require('commander.js');
var util = require('util');
var http = require('http');
program
.version('0.0.1')
.option('-h, --host <host>', 'Host running server', String,'localhost')
.option('-p, --port <port>', 'Port to open to connect messages on',Number,12000)
.option('-k, --key <key>,', 'Routing Key to be used',String,'key1')
.option('-c, --count <count>','Iteration count',Number,50)
.option('-m, --mesg <mesg>','Message prefix',String,'hello')
.option('-t, --timeout', 'Timeout in ms between message posts')
.parse(process.argv);
function setup(host,port,key,mesg) {
var client = http.createClient(3000, host);
var request = client.request('GET','/register/'+port+"/"+key);
request.end();
request.on('response', function(response) {
response.on('data', function(chunk) {
postMessage(host,port,key,mesg,1);
});
});
}
function postMessage(host,port,key,mesg,count) {
var timeNow = new Date().getTime();
var mesgNow = mesg+"-"+count+"-"+port;
console.log("Type: Sent Mesg, Message: %s, Time: %s",mesgNow,timeNow);
var client1 = http.createClient(3000, host);
var request1 = client1.request('GET','/message/'+key+"/"+mesgNow);
request1.end();
count++;
if (count <100) {
setTimeout( function() { postMessage(host,port,key,mesg,count); }, 1000 );
}
}
var port = program.port;
var host = program.host;
var key = program.key;
var mesg = program.mesg;
var count = program.count;
var keys = ['key1','key2','key3','key4','key5'];
var messages = ['hello','world','good','morning','bye'];
var start=port;
for (i=0; i<count; i++) {
var index = i%keys.length;
var socket = start + i;
setup(host,socket,keys[index],messages[index]);
}
Error attached
buffer.js:494
throw new Error('sourceEnd out of bounds');
^
Error: sourceEnd out of bounds
at Buffer.copy (buffer.js:494:11)
at frame (/home/hvram/programs/node_modules/node-amqp/amqp.js:170:10)
at header (/home/hvram/programs/node_modules/node-amqp/amqp.js:160:14)
at frameEnd (/home/hvram/programs/node_modules/node-amqp/amqp.js:205:16)
at frame (/home/hvram/programs/node_modules/node-amqp/amqp.js:172:14)
at header (/home/hvram/programs/node_modules/node-amqp/amqp.js:160:14)
at frameEnd (/home/hvram/programs/node_modules/node-amqp/amqp.js:205:16)
at frame (/home/hvram/programs/node_modules/node-amqp/amqp.js:172:14)
at header (/home/hvram/programs/node_modules/node-amqp/amqp.js:160:14)
at frameEnd (/home/hvram/programs/node_modules/node-amqp/amqp.js:205:16)

Resources