The stream event is not working in PeerJS - express

The application is a Zoom-like program where users are connected P2P using PeerJS calling feature.
This is the PeerJS object set up:
var peer = new Peer(undefined, {
config: {
'iceServers': [
{
url: 'stun:relay.metered.ca:80'
},
{
url: 'turn:relay.metered.ca:80',
username: '*********',
credential: '**********',
},
{
url: 'turn:relay.metered.ca:443',
username: '*********',
credential: '*********'
}
]},
host: '/',
port: '3001'
})
Playing the current user's stream, and listening for other peers' calls:
const myVideo = document.createElement('video')
myVideo.muted = true
const peers = {}
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(stream => {
addVideoStream(myVideo, stream)
// listen for external calls on peer server
peer.on('call', call => {
call.answer(stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream)
})
})
socket.on('user-connected', userId => {
const call = peer.call(userId, stream)
const video = document.createElement('video')
call.on('stream', function(videoStream) {
addVideoStream(video, videoStream)
})
call.on('close', () => {
video.remove()
})
peers[userId] = call
})
})
peer.on('open', id => {
socket.emit('join-room', ROOM_ID, id)
})
function addVideoStream(video, stream) {
video.srcObject = stream
// once the video's ready play it
video.addEventListener('loadedmetadata', () => {
video.play()
})
videoGrid.append(video)
}
The server file:
const express = require('express')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server)
const { v4: uuidV4 } = require('uuid')
app.set('view engine', 'ejs')
app.use(express.static('public'))
app.get('/', (req, res) => {
res.redirect(`/${uuidV4()}`)
})
app.get('/:room', (req, res) => {
res.render('room', { roomId: req.params.room })
})
io.on('connection', socket => {
socket.on('join-room', (roomId, userId) => {
socket.join(roomId)
socket.to(roomId).emit('user-connected', userId)
socket.on('disconnect', () => {
socket.to(roomId).emit('user-disconnected', userId)
})
})
})
server.listen(3000)
I connect the app successfully, and I can make calls when I open the the same room's URL in another browser window (Chrome), but the call.on('stream') does not execute. I tried to see if there is an error using peer.on(error), and console.log() in the stream event's callback function, but there were no errors. I don't know what I'm missing here! Any help would be appreciated. Thank you.

Related

WebRTC(simple-peer) doesn't get the signal after I added APIs on server

I am using simple-peer and it really worked well on server before I added APIs for my project. I already did secure with https in the beginning and the only thing that has changed is releasing the server with APIs... Here is my code and now I only can check console.log(1), (3) for initiator peer and console.log(2), (8) for requesting peer. On requestinng peer internet tab, "Uncaught ReferenceError: process is not defined" this error occurs and I don't know why this error occurs on client side. Also it worked well on both of Chrome and Edge but now I can't even get my own stream on Chrome.
const myVideo = useRef();
const userVideo = useRef();
const connectionRef = useRef();
const roomName = "123";
let userStream = null;
let creator = false;
useEffect(() => {
const socket = io("https://www.jg-jg.shop");
socket.emit("joinRoom", roomName);
socket.on("created", () => {
creator = true;
navigator.mediaDevices
.getUserMedia({ video: true, audio: true })
.then((stream) => {
userStream = stream;
myVideo.current.srcObject = stream;
console.log(1);
});
});
socket.on("joined", () => {
navigator.mediaDevices
.getUserMedia({ video: true, audio: true })
.then((stream) => {
userStream = stream;
myVideo.current.srcObject = stream;
console.log(2);
});
socket.emit("ready", roomName);
});
socket.on("ready", () => {
if (creator) {
const peer = new Peer({
initiator: true,
trickle: false,
stream: userStream,
});
peer.on("signal", (signal) => {
socket.emit("sendingSignal", { signal, roomName });
console.log(3);
});
peer.on("stream", (stream) => {
userVideo.current.srcObject = stream;
console.log(4);
});
socket.on("receivingSignal", (signal) => {
peer.signal(signal);
console.log(5);
});
connectionRef.current = peer;
}
});
socket.on("offer", (incomingSignal) => {
if (!creator) {
const peer = new Peer({
initiator: false,
trickle: false,
stream: userStream,
});
peer.on("signal", (signal) => {
socket.emit("returningSignal", { signal, roomName });
console.log(6);
});
peer.on("stream", (stream) => {
userVideo.current.srcObject = stream;
console.log(7);
});
peer.signal(incomingSignal);
console.log(8);
connectionRef.current = peer;
}
});
}, []);

