I'm new to node.js and working my way through examples of the 'smashing node.js' book. In the websockets chapter I'm struggling to get this example to work. Forgive me it's a really simple error! I'm confused that why couldn't I see the cursor even using the author's code? And I downloaded the source code from Github.
Code is below: (firstly server.js file):
var express = require('express')
, wsio = require('websocket.io')
/**
* Create express app.
*/
var app = express.createServer();
/**
* Attach websocket server.
*/
var ws = wsio.attach(app);
/**
* Serve our code
*/
app.use(express.static('public'))
/**
* Listening on connections
*/
var positions = {}
, total = 0
ws.on('connection', function (socket) {
// we give the socket an id
socket.id = ++total;
// we send the positions of everyone else
socket.send(JSON.stringify(positions));
socket.on('message', function (msg) {
try {
var pos = JSON.parse(msg);
} catch (e) {
return;
}
positions[socket.id] = pos;
broadcast(JSON.stringify({ type: 'position', pos: pos, id: socket.id }));
});
socket.on('close', function () {
delete positions[socket.id];
broadcast(JSON.stringify({ type: 'disconnect', id: socket.id }));
});
function broadcast (msg) {
for (var i = 0, l = ws.clients; i < l; i++) {
// we avoid sending a message to the same socket that broadcasts
if (socket.id != ws.clients[i].id) {
// we call `send` on the other clients
ws.clients[i].send(msg);
}
}
}
});
/**
* Listen
*/
app.listen(3000);
Secondly the script contents of the ./public/index.html file:
<!doctype html>
<html>
<head>
<title>WebSocket cursors</title>
<script src="onload">
window.onload = function () {
var ws = new WebSocket('ws://localhost:3000');
ws.onopen = function () {
document.onmousemove = function (ev) {
ws.send(JSON.stringify({ x: ev.clientX, y: ev.clientY }));
}
}
ws.onmessage = function (msg) {
var obj = JSON.parse(msg);
// the first message is the position of all existing cursors
if (initialized) {
initialized = true;
for (var id in obj) {
move(id, obj[i]);
}
} else {
// other messages can either be a position change or
// a disconnection
if ('disconnect' == obj.type) {
remove(obj.id);
} else {
move(obj.id, obj.pos);
}
}
}
function move (id, pos) {
var cursor = document.getElementById('cursor-' + id);
if (!cursor) {
cursor = document.createElement('img');
cursor.src = '/cursor.png';
cursor.style.position = 'absolute';
document.body.appendChild(cursor);
}
cursor.style.left = pos.x + 'px';
cursor.style.top = pos.y + 'px';
}
function remove (id) {
var cursor = document.getElementById('cursor-' + id);
cursor.parentNode.removeChild(cursor);
}
}
</script>
</head>
<body>
<h1>WebSocket cursors</h1>
</body>
</html>
I changed the express framework from version 2.x.x to the newest version and I updated my websocket.io. Then Rewrited my codes. Now I can run it successfully.
Here's my codes:
server.js:
var express = require('express'),
wsio = require('websocket.io'),
port = 3000,
app = express(),
server = app.listen(port, function() {
console.log('server listening on port ' + port);
});
app.use(express.static('public'))
app.get('/', function(req, res) {
res.sendFile(__dirname + '/index.html');
});
var ws = wsio.attach(server),
positions = {},
total = 0;
ws.on('connection', function(socket) {
socket.id = ++total;
socket.send(JSON.stringify(positions));
socket.on('message', function(msg) {
try {
var pos = JSON.parse(msg);
} catch (e) {
return;
}
positions[socket.id] = pos;
broadcast(JSON.stringify({
type: 'position',
pos: pos,
id: socket.id
}));
});
socket.on('close', function() {
delete positions[socket.id];
broadcast(JSON.stringify({
type: 'disconnect',
id: socket.id
}));
});
function broadcast(msg) {
for (var i = 0, l = ws.clients.length; i < l; i++) {
if (ws.clients[i] && socket.id != ws.clients[i].id) {
ws.clients[i].send(msg);
}
}
}
});
And index.html:
<!doctype html>
<html>
<head>
<title>WebSocket cursors</title>
<script>
window.onload = function() {
var ws = new WebSocket('ws://localhost:3000');
ws.onopen = function() {
document.onmousemove = function(ev) {
ws.send(JSON.stringify({
x: ev.clientX,
y: ev.clientY
}));
}
}
ws.onmessage = function(msg) {
var obj = JSON.parse(msg.data),
initialized = (obj.type) ? true : false;
if (!initialized) {
initialized = true;
for (var id in obj) {
move(id, obj[id]);
}
} else {
if ('disconnect' == obj.type) {
remove(obj.id);
} else {
move(obj.id, obj.pos);
}
}
}
function move(id, pos) {
var cursor = document.getElementById('cursor-' + id);
if (!cursor) {
cursor = document.createElement('img');
cursor.src = '/cursor.png';
cursor.id = 'cursor-' + id;
cursor.style.position = 'absolute';
document.body.appendChild(cursor);
}
cursor.style.left = pos.x + 'px';
cursor.style.top = pos.y + 'px';
}
function remove(id) {
var cursor = document.getElementById('cursor-' + id);
if (cursor) cursor.parentNode.removeChild(cursor);
}
}
</script>
</head>
<body>
<h1>WebSocket cursors</h1>
</body>
</html>
Related
What is working
I created a node https/websocket server using WebSocket-Node. Here a snippet on how I add key/cert :
import WebSockerServer from "websocket";
import fs from "fs";
const httpsSignalServer = https.createServer(
{
key: fs.readFileSync("./server.private.key"),
cert: fs.readFileSync("./server.crt"),
},
(req, res) => {
console.log("signal server : we have received a request");
}
);
const signalWebsocket = new WebSockerServer.server({
httpServer: httpsSignalServer,
});
signalWebsocket.on("request", (request) => onRequest(request));
httpsSignalServer.listen(8080, () => console.log("My signal server is listening"));
I have a React html web page that sends data to server above with WebSocket web api via wss :
new WebSocket("wss://192.168.230.138:8081");
My react app runs in an https server. So far everything works.
What is not working
The problem is my little node websocket client. I am still using WebSocket-Node. The client documentation shows a tlsOptions to be given to the constructor.
Client code snippet:
var WebSocketClient = require("websocket").client;
var fs = require("fs");
const tlsOptions = {
key: "./server.private.key",
cert: "./server.crt",
};
var client = new WebSocketClient({
key: fs.readFileSync(tlsOptions.key),
cert: fs.readFileSync(tlsOptions.cert),
});
.
.
.
client.connect("wss://localhost:8080/", "");
The client returns the message :
Error: unable to verify the first certificate
Is there anyone out there who knows how to do the client setup with the tlsOptions?
Thank you
I wrote a perfectly functional simple chat script which is using secured connection. I added a couple of simple chat commands to enable or disable the chat from entering commands in chat.You could use this if you like or parts of it or even just see how I did it. Hope you find some use for it.
var maxLength = 100; // chars per chat
const port = 1441;
var JoinLeaveMsg = 1
var fs = require('fs');
const HTTPS = require('https');
const WebSocket = require('ws');
const server = new HTTPS.createServer({
cert: fs.readFileSync('C:/Certbot/live/MyDomain.com/cert.pem'),
key: fs.readFileSync('C:/Certbot/live/MyDomain.com/privkey.pem')
});
const wss = new WebSocket.Server({ server })
wss.on('connection', function connection(ws) {
const now = new Date();
const tDate = now.toDateString();
const date = new Date(now);
const currentDate = date.toLocaleTimeString();
console.log(tDate + ' - ' + currentDate + " - New User Connected");
ws.send(JSON.stringify(
{
name: "welcome",
message: "wanna chat?"
}
));
ws.on('message', function(message){
message = JSON.parse(message);
const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
// START CONNECT
if(message.type == "name"){
// START CHECKS FOR DOUBLE LOGIN
var dirty = message.data;
const ConnectingUser = DOMPurify.sanitize(dirty);
if (ConnectingUser != ''){
wss.clients.forEach(function e(client) {
var ConnectedUser = client.personName;
if(ConnectedUser == ConnectingUser) {
client.send(JSON.stringify(
{
name: "***Server***",
message: "***We do not allow double logins!"
}
));
client.send(JSON.stringify(
{
name: "***Server***",
message: "🔴 Disconnected..."
}
));
client.close();
}
});
}
// END CHECKS FOR DOUBLE LOGIN
ws.personName = ConnectingUser;
memberJoinedChatMsg(ws, ConnectingUser);
return;
}
// START INPUT DATA
if(message.type == "message"){
var ConnectedUser = ws.personName;
if (ConnectedUser != ''){
var dirty = message.message;
const Message = DOMPurify.sanitize(dirty);
var string = Message;
var temp = string.split(" ");
var CommandOne = temp[0];
var CommandTwo = temp[1];
var CommandThree = temp.slice(2).join(" ");
// lets process the messages and send to users
if((Message) != "") {
// START COMMANDS
// Do join/leave msg disable--------------------------------------------------------------------
if((CommandOne) == "/jpenable" || (CommandOne) == "!jpenable"){
JoinLeaveMsg = "1";
Msg ='***Join/Part message has been Enabled!';
sendMsgToAll(Msg)
MessageString ="";
}
// Do join/leave msg enable--------------------------------------------------------------------
if((CommandOne) == "/jpdisable" || (CommandOne) == "!jpdisable"){
JoinLeaveMsg = "0";
Msg ='***Join/Part message has been Disabled!';
sendMsgToAll(Msg)
MessageString ="";
}
// END COMMANDS
if ((Message).length < maxLength) {
wss.clients.forEach(function e(client) {
client.send(JSON.stringify(
{
name: ConnectedUser,
message: Message
}
));
console.log("Sent: "+ConnectedUser+" "+Message);
});
}else{
wss.clients.forEach(function e(client) {
if(client === ws)
client.send(JSON.stringify(
{
name: '***Server***',
message: "*** Your message exceeds "+maxLength+" characters!"
}
));
});
};
}else{
// dont process if empty line entered
};
}
}
// END INPUT DATA
})
ws.on('close', function(message) {
if(ws.personName){
memberLeftChatMsg(ws);
}
ws.close();
//console.log("Client Disconnected");
});
// END CONNECT
})
server.listen(port, function() {
console.log(`Server is listening on ${port}!`)
})
// FUNCTIONS
// complete Send Message To All
function sendMsgToAll(Msg){
wss.clients.forEach(function e(client) {
client.send(JSON.stringify(
{
name: "Server",
message: Msg
}
));
});
};
// complete
function memberJoinedChatMsg(ws, ConnectingUser) {
if(JoinLeaveMsg != "0"){
wss.clients.forEach(function e(client) {
client.send(JSON.stringify(
{
name: "***Server***",
message: "🟢 <<b>"+ConnectingUser+"</b>> has entered chat!"
}
));
});
}
};
// complete
function memberLeftChatMsg(ws) {
if(JoinLeaveMsg != "0"){
wss.clients.forEach(function e(client) {
client.send(JSON.stringify(
{
name: "***Server***",
message: "🔴 <<b>"+ws.personName+"</b>> has left chat!"
}
));
});
}
};
I also have Dompurify installed using npm to help prevent entities being posted. Remember to open the port in your firewall or change the port number in the script. If you need the client side script including the html, .css let me know, I can upload those too :)
CLIENT SCRIPT:
(function() {
const sendBtn = document.querySelector('#send');
const messages = document.querySelector('#messages');
const messageBox = document.querySelector('#messageBox');
var ws;
var text = "Server: Server Offline!";
var sName = name;
function updateChat(element) {
var name = element[0];
var message = element[1];
const now = new Date();
const tDate = now.toDateString();
const date = new Date(now);
const currentDate = date.toLocaleTimeString();
if(sName == name){
$('.responsive-html5-chat' + ' div.messages').append( '<div class="message"><div class="myMessage"><p>' + message + "</p><date>" + name + " " + currentDate + "</date></div></div>" );
}else{
$('.responsive-html5-chat' + ' div.messages').append( '<div class="message"><div class="fromThem"><p>' + message + "</p><date>" + currentDate + " " + name + "</date></div></div>" );
}
setTimeout(function () { $('.responsive-html5-chat' + ' > span').addClass("spinner"); }, 100);
setTimeout(function () { $('.responsive-html5-chat' + ' > span').removeClass("spinner"); }, 2000);
$(".messages").scrollTop(function() { return this.scrollHeight; });
}
function responsiveChat(element) {
$(element).html('<form class="chat"><span></span><div class="messages"></div><input type="text" id="msg" placeholder="Your message" autocomplete="off"><input id="send" type="submit" value="Send"></form>');
}
responsiveChat('.responsive-html5-chat');
function init() {
if (ws) {
ws.onerror = ws.onopen = ws.onclose = null;
ws.close();
}
ws = new WebSocket('wss://MyDomain.com:1441');
ws.onopen = function() {
if(name) {
ws.send(JSON.stringify({
type: "name",
data: name
}));
}else{
const dat = ['', 'Please Login to chat!'];
updateChat(dat);
ws = null;
return ;
}
}
// START RECIEVED FROM SERVER
ws.onmessage = function(event){
var json = JSON.parse(event.data);
var Name = json.name;
var Message = json.message;
if(name) {
if (Name === "***Server***"){
const dat = ['***Server***', Message];
updateChat(dat);
}else if(Name === "welcome" && name != '') {
Message = 'Hi ' + name + ' ' + Message;
const dat = ['', Message];
updateChat(dat);
}else{
const dat = [Name, Message];
updateChat(dat);
}
}
}
// END RECIEVED FROM SERVER
ws.onclose = function(event) {
if (event.wasClean) {
ws = null;
} else {
const dat = ['', 'Server Offline!'];
updateChat(dat);
ws = null;
}
};
}
// START SEND TO SERVER
send.onclick = function (event){
event.preventDefault();
if(name) {
if (!ws) {
const dat = ['', 'You are not connected... :('];
updateChat(dat);
ws = null;
return ;
}
var messages = document.getElementById("msg").value;
document.getElementById("msg").value = ''; // empty msg box
ws.send(JSON.stringify({
type: "message",
data: name,
message: messages
}));
}else{
const dat = ['', 'Please Login to chat!'];
updateChat(dat);
ws = null;
return ;
}
};
init();
})();
I have an application. In this application, I cannot change the video that goes to the other party.
'use strict';
var Meeting = function (socketioHost) {
var exports = {};
var _isInitiator = false;
var _localStream;
var _remoteStream;
var _turnReady;
var _pcConfig = {'iceServers': [{'url': 'stun:stun.l.google.com:19302'}]};
var _constraints = {
video: {
width: {ideal: 320},
height: {ideal: 240},
frameRate: {ideal: 20}
},
audio: {
googEchoCancellation: true,
googAutoGainControl: true,
googNoiseSuppression: true,
googHighpassFilter: true,
googEchoCancellation2: true,
googAutoGainControl2: true,
googNoiseSuppression2: true
},
options: {
mirror: true
}
};
if(navigator.userAgent.includes("iPhone")) {
var _constraints = {
video : true
}
}
var _defaultChannel;
var _privateAnswerChannel;
var _offerChannels = {};
var _opc = {};
var _apc = {};
var _sendChannel = {};
var _room;
var _myID;
var _onRemoteVideoCallback;
var _onLocalVideoCallback;
var _onChatMessageCallback;
var _onChatReadyCallback;
var _onChatNotReadyCallback;
var _onParticipantHangupCallback;
var _host = socketioHost;
////////////////////////////////////////////////
// PUBLIC FUNCTIONS
////////////////////////////////////////////////
/**
*
* Add callback function to be called when a chat message is available.
*
* #param name of the room to join
*/
function joinRoom(name) {
_room = name;
_myID = generateID();
// Open up a default communication channel
initDefaultChannel();
if (_room !== '') {
console.log('Create or join room', _room);
_defaultChannel.emit('create or join', {room:_room, from:_myID});
}
// Open up a private communication channel
initPrivateChannel();
//console.log(_devices);
navigator.mediaDevices.getUserMedia(_constraints)
.then(handleUserMedia)
.catch(handleUserMediaError);
window.onbeforeunload = function(e){
_defaultChannel.emit('message',{type: 'bye', from:_myID});
}
}
/**
*
* Send a chat message to all channels.
*
* #param message String message to be send
*/
function sendChatMessage(message) {
console.log("Sending "+message)
for (var channel in _sendChannel) {
if (_sendChannel.hasOwnProperty(channel)) {
_sendChannel[channel].send(message);
}
}
}
/**
*
* Toggle microphone availability.
*
*/
function toggleMic() {
var tracks = _localStream.getTracks();
for (var i = 0; i < tracks.length; i++) {
if (tracks[i].kind=="audio") {
tracks[i].enabled = !tracks[i].enabled;
}
}
}
/**
*
* Toggle video availability.
*
*/
function toggleVideo() {
var tracks = _localStream.getTracks();
for (var i = 0; i < tracks.length; i++) {
if (tracks[i].kind=="video") {
tracks[i].enabled = !tracks[i].enabled;
}
}
}
/**
*
* Add callback function to be called when remote video is available.
*
* #param callback of type function(stream, participantID)
*/
function onRemoteVideo(callback) {
_onRemoteVideoCallback = callback;
}
/**
*
* Add callback function to be called when local video is available.
*
* #param callback function of type function(stream)
*/
function onLocalVideo(callback) {
_onLocalVideoCallback = callback;
}
/**
*
* Add callback function to be called when chat is available.
*
* #parama callback function of type function()
*/
function onChatReady(callback) {
_onChatReadyCallback = callback;
}
/**
*
* Add callback function to be called when chat is no more available.
*
* #parama callback function of type function()
*/
function onChatNotReady(callback) {
_onChatNotReadyCallback = callback;
}
/**
*
* Add callback function to be called when a chat message is available.
*
* #parama callback function of type function(message)
*/
function onChatMessage(callback) {
_onChatMessageCallback = callback;
}
/**
*
* Add callback function to be called when a a participant left the conference.
*
* #parama callback function of type function(participantID)
*/
function onParticipantHangup(callback) {
_onParticipantHangupCallback = callback;
}
////////////////////////////////////////////////
// INIT FUNCTIONS
////////////////////////////////////////////////
function initDefaultChannel() {
_defaultChannel = openSignalingChannel('');
_defaultChannel.on('created', function (room){
console.log('Created room ' + room);
_isInitiator = true;
});
_defaultChannel.on('join', function (room){
console.log('Another peer made a request to join room ' + room);
});
_defaultChannel.on('joined', function (room){
console.log('This peer has joined room ' + room);
});
_defaultChannel.on('message', function (message){
console.log('Client received message:', message);
if (message.type === 'newparticipant') {
var partID = message.from;
// Open a new communication channel to the new participant
_offerChannels[partID] = openSignalingChannel(partID);
// Wait for answers (to offers) from the new participant
_offerChannels[partID].on('message', function (msg){
if (msg.dest===_myID) {
if (msg.type === 'answer') {
_opc[msg.from].setRemoteDescription(new RTCSessionDescription(msg.snDescription))
.then(setRemoteDescriptionSuccess)
.catch(setRemoteDescriptionError);
} else if (msg.type === 'candidate') {
var candidate = new RTCIceCandidate({sdpMLineIndex: msg.label, candidate: msg.candidate});
console.log('got ice candidate from '+msg.from);
_opc[msg.from].addIceCandidate(candidate, addIceCandidateSuccess, addIceCandidateError);
}
}
});
// Send an offer to the new participant
createOffer(partID);
} else if (message.type === 'bye') {
hangup(message.from);
} else if(message.type === 'change') {
$('#' + message.from).remove();
if(_myID !== message.from) {
createOffer(message.from);
}
}
});
}
function initPrivateChannel() {
// Open a private channel (namespace = _myID) to receive offers
_privateAnswerChannel = openSignalingChannel(_myID);
// Wait for offers or ice candidates
_privateAnswerChannel.on('message', function (message){
if (message.dest===_myID) {
if(message.type === 'offer') {
var to = message.from;
createAnswer(message, _privateAnswerChannel, to);
} else if (message.type === 'candidate') {
var candidate = new RTCIceCandidate({sdpMLineIndex: message.label, candidate: message.candidate});
_apc[message.from].addIceCandidate(candidate, addIceCandidateSuccess, addIceCandidateError);
}
}
});
}
function requestTurn(turn_url) {
var turnExists = false;
for (var i in _pcConfig.iceServers) {
if (_pcConfig.iceServers[i].url.substr(0, 5) === 'turn:') {
turnExists = true;
_turnReady = true;
break;
}
}
if (!turnExists) {
console.log('Getting TURN server from ', turn_url);
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (xhr.readyState === 4 && xhr.status === 200) {
var turnServer = JSON.parse(xhr.responseText);
console.log('Got TURN server: ', turnServer);
_pcConfig.iceServers.push({
'url': 'turn:' + turnServer.username + '#' + turnServer.turn,
'credential': turnServer.password
});
_turnReady = true;
}
}
xhr.open('GET', turn_url, true);
xhr.send();
}
}
///////////////////////////////////////////
// UTIL FUNCTIONS
///////////////////////////////////////////
/**
*
* Call the registered _onRemoteVideoCallback
*
*/
function addRemoteVideo(stream, from) {
// call the callback
_onRemoteVideoCallback(stream, from);
}
/**
*
* Generates a random ID.
*
* #return a random ID
*/
function generateID() {
var s4 = function() {
return Math.floor(Math.random() * 0x10000).toString(16);
};
return s4() + s4() + "-" + s4() + "-" + s4() + "-" + s4() + "-" + s4() + s4() + s4();
}
////////////////////////////////////////////////
// COMMUNICATION FUNCTIONS
////////////////////////////////////////////////
/**
*
* Connect to the server and open a signal channel using channel as the channel's name.
*
* #return the socket
*/
function openSignalingChannel(channel) {
var namespace = _host + '/' + channel;
var sckt = io.connect(namespace);
return sckt;
}
function logout(from) {
hangup(from)
window.dispatchEvent(new Event('beforeunload'))
}
/**
*
* Send an offer to peer with id participantId
*
* #param participantId the participant's unique ID we want to send an offer
*/
function createOffer(participantId) {
console.log('Creating offer for peer '+participantId);
_opc[participantId] = new RTCPeerConnection(_pcConfig);
_opc[participantId].onicecandidate = handleIceCandidateAnswerWrapper(_offerChannels[participantId], participantId);
_opc[participantId].onaddstream = handleRemoteStreamAdded(participantId);
_opc[participantId].onremovestream = handleRemoteStreamRemoved;
_opc[participantId].addStream(_localStream);
try {
// Reliable Data Channels not yet supported in Chrome
_sendChannel[participantId] = _opc[participantId].createDataChannel("sendDataChannel", {reliable: false});
_sendChannel[participantId].onmessage = handleMessage;
//console.log('Created send data channel');
} catch (e) {
alert('Failed to create data channel. ' + 'You need Chrome M25 or later with RtpDataChannel enabled');
//console.log('createDataChannel() failed with exception: ' + e.message);
}
_sendChannel[participantId].onopen = handleSendChannelStateChange(participantId);
_sendChannel[participantId].onclose = handleSendChannelStateChange(participantId);
var onSuccess = function(participantId) {
return function(sessionDescription) {
var channel = _offerChannels[participantId];
// Set Opus as the preferred codec in SDP if Opus is present.
sessionDescription.sdp = preferOpus(sessionDescription.sdp);
_opc[participantId].setLocalDescription(sessionDescription);
console.log('Sending offer to channel '+ channel.name);
channel.emit('message', {snDescription: sessionDescription, from:_myID, type:'offer', dest:participantId});
}
}
_opc[participantId].createOffer(onSuccess(participantId), handleCreateOfferError);
}
function createAnswer(sdp, cnl, to) {
_apc[to] = new RTCPeerConnection(_pcConfig);
_apc[to].onicecandidate = handleIceCandidateAnswerWrapper(cnl, to);
_apc[to].onaddstream = handleRemoteStreamAdded(to);
_apc[to].onremovestream = handleRemoteStreamRemoved;
_apc[to].addStream(_localStream);
_apc[to].setRemoteDescription(new RTCSessionDescription(sdp.snDescription))
.then(setRemoteDescriptionSuccess)
.catch(setRemoteDescriptionError);
_apc[to].ondatachannel = gotReceiveChannel(to);
var onSuccess = function(channel) {
return function(sessionDescription) {
// Set Opus as the preferred codec in SDP if Opus is present.
sessionDescription.sdp = preferOpus(sessionDescription.sdp);
_apc[to].setLocalDescription(sessionDescription);
console.log('Sending answer to channel '+ channel.name);
channel.emit('message', {snDescription:sessionDescription, from:_myID, type:'answer', dest:to});
}
}
_apc[to].createAnswer(onSuccess(cnl), handleCreateAnswerError);
}
function hangup(from) {
console.log('Bye received from '+ from);
if (_opc.hasOwnProperty(from)) {
_opc[from].close();
_opc[from] = null;
}
if (_apc.hasOwnProperty(from)) {
_apc[from].close();
_apc[from] = null;
}
_onParticipantHangupCallback(from);
}
////////////////////////////////////////////////
// HANDLERS
////////////////////////////////////////////////
// SUCCESS HANDLERS
function handleUserMedia(stream) {
console.log('Adding local stream');
_onLocalVideoCallback(stream);
_localStream = stream;
_defaultChannel.emit('message', {type:'newparticipant', from: _myID});
}
function changeHandleUserMedia(stream) {
_onLocalVideoCallback(stream);
_localStream = stream;
_defaultChannel.emit('message', {type:'change', from: _myID});
}
function handleRemoteStreamRemoved(event) {
console.log('Remote stream removed. Event: ', event);
}
function handleRemoteStreamAdded(from) {
return function(event) {
//console.log('Remote stream added');
addRemoteVideo(event.stream, from);
_remoteStream = event.stream;
}
}
function handleIceCandidateAnswerWrapper(channel, to) {
return function handleIceCandidate(event) {
console.log('handleIceCandidate event');
if (event.candidate) {
channel.emit('message',
{type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate,
from: _myID,
dest:to}
);
} else {
console.log('End of candidates.');
}
}
}
function setLocalDescriptionSuccess() {}
function setRemoteDescriptionSuccess() {}
function addIceCandidateSuccess() {}
function gotReceiveChannel(id) {
return function(event) {
console.log('Receive Channel Callback');
_sendChannel[id] = event.channel;
_sendChannel[id].onmessage = handleMessage;
_sendChannel[id].onopen = handleReceiveChannelStateChange(id);
_sendChannel[id].onclose = handleReceiveChannelStateChange(id);
}
}
function handleMessage(event) {
console.log('Received message: ' + event.data);
_onChatMessageCallback(event.data);
}
function handleSendChannelStateChange(participantId) {
return function() {
var readyState = _sendChannel[participantId].readyState;
console.log('Send channel state is: ' + readyState);
// check if we have at least one open channel before we set hat ready to false.
var open = checkIfOpenChannel();
enableMessageInterface(open);
}
}
function handleReceiveChannelStateChange(participantId) {
return function() {
var readyState = _sendChannel[participantId].readyState;
// check if we have at least one open channel before we set hat ready to false.
var open = checkIfOpenChannel();
enableMessageInterface(open);
}
}
function checkIfOpenChannel() {
var open = false;
for (var channel in _sendChannel) {
if (_sendChannel.hasOwnProperty(channel)) {
open = (_sendChannel[channel].readyState == "open");
if (open == true) {
break;
}
}
}
return open;
}
function enableMessageInterface(shouldEnable) {
if (shouldEnable) {
_onChatReadyCallback();
} else {
_onChatNotReadyCallback();
}
}
// ERROR HANDLERS
function handleCreateOfferError(event){
console.log('createOffer() error: ', event);
}
function handleCreateAnswerError(event){
console.log('createAnswer() error: ', event);
}
function handleUserMediaError(error){
console.log('getUserMedia error: ', error);
}
function setLocalDescriptionError(error) {
console.log('setLocalDescription error: ', error);
}
function setRemoteDescriptionError(error) {
console.log('setRemoteDescription error: ', error);
}
function addIceCandidateError(error) {}
////////////////////////////////////////////////
// CODEC
////////////////////////////////////////////////
// Set Opus as the default audio codec if it's present.
function preferOpus(sdp) {
var sdpLines = sdp.split('\r\n');
var mLineIndex;
// Search for m line.
for (var i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('m=audio') !== -1) {
mLineIndex = i;
break;
}
}
if (mLineIndex === null || mLineIndex === undefined) {
return sdp;
}
// If Opus is available, set it as the default in m line.
for (i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('opus/48000') !== -1) {
var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
if (opusPayload) {
sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex], opusPayload);
}
break;
}
}
// Remove CN in m line and sdp.
sdpLines = removeCN(sdpLines, mLineIndex);
sdp = sdpLines.join('\r\n');
return sdp;
}
function extractSdp(sdpLine, pattern) {
var result = sdpLine.match(pattern);
return result && result.length === 2 ? result[1] : null;
}
// Set the selected codec to the first in m line.
function setDefaultCodec(mLine, payload) {
var elements = mLine.split(' ');
var newLine = [];
var index = 0;
for (var i = 0; i < elements.length; i++) {
if (index === 3) { // Format of media starts from the fourth.
newLine[index++] = payload; // Put target payload to the first.
}
if (elements[i] !== payload) {
newLine[index++] = elements[i];
}
}
return newLine.join(' ');
}
// Strip CN from sdp before CN constraints is ready.
function removeCN(sdpLines, mLineIndex) {
var mLineElements = sdpLines[mLineIndex].split(' ');
// Scan from end for the convenience of removing an item.
for (var i = sdpLines.length-1; i >= 0; i--) {
var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
if (payload) {
var cnPos = mLineElements.indexOf(payload);
if (cnPos !== -1) {
// Remove CN payload from m line.
mLineElements.splice(cnPos, 1);
}
// Remove CN line in sdp
sdpLines.splice(i, 1);
}
}
sdpLines[mLineIndex] = mLineElements.join(' ');
return sdpLines;
}
////////////////////////////////////////////////
// EXPORT PUBLIC FUNCTIONS
////////////////////////////////////////////////
exports.joinRoom = joinRoom;
exports.toggleMic = toggleMic;
exports.toggleVideo = toggleVideo;
exports.onLocalVideo = onLocalVideo;
exports.onRemoteVideo = onRemoteVideo;
exports.onChatReady = onChatReady;
exports.onChatNotReady = onChatNotReady;
exports.onChatMessage = onChatMessage;
exports.sendChatMessage = sendChatMessage;
exports.onParticipantHangup = onParticipantHangup;
exports.changeHandleUserMedia = changeHandleUserMedia;
exports.logout = logout;
exports.opc = _opc;
exports.apc = _apc;
return exports;
};
i am providing my links from here and it works very well. Can you give an example of how I can do this?
$( document ).ready(function() {
/////////////////////////////////
// CREATE MEETING
/////////////////////////////////
meeting = new Meeting(host);
meeting.onLocalVideo(function(stream) {
//alert(stream.getVideoTracks().length);
document.querySelector('#localVideo').srcObject = stream;
$("#micMenu").on("click",function callback(e) {
$(this).toggleText("mic_off", "mic");
meeting.toggleMic();
});
$("#videoMenu").on("click",function callback(e) {
$(this).toggleText("videocam_off", "videocam");
meeting.toggleVideo();
});
$("#speakerMenu").on("click", function callback(e) {
$(this).toggleText("volume_off", "volume_up");
$("#localVideo").prop('muted', true);
});
$('#chatMenu').on('click', function callback(e) {
$(this).toggleText('speaker_notes_off', 'chat');
});
$('#close').on('click', function callback(e) {
meeting.logout($('.videoWrap').eq(1).attr('id'));
});
$(document).on('change', '#videoInput', function callback(e) {
var mediaParams = {
video: {mandatory: {sourceId: $(this).val()}}
};
navigator.mediaDevices.getUserMedia(mediaParams)
.then(function(stream){
meeting.handleUserMedia(stream);
})
.catch(function(e) { });
});
}
);
meeting.onRemoteVideo(function(stream, participantID) {
addRemoteVideo(stream, participantID);
}
);
meeting.onParticipantHangup(function(participantID) {
// Someone just left the meeting. Remove the participants video
removeRemoteVideo(participantID);
}
);
meeting.onChatReady(function() {
console.log("Chat is ready");
}
);
meeting.onChatNotReady(function() {
console.log("Chat is not ready");
}
);
var room = window.location.pathname.match(/([^\/]*)\/*$/)[1];
meeting.joinRoom(room);
}); // end of document.ready
obviously i am changing the local video. but I can't change it for other users.
In my practice, both local and remote video can be viewed,my code is as follows
package.json:
{
"name": "peer to peer",
"version": "1.0.0",
"description": "point to point by webrtc & websocket",
"main": "index.js",
"scripts": {
"dev": "node index.js"
},
"author": "webrtc",
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
"express-ws": "^4.0.0"
}
}
index.js:
const app = require('express')();
const wsInstance = require('express-ws')(app);
app.ws('/', ws => {
ws.on('message', data => {
// post cast
wsInstance.getWss().clients.forEach(server => {
if (server !== ws) {
server.send(data);
}
});
});
});
app.get('/', (req, res) => {
res.sendFile('./client/index.html', { root: __dirname });
});
app.get('/p2p', (req, res) => {
res.sendFile('./client/p2p.html', { root: __dirname });
});
app.listen(8091);
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>p2p webrtc</title>
<style>
.container {
width: 250px;
margin: 100px auto;
padding: 10px 30px;
border-radius: 4px;
border: 1px solid #ebeef5;
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
color: #303133;
}
</style>
</head>
<body>
<div class="container">
<p>seheme:</p>
<ul>
<li>openanswer;</li>
<li>openoffer;</li>
<li> comfirm connected ws ;</li>
<li> offer send 'start' button;</li>
</ul>
</div>
</body>
</html>
p2p.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.container {
width: 100%;
display: flex;
display: -webkit-flex;
justify-content: space-around;
padding-top: 20px;
}
.video-box {
position: relative;
width: 800px;
height: 400px;
}
#remote-video {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
border: 1px solid #eee;
background-color: #F2F6FC;
}
#local-video {
position: absolute;
right: 0;
bottom: 0;
width: 240px;
height: 120px;
object-fit: cover;
border: 1px solid #eee;
background-color: #EBEEF5;
}
.start-button {
position: absolute;
left: 50%;
top: 50%;
width: 100px;
display: none;
line-height: 40px;
outline: none;
color: #fff;
background-color: #409eff;
border: none;
border-radius: 4px;
cursor: pointer;
transform: translate(-50%, -50%);
}
.logger {
width: 40%;
padding: 14px;
line-height: 1.5;
color: #4fbf40;
border-radius: 6px;
background-color: #272727;
}
.logger .error {
color: #DD4A68;
}
</style>
</head>
<body>
<div class="container">
<div class="video-box">
<video id="remote-video"></video>
<video id="local-video" muted></video>
<button class="start-button" onclick="startLive()">start</button>
</div>
<div class="logger"></div>
</div>
<script>
const message = {
el: document.querySelector('.logger'),
log (msg) {
this.el.innerHTML += `<span>${new Date().toLocaleTimeString()}:${msg}</span><br/>`;
},
error (msg) {
this.el.innerHTML += `<span class="error">${new Date().toLocaleTimeString()}:${msg}</span><br/>`;
}
};
const target = location.search.slice(6);
const localVideo = document.querySelector('#local-video');
const remoteVideo = document.querySelector('#remote-video');
const button = document.querySelector('.start-button');
localVideo.onloadeddata = () => {
message.log('play local video');
localVideo.play();
}
remoteVideo.onloadeddata = () => {
message.log('play remote video');
remoteVideo.play();
}
document.title = target === 'offer' ? 'offer' : 'answer';
message.log('Multi-hole channel (WebSocket) is being created...');
const socket = new WebSocket('ws://localhost:8091');
socket.onopen = () => {
message.log('The signaling channel is successfully created!');
target === 'offer' && (button.style.display = 'block');
}
socket.onerror = () => message.error('Failed to create signaling channel!');
socket.onmessage = e => {
const { type, sdp, iceCandidate } = JSON.parse(e.data)
if (type === 'answer') {
peer.setRemoteDescription(new RTCSessionDescription({ type, sdp }));
} else if (type === 'answer_ice') {
peer.addIceCandidate(iceCandidate);
} else if (type === 'offer') {
startLive(new RTCSessionDescription({ type, sdp }));
} else if (type === 'offer_ice') {
peer.addIceCandidate(iceCandidate);
}
};
const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
!PeerConnection && message.error('The browser does not support WebRTC!');
const peer = new PeerConnection();
peer.ontrack = e => {
if (e && e.streams) {
message.log('Receive the other party's audio/video stream data...');
remoteVideo.srcObject = e.streams[0];
}
};
peer.onicecandidate = e => {
if (e.candidate) {
message.log('Collect and send candidates');
socket.send(JSON.stringify({
type: `${target}_ice`,
iceCandidate: e.candidate
}));
} else {
message.log('The candidate collection is complete!');
}
};
async function startLive (offerSdp) {
target === 'offer' && (button.style.display = 'none');
let stream;
try {
message.log('Try to call the local camera/microphone');
stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
message.log('The camera/microphone is successfully acquired!');
localVideo.srcObject = stream;
} catch {
message.error('Camera/microphone acquisition failed!');
return;
}
message.log(`------ WebRTC ${target === 'offer' ? 'offer' : 'answer'}seheme ------`);
message.log('Add a media track to the track set');
stream.getTracks().forEach(track => {
peer.addTrack(track, stream);
});
if (!offerSdp) {
message.log('Create a local SDP');
const offer = await peer.createOffer();
await peer.setLocalDescription(offer);
message.log(`Transmission initiator local SDP`);
socket.send(JSON.stringify(offer));
} else {
message.log('SDP received from the sender');
await peer.setRemoteDescription(offerSdp);
message.log('Create receiver (answer) SDP');
const answer = await peer.createAnswer();
message.log(`Transmission receiver (response) SDP`);
socket.send(JSON.stringify(answer));
await peer.setLocalDescription(answer);
}
}
</script>
</body>
</html>
Use code:
npm install //npm install dependency
npm run dev //open localhost:8091
Hope it can help you!
I believe your issue is not about switching WebRTC stream. Having you stated that you can switch the local camera and being a task you find answered elsewhere Like here
This is why I believe the issue is about design: To change Media Device you need a new stream. To get a new stream you need to prompt the user with getUserMedia()
So you have two solutions:
either you request the user two (or more) video inputs and keep all of them. Then switch them upon receiving the remote event.
or do something similar. Prompting the user, for the new video device, just when you receive the event.
Bear in mind that some browsers, do not allow to programmatically do certain requests. To be precise in some cases you need direct user interaction (like a click) to be allowed to fire certain requests. This may be the case with getUserMedia() if you are not getting prompted when the function is called by the remote event.
So right now I have a game that I am making where whenever someone connects, I give their client a number that my server uses to differentiate between the different characters. The characters are objects in an array so a client with an id "0" would be controlling the first object in my array.
However, I am having trouble deciding what to do when the person leaves.
server.js
// Setup basic express server
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var port = process.env.PORT || 8082;
server.listen(port, function () {
console.log('Server listening at port %d', port);
});
// Routing
app.use(express.static(__dirname + '/public'));
const speed = 5;
var hero1 = {
x : 0,
y : 0
}
var hero2 = {
x : 0,
y : 0
}
var hero3 = {
x : 0,
y : 0
}
var hero4 = {
x : 0,
y : 0
}
var hero5 = {
x : 0,
y : 0
}
var hero6 = {
x : 0,
y : 0
}
var allHeroes = [hero1, hero2, hero3, hero4, hero5, hero6];
var heroesOn = [];
function sendCoords() {
io.sockets.emit("draw", heroesOn)
}
io.on('connection', function (socket) {
socket.on('disconnect', function(){
console.log(clients)
});
var clients = Object.keys(io.sockets.sockets)
heroesOn.push(allHeroes[clients.length - 1]);
console.log(clients)
io.sockets.connected[clients[clients.length - 1]].emit("userId", clients.length - 1);
socket.on("move", function(data) {
if(heroesOn[data.user].x < 1) {
data.a = false;
} else if(data.a == true) {
heroesOn[data.user].x = heroesOn[data.user].x - speed
}
if(heroesOn[data.user].y < 1) {
data.w = false;
} else if(data.w == true) {
heroesOn[data.user].y = heroesOn[data.user].y - speed
}
if(heroesOn[data.user].y > 474) {
data.s = false;
} else if(data.s == true) {
heroesOn[data.user].y = heroesOn[data.user].y + speed
}
if(heroesOn[data.user].x > 974) {
data.d = false;
} else if(data.d == true) {
heroesOn[data.user].x = heroesOn[data.user].x + speed
}
})
})
setInterval(sendCoords, 1000/60)
client.js
$(function() {
var socket = io();
document.onkeydown = down;
document.onkeyup = up;
var canvas = document.querySelector('#canvas');
var ctx = canvas.getContext('2d');
var character = {
user : 0,
w : false,
a : false,
s : false,
d : false
}
socket.on("userId", function(data) {
console.log("got id")
character.user = data;
})
function down(e) {
socket.emit("move", character);
if(e.keyCode == 87) {
character.w = true;
}
if(e.keyCode == 65) {
character.a = true;
}
if(e.keyCode == 83) {
character.s = true;
}
if(e.keyCode == 68) {
character.d = true;
}
}
function up(e) {
socket.emit("move", character);
if(e.keyCode == 87) {
character.w = false;
}
if(e.keyCode == 65) {
character.a = false;
}
if(e.keyCode == 83) {
character.s = false;
}
if(e.keyCode == 68) {
character.d = false;
}
}
setInterval(function() {
}, 1000/60)
socket.on("draw", function(data) {
canvas.width=canvas.width;
for(var i = 0; i < data.length; i++) {
ctx.rect(data[i].x, data[i].y, 25, 25)
}
ctx.stroke();
})
});
How do I go about removing the hero from the heroesOn array whenever the client that is controlling that hero leaves.
I would consider using a map (i.e. an object) instead of an array to track your heroes. For example, when a client connects:
var heroesOn = {}; // key: socketid, value: hero
var counter = 0;
io.on('connection', function (socket) {
heroesOn[socket.id] = allHeroes[counter++];
...continue your code...
});
And when a socket disconnects:
socket.on('disconnect', function(socket){
delete heroesOn[socket.id];
});
While setting the remoteDescription , I am getting below error in firefox :
DOMException [InvalidStateError: "Cannot set remote offer in state have-local-offer"
code: 11
nsresult: 0x8053000b
location: http://localhost:8080/resources/assets/js/test-online.js:111]
Please find below my test-online.js code
var localVideo;
var remoteVideo;
var peerConnection;
var serverConnection;
var peerConnectionConfig = {'iceServers': [{'url': 'stun:stun.services.mozilla.com'}, {'url': 'stun:stun.l.google.com:19302'}]};
pageReady();
var offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
};
var sdpConstraints = {'mandatory': {
'OfferToReceiveAudio':true,
'OfferToReceiveVideo':true }};
function pageReady() {
localVideo = document.getElementById('localVideo');
remoteVideo = document.getElementById('remoteVideo');
localVideo.addEventListener('loadedmetadata', function() {
trace('Local video videoWidth: ' + this.videoWidth +
'px, videoHeight: ' + this.videoHeight + 'px');
});
remoteVideo.addEventListener('loadedmetadata', function() {
trace('Remote video videoWidth: ' + this.videoWidth +
'px, videoHeight: ' + this.videoHeight + 'px');
});
remoteVideo.onresize = function() {
trace('Remote video size changed to ' +
remoteVideo.videoWidth + 'x' + remoteVideo.videoHeight);
// We'll use the first onsize callback as an indication that video has started
// playing out.
if (startTime) {
var elapsedTime = window.performance.now() - startTime;
trace('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
startTime = null;
}
};
serverConnection = new SockJS("/onlineHandler");
serverConnection.onopen = function() {
console.log("Opening server connection");
};
serverConnection.onmessage = gotMessageFromServer;
serverConnection.onclose = function() {
console.log("Closing server connection");
};
//serverConnection.onmessage = gotMessageFromServer;
var constraints = {
video: true,
audio: true,
};
navigator.mediaDevices.getUserMedia(constraints)
.then(getUserMediaSuccess)
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
}
function getUserMediaSuccess(stream) {
trace('Received local stream');
localVideo.srcObject = stream;
localStream = stream;
}
function start(isCaller) {
trace('Starting call');
startTime = window.performance.now();
var videoTracks = localStream.getVideoTracks();
var audioTracks = localStream.getAudioTracks();
if (videoTracks.length > 0) {
trace('Using video device: ' + videoTracks[0].label);
}
if (audioTracks.length > 0) {
trace('Using audio device: ' + audioTracks[0].label);
}
peerConnection = new RTCPeerConnection(peerConnectionConfig);
peerConnection.onicecandidate = gotIceCandidate;
peerConnection.oniceconnectionstatechange = onIceStateChange;
peerConnection.onaddStream = gotRemoteStream;
peerConnection.addStream(localStream);
if(isCaller) {
peerConnection.createOffer(gotDescription, errorHandler , offerOptions);
}
}
function gotMessageFromServer(message) {
/* if(!peerConnection) start(false);
var signal = JSON.parse(message.data);
// console.log("Got Message from server :" + message.data);
if(signal.sdp) {;
console.log("hi in sdp" + message.data);
peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp), function() {
console.log("Creating answer :");
if (peerConnection.remoteDescription.type == 'offer')
peerConnection.createAnswer(gotDescription, errorHandler);
}, errorHandler);
} else if(signal.ice) {
peerConnection.addIceCandidate(new RTCIceCandidate(signal.ice));
}*/
var signal = JSON.parse(message.data);
if (signal.type === 'offer') {
peerConnection.setRemoteDescription(new RTCSessionDescription(signal),doAnswer,errorHandler);
} else if (signal.type === 'answer') {
peerConnection.setRemoteDescription(new RTCSessionDescription(signal),doNothing, errorHandler);
} else if (signal.type === 'candidate') {
var candidate = new RTCIceCandidate({
sdpMLineIndex:signal.label,
candidate: signal.candidate
});
peerConnection.addIceCandidate(candidate);
} else if (signal === 'bye' && isStarted) {
handleRemoteHangup();
}
}
function doNothing(){
}
function doAnswer() {
console.log('Sending answer to peer.');
peerConnection.createAnswer(gotDescription, errorHandler, sdpConstraints);
}
function handleRemoteHangup() {
// console.log('Session terminated.');
// stop();
// isInitiator = false;
}
function gotIceCandidate(event) {
if(event.candidate != null) {
var message ={
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate};
// serverConnection.send(JSON.stringify({'ice': event.candidate}));
serverConnection.send(JSON.stringify(message));
}
}
function onIceStateChange(event) {
if (peerConnection) {
trace(' ICE state: ' + peerConnection.iceConnectionState);
console.log('ICE state change event: ', event);
}
}
function gotDescription(description) {
// trace('Offer from peerConnection\n' + description.sdp);
description.sdp = preferOpus(description.sdp);
// pc.setLocalDescription(description);
console.log('setLocalAndSendMessage sending message' , description);
// trace('peerConnection setLocalDescription start');
peerConnection.setLocalDescription(
description,
function () {
serverConnection.send(JSON.stringify(description));
},
onSetSessionDescriptionError
);
}
function preferOpus(sdp) {
var sdpLines = sdp.split('\r\n');
var mLineIndex;
// Search for m line.
for (var i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('m=audio') !== -1) {
mLineIndex = i;
break;
}
}
if (mLineIndex === null) {
return sdp;
}
// If Opus is available, set it as the default in m line.
for (i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('opus/48000') !== -1) {
var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
if (opusPayload) {
sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex], opusPayload);
}
break;
}
}
// Remove CN in m line and sdp.
sdpLines = removeCN(sdpLines, mLineIndex);
sdp = sdpLines.join('\r\n');
return sdp;
}
function extractSdp(sdpLine, pattern) {
var result = sdpLine.match(pattern);
return result && result.length === 2 ? result[1] : null;
}
// Set the selected codec to the first in m line.
function setDefaultCodec(mLine, payload) {
var elements = mLine.split(' ');
var newLine = [];
var index = 0;
for (var i = 0; i < elements.length; i++) {
if (index === 3) { // Format of media starts from the fourth.
newLine[index++] = payload; // Put target payload to the first.
}
if (elements[i] !== payload) {
newLine[index++] = elements[i];
}
}
return newLine.join(' ');
}
// Strip CN from sdp before CN constraints is ready.
function removeCN(sdpLines, mLineIndex) {
var mLineElements = sdpLines[mLineIndex].split(' ');
// Scan from end for the convenience of removing an item.
for (var i = sdpLines.length-1; i >= 0; i--) {
var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
if (payload) {
var cnPos = mLineElements.indexOf(payload);
if (cnPos !== -1) {
// Remove CN payload from m line.
mLineElements.splice(cnPos, 1);
}
// Remove CN line in sdp
sdpLines.splice(i, 1);
}
}
sdpLines[mLineIndex] = mLineElements.join(' ');
return sdpLines;
}
function onSetSessionDescriptionError(error) {
trace('Failed to set session description: ' + error.toString());
}
function gotRemoteStream(event) {
remoteVideo.srcObject = event.stream;
trace('Received remote stream');
}
function errorHandler(error) {
console.log(error);
}
And my html code is below :
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<html lang="en">
<head>
<link
href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css"
rel="stylesheet">
<!-- Meta tag to not followed by search engine. -->
<meta name="robots" content="noindex,nofollow,nosnippet,noodp,noarchive">
<meta name="keywords" content="JavaScript, WebRTC" />
<meta name="description" content="WebRTC codelab" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1">
<style>
video {
filter: hue-rotate(180deg) saturate(200%);
-moz-filter: hue-rotate(180deg) saturate(200%);
-webkit-filter: hue-rotate(180deg) saturate(200%);
/*-webkit-filter: grayscale(0.3) hue-rotate(360deg) saturate(10) opacity(0.7) sepia(0.5); */
}
</style>
</head>
<body>
<div id='videos'>
<video id='localVideo' autoplay muted></video>
<video id='remoteVideo' autoplay></video>
</div>
<input type="button" id="start" onclick="start(true)" value="Start Video"></input>
<script src="//cdn.jsdelivr.net/sockjs/1.0.0/sockjs.min.js"></script>
<script
src="${pageContext.request.contextPath}/resources/assets/js/jquery-2.1.1.min.js"></script>
<script
src="${pageContext.request.contextPath}/resources/assets/js/bootstrap.min.js"></script>
<script src ="${pageContext.request.contextPath}/resources/assets/js/adapter-0.2.10.js"></script>
<script src="${pageContext.request.contextPath}/resources/assets/js/test-online.js"></script>
</body>
</html>
I am not able to understand what I am doing wrong here.I am still a novice in webrtc field juts want to run this basic thing in my code.
The offer-answer exchange is a state machine, and some methods are disallowed in certain states.
Calling setLocalDescription (or setRemoteDescription) changes the signaling state to "have-local-offer" (or "have-remote-offer").
At that point, it is the application's job to bring the state back to stable or closed, as described in the spec.
For instance, it is the application's responsibility to handle glare (which is where both sides send an offer at the same time).
There is also a bug in Firefox that it doesn't allow you to call createOffer or setLocalDescription again once in have-local-offer and vice versa for the answer (the little hoops in the state diagram in the spec linked above). But it doesn't sound from your error message like you're hitting that.
So, I have created a simple chat system which consists of nodeJS and mongodb. I currently 'host' it on apache, and it works perfectly on my computer, but if someone else connects to my site on the local network, then the chat doesnt work as intended. The send message and show message function breaks.
Ps: The code is from phpacademy, so cudos to them.
Here's the code I used:
HTML:
<!DOCTYPE html>
<html>
<head>
<title>ChattSystem</title>
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div class="chat">
<input type="text" class="chat-name" placeholder="Enter your name">
<div class="chat-messages"></div>
<textarea placeholder="Type your message"></textarea>
<div class="chat-status">Status: <span>Idle</span></div>
</div>
<script src="http://127.0.0.1:8080/socket.io/socket.io.js"></script>
<script>
(function() {
var getNode = function(s) {
return document.querySelector(s);
},
// Get required nodes
status = getNode('.chat-status span'),
messages = getNode('.chat-messages'),
textarea = getNode('.chat textarea'),
chatName = getNode('.chat-name');
statusDefault = status.textContent,
setStatus = function(s) {
status.textContent = s;
if(s !== statusDefault) {
var delay = setTimeout(function() {
setStatus(statusDefault);
clearInterval(delay);
}, 3000);
}
};
try {
var socket = io.connect('http://127.0.0.1:8080');
} catch(e) {
// Set status to warn user
}
if(socket !== undefined) {
// Listen for output
socket.on('output', function(data) {
if(data.length) {
// Loop through results
for(var x = 0; x < data.length; x = x + 1) {
var message = document.createElement('div');
message.setAttribute('class', 'chat-message');
message.textContent = data[x].name + ': ' + data[x].message;
// Append
messages.appendChild(message);
messages.insertBefore(message, messages.firstChild);
}
}
});
// Listen for a status
socket.on('status', function(data) {
setStatus((typeof data === 'object') ? data.message : data);
if(data.clear === true) {
textarea.value = '';
}
});
// Listen for keydown
textarea.addEventListener('keydown', function(event) {
var self = this,
name = chatName.value;
if(event.which);if(event.which == 13 && event.shiftKey === false) {
socket.emit('input', {
name: name,
message: self.value
});
event.preventDefault();
}
});
}
})();
</script>
</body>
</html>
Server.js
var mongo = require('mongodb').MongoClient,
client = require('socket.io').listen(8080).sockets;
mongo.connect('mongodb://127.0.0.1/chat', function(err, db) {
if(err) throw err;
client.on('connection', function(socket) {
var col = db.collection('messages'),
sendStatus = function(s) {
socket.emit('status', s);
};
// Emit all messages
col.find().limit(100).sort({_id: 1}).toArray(function(err, res) {
if(err) throw err;
socket.emit('output', res);
});
// Wait for input
socket.on('input', function(data) {
var name = data.name,
message = data.message,
whitespacePattern = /^\s*$/;
if(whitespacePattern.test(name) || whitespacePattern.test(message)) {
sendStatus('Name and message is required.');
} else {
col.insert({name: name, message: message}, function() {
// Emit latest message to ALL qclients
client.emit('output', [data]);
sendStatus({
message: "Message sent",
clear: true
});
});
}
});
});
});
In your JS from client side, change the IP to your server IP..
You script has currently the same local IP, thats mean what is always in localhost...
Find your IP and put it:
HTML (JS client side)
try {
var socket = io.connect('http://192.168.x.x:8080');
} catch(e) {
// Set status to warn user
}