WebRTC connectiong only send one candidate

I am new coding in Javascript.
I am creating a WebRTC connection between my iPhone and my browser.
The connection works but my code only send one candidate and I don't know if I am doing anything wrong. I would appreciate any comment or support.
Thanks
const createPeerConnection = (signaling) => {
const peerConnection = new RTCPeerConnection({
iceServers: [],
});
const offerOptions = {
offerToReceiveVideo: true, offerToReceiveAudio: true
};
peerConnection.createOffer(offerOptions);
createAndSendOffer(signaling, peerConnection);
peerConnection.onicecandidate = (iceEvent) => {
if (iceEvent && iceEvent.candidate) {
signaling.send(JSON.stringify({
type: MESSAGE_TYPE.IceCandidate,
payload: iceEvent.candidate,
}));
}
};
peerConnection.onconnectionstatechange = (state ) => {
console.log(peerConnection.connectionState);
};
return peerConnection;
};
const createAndSendOffer = async (signaling, peerConnection) => {
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
signaling.send(JSON.stringify({ type: MESSAGE_TYPE.SessionDescription, payload: offer }));
};

Nextjs with Expressjs POST route not found

I have recently integrated Expressjs with my Nextjs project and was trying to run as POST request to an API.
But I'm getting an error message
Cannot POST /api/auth/signin/`
I do not receive the same when I make a GET request. Based on the way I integrated ExpressJS from a tutorial, I'm curious if the error message for POST requests stems from not attaching the .post() method to the express server in my server side server file. Does this sound like the source of the issue?
ssr-server.js:
const express = require('express')()
const next = require('next')
const bodyParser = require('body-parser')
// Session Management
const session = require('express-session')
const redis = require('redis');
const redisClient = redis.createClient({
host: process.env.REDIS_HOST || 'localhost',
port: process.env.REDIS_PORT || '6379',
url: process.env.REDIS_URL || ''
});
const redisStore = require('connect-redis')(session);
redisClient.on_connect("error", function(error){
console.error(error);
})
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
console.log("Test")
app.prepare()
.then(() => {
const server = express()
// Session Management
server.use(session({
store: new redisStore({client: redis.RedisClient}),
secret: process.env.SESSION_SECRET,
saveUninitialized: false,
resave: false,
cookie: {
secure: process.env.SESSION_SECURE || false,
httpOnly: true,
}
}))
// Form body parsing
server.use(bodyParser.json())
server.use(bodyParser.urlencoded({extended: true}))
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(3000, (err) => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})
.catch((ex) => {
console.error(ex.stack)
process.exit(1)
})
/api/auth/signin.js:
export default (req, res) => {
if (req.method === 'POST') {
if (res.statusCode = 200){
return res.json({ body: req.body, message: 'Success' })
} else {
return res.json({ message: 'Error on POST'})
}
} else {
return res.json({ message: 'Error' })
}
}

Apollo subscriptions JWT authentication

I am using Robin Wieruch's fullstack boilerplate but it is missing authentication for subscriptions. It uses JWT token for sessions and it is working fine for http but for ws auth is completely missing.
I need to pass user trough context for subscriptions as well, I need session info in subscriptions resolver to be able to decide weather I should fire subscription or not.
I did search Apollo docs, I saw I should use onConnect: (connectionParams, webSocket, context) function, but there is no fully functional fullstack example, I am not sure how to pass JWT from client to be able to get it in webSocket object.
Here is what I have so far:
Server:
import express from 'express';
import {
ApolloServer,
AuthenticationError,
} from 'apollo-server-express';
const app = express();
app.use(cors());
const getMe = async req => {
const token = req.headers['x-token'];
if (token) {
try {
return await jwt.verify(token, process.env.SECRET);
} catch (e) {
throw new AuthenticationError(
'Your session expired. Sign in again.',
);
}
}
};
const server = new ApolloServer({
introspection: true,
typeDefs: schema,
resolvers,
subscriptions: {
onConnect: (connectionParams, webSocket, context) => {
console.log(webSocket);
},
},
context: async ({ req, connection }) => {
// subscriptions
if (connection) {
return {
// how to pass me here as well?
models,
};
}
// mutations and queries
if (req) {
const me = await getMe(req);
return {
models,
me,
secret: process.env.SECRET,
};
}
},
});
server.applyMiddleware({ app, path: '/graphql' });
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
const isTest = !!process.env.TEST_DATABASE_URL;
const isProduction = process.env.NODE_ENV === 'production';
const port = process.env.PORT || 8000;
httpServer.listen({ port }, () => {
console.log(`Apollo Server on http://localhost:${port}/graphql`);
});
Client:
const httpLink = createUploadLink({
uri: 'http://localhost:8000/graphql',
fetch: customFetch,
});
const wsLink = new WebSocketLink({
uri: `ws://localhost:8000/graphql`,
options: {
reconnect: true,
},
});
const terminatingLink = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return (
kind === 'OperationDefinition' && operation === 'subscription'
);
},
wsLink,
httpLink,
);
const authLink = new ApolloLink((operation, forward) => {
operation.setContext(({ headers = {} }) => {
const token = localStorage.getItem('token');
if (token) {
headers = { ...headers, 'x-token': token };
}
return { headers };
});
return forward(operation);
});
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) => {
console.log('GraphQL error', message);
if (message === 'UNAUTHENTICATED') {
signOut(client);
}
});
}
if (networkError) {
console.log('Network error', networkError);
if (networkError.statusCode === 401) {
signOut(client);
}
}
});
const link = ApolloLink.from([authLink, errorLink, terminatingLink]);
const cache = new InMemoryCache();
const client = new ApolloClient({
link,
cache,
resolvers,
typeDefs,
});
You need to use connectionParams to set the JWT from the client-side. Below is the code snippet using the angular framework:
const WS_URI = `wss://${environment.HOST}:${environment.PORT}${
environment.WS_PATH
}`;
const wsClient = subscriptionService.getWSClient(WS_URI, {
lazy: true,
// When connectionParams is a function, it gets evaluated before each connection.
connectionParams: () => {
return {
token: `Bearer ${authService.getJwt()}`
};
},
reconnect: true,
reconnectionAttempts: 5,
connectionCallback: (error: Error[]) => {
if (error) {
console.log(error);
}
console.log('connectionCallback');
},
inactivityTimeout: 1000
});
const wsLink = new WebSocketLink(wsClient);
In your server-side, you are correct, using onConnect event handler to handle the JWT. E.g.
const server = new ApolloServer({
typeDefs,
resolvers,
context: contextFunction,
introspection: true,
subscriptions: {
onConnect: (
connectionParams: IWebSocketConnectionParams,
webSocket: WebSocket,
connectionContext: ConnectionContext,
) => {
console.log('websocket connect');
console.log('connectionParams: ', connectionParams);
if (connectionParams.token) {
const token: string = validateToken(connectionParams.token);
const userConnector = new UserConnector<IMemoryDB>(memoryDB);
let user: IUser | undefined;
try {
const userType: UserType = UserType[token];
user = userConnector.findUserByUserType(userType);
} catch (error) {
throw error;
}
const context: ISubscriptionContext = {
// pubsub: postgresPubSub,
pubsub,
subscribeUser: user,
userConnector,
locationConnector: new LocationConnector<IMemoryDB>(memoryDB),
};
return context;
}
throw new Error('Missing auth token!');
},
onDisconnect: (webSocket: WebSocket, connectionContext: ConnectionContext) => {
console.log('websocket disconnect');
},
},
});
server-side: https://github.com/mrdulin/apollo-graphql-tutorial/blob/master/src/subscriptions/server.ts#L72
client-side: https://github.com/mrdulin/angular-apollo-starter/blob/master/src/app/graphql/graphql.module.ts#L38

webrtc each step success but no video share

chrome version: 62.0.3202.94;
firefox version: 57.0.1;
I write a simple demo use webrtc and socket.io.
It works with pages. For example, I open one page to connect socket, and waiting for PeerConnection info from the main page(which get the local media). When I open the main, I create ice and sdp and then exchange them by socket.io to create connection.
Here is the code.
// The server side:
const express = require('express')
const app = express()
const path = require('path')
app.use(express.static(path.join(__dirname, 'public')))
app.get('/phone', function(req, res) {
res.sendfile(__dirname + '/phone.html')
})
app.get('/', function(req, res) {
res.sendfile(__dirname + '/index.html')
})
const server = require('http').createServer(app)
const io = require('socket.io')(server)
let clients = []
io.on('connection', function(socket) {
clients.push(socket)
const referer = socket.handshake.headers.referer
// socket connect from '/phone'
if (referer.match('/phone')) {
// send the ice from phone to others
socket.on('phone_ice_candidate', function(res) {
socket.broadcast.emit('pc_add_ice', {
ice: res.ice
})
})
// send the sdp from phone to others
socket.on('send_phone_sdp', function(data) {
socket.broadcast.emit('set_pc_remote_sdp', {
desc: data.desc
})
})
}
// phone add ice from web
socket.on('remote_ice_candidate', function(ice) {
socket.to(getId(clients, '/phone')).emit('send_ice_to_pc', {
ice: ice
})
})
// phone add sdp from web
socket.on('send_pc_sdp', function(data) {
// send to phone
socket.to(getId(clients, '/phone')).emit('set_phone_remote_sdp', {
desc: data
})
})
// socket disconnect and remove it from clients
socket.on('disconnect', () => {
let id = socket.id
clients.forEach((client, index) => {
if (client.id === id) {
clients.splice(index, 1)
}
})
})
})
// get the socket id to emit
function getId(sockets, exp) {
let id
sockets.forEach(socket => {
if (socket.handshake.headers.referer.match(exp)) {
id = socket.id
}
})
return id
}
server.listen(3000, function() {
console.log('port listening at 3000')
})
// --------------------------------------------- //
// web.js
var socket = io();
var server = {
// "iceServers": [{
// "url": "stun:stun.l.google.com:19302"
// }]
},
pc = new RTCPeerConnection(null),
v = document.querySelector('#video2')
// web onicecandidate
pc.onicecandidate = function(event) {
if (event.candidate) {
socket.emit('remote_ice_candidate', {
ice: event.candidate
})
}
}
// web addIceCandidate
socket.on('pc_add_ice', function(event) {
pc.addIceCandidate(new RTCIceCandidate(event.ice))
})
// didn't trigger
pc.ontrack = function(e) {
// v.srcObject = e.streams[0];
console.log(e, 'pc.ontrack')
}
// web setRemoteDescription and createAnswer
socket.on('set_pc_remote_sdp', function(e) {
pc.setRemoteDescription(e.desc).then(
function() {
console.log('remote setRemoteDescription success')
pc.createAnswer().then(function(desc) {
pc.setLocalDescription(desc).then(
function() {
socket.emit('send_pc_sdp', {
desc: desc
})
},
function(err) {
console.log(err)
}
);
})
},
function() {
console.log('pc setLocalDescription error')
}
)
})
// web iceConnectionState
pc.oniceconnectionstatechange = function() {
console.log('web oniceconnectionstatechange', pc.iceConnectionState)
// log checking -> connected
};
//---------------------------------------------//
// phone.js
var socket = io();
var server = {
// "iceServers": [{
// "url": "stun:stun.l.google.com:19302"
// }]
},
pc = new RTCPeerConnection(null),
v = document.querySelector('#video1')
// phone onicecandidate
pc.onicecandidate = function(event) {
if (event.candidate) {
socket.emit('phone_ice_candidate', {
ice: event.candidate
})
}
}
// phone addIceCandidate
socket.on('send_ice_to_pc', function(event) {
pc.addIceCandidate(new RTCIceCandidate(event.ice.ice))
})
// getUserMedia
navigator.mediaDevices.getUserMedia({
video: {
width: 400,
height: 300
},
audio: false
})
.then(function(stream) {
v.src = window.URL.createObjectURL(stream);
pc.addStream(stream);
})
.then(function() {
// create offer
pc.createOffer({
offerToReceiveVideo: 1
}).then(function(e) {
// pc setLocalDescription
pc.setLocalDescription(e).then(
function() {
socket.emit('send_phone_sdp', {
desc: e
})
},
function() {
console.log('pc setLocalDescription error')
}
)
});
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
})
// phone setRemoteDescription
socket.on('set_phone_remote_sdp', function(e) {
pc.setRemoteDescription(e.desc.desc).then(
function() {
console.log('pc setRemoteDescription success')
},
function(err) {
console.log(err)
})
})
// phone iceConnectionState
pc.oniceconnectionstatechange = function() {
console.log('phone oniceconnectionstatechange', pc.iceConnectionState)
// log checking -> connected -> completed
};
When i use firefox to open it, there is an error ICE failed, add a STUN server and see about:webrtc for more details in console.
In chrome the 'phone iceConnectionState' changed checking -> connected -> completed, the 'web iceConnectionState' changed checking -> connected.
have you set autoplay in your html? I have the same issue, and it turns out I should have set autoplay in my html tag. Namely:
<video autoplay></video>
Hope this helps!
getUserMedia is an async function. You are calling createOffer before you call pc.addStream which means there is nothing to negotiate.
Make the promise callback return your pc.createOffer() after pc.addStream(stream);
PTAL at https://www.html5rocks.com/en/tutorials/webrtc/basics/#toc-signaling and compare your code to the example and see if you can figure it out.