I read about WebRTC in MDN and tried to open peer connection. I decided to open both local and remote connections in one page, and wrote this code:
const configuration = {iceServers: [
{urls: 'stun:stun.l.google.com:19302'},
{urls: 'stun:stun1.l.google.com:19302'},
]};
const localConnection = new RTCPeerConnection(configuration);
const remoteConnection = new RTCPeerConnection(configuration);
let localSendChannel, remoteSendChannel;
localConnection.onicecandidate = ({candidate}) => {
console.log('local candidate', candidate);
if (candidate) remoteConnection.addIceCandidate(candidate)
}
remoteConnection.onicecandidate = ({candidate}) => {
console.log('remote candidate', candidate);
if (candidate) localConnection.addIceCandidate(candidate)
}
const connect = async () => {
const offer = await localConnection.createOffer();
console.log('local offer', offer);
await localConnection.setLocalDescription(offer);
console.log('local localDescription', localConnection.localDescription);
await remoteConnection.setRemoteDescription(localConnection.localDescription);
const answer = await remoteConnection.createAnswer();
await remoteConnection.setLocalDescription(answer);
console.log('remote answer', answer);
console.log('remote localDescription', remoteConnection.localDescription);
await localConnection.setRemoteDescription(remoteConnection.localDescription);
localConnection.addEventListener('connectionstatechange', (e) => {
console.log('localConnection new state', e.connectionState);
});
remoteConnection.addEventListener('connectionstatechange', (e) => {
console.log('remoteConnection new state', e.connectionState);
});
const gumStream = await navigator.mediaDevices.getUserMedia({video: false, audio: true});
for (const track of gumStream.getTracks())
localConnection.addTrack(track);
}
const openLocalChannel = async () => {
localSendChannel = localConnection.createDataChannel("sendChannel");
localSendChannel.onopen = () => {
console.log('local datachannel was opened');
localSendChannel.send("Hello, world!")
}
localSendChannel.onclose = () => console.log('local datachannel was closed');
localSendChannel.onmessage = (msg) => console.log('local channel got message', msg);
}
const waitRemoteChannel = () => {
remoteConnection.ondatachannel = (e) => {
remoteSendChannel = e.channel;
console.log('remote atachannel was init');
remoteSendChannel.onopen = () => console.log('remote datachannel was opened');
remoteSendChannel.onclose = () => console.log('remote datachannel was closed');
remoteSendChannel.onmessage = (msg) => console.log('remote channel got message', msg);
};
}
const start = async () => {
await connect();
waitRemoteChannel();
await openLocalChannel();
localConnection.addEventListener('connectionstatechange', async (e) => {
console.log('localConnection new state', e.connectionState);
});
remoteConnection.addEventListener('connectionstatechange', (e) => {
console.log('localConnection new state', e.connectionState);
});
}
start();
I haven't any candidates in Chrome and have only null candidates in FireFox. Can you point, where is mistake?
Updated: I added to the code media tracks adding and trying of create datachannel after connection create. But problem is staying
You are not doing anything with the connection, neither adding a track nor creating a datachannel. As a result the offer will be formally valid but will not cause ice candidates to be gathered and without candidates there will be no connection.
Related
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.
I read some tips on how to mock your request/response in Express framework in the blog:
https://codewithhugo.com/express-request-response-mocking/. However, I have no clue how to mock the controller below.
export const healthCheck = async (req, res, next) => {
log("debug", "healthCheck controller called");
const healthcheck = {
uptime: process.uptime(),
message: "Server is running!",
now_timestamp: Date.now()
};
try {
res.send(healthcheck);
} catch (error) {
healthcheck.message = error;
res.status(503).send();
}
};
I am glad to share my efforts below. My suspicion is that I must mock class Date as well.
import {
healthCheck
} from "../healthcheck.js";
const mockRequest = () => {
const req = {}
req.body = jest.fn().mockReturnValue(req)
req.params = jest.fn().mockReturnValue(req)
return req
};
const mockResponse = () => {
const res = {}
res.get = jest.fn().mockReturnValue(res)
res.send = jest.fn().mockReturnValue(res)
res.status = jest.fn().mockReturnValue(res)
res.json = jest.fn().mockReturnValue(res)
return res
};
const mockNext = () => {
return jest.fn()
};
describe("healthcheck", () => {
afterEach(() => {
// restore the spy created with spyOn
jest.restoreAllMocks();
});
it("should call mocked log for invalid from scaler", async () => {
let req = mockRequest();
let res = mockResponse();
let next = mockNext();
await healthCheck(req, res, next);
expect(res.send).toHaveBeenCalledTimes(1)
expect(res.send.mock.calls.length).toBe(1);
});
});
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;
}
});
}, []);
I am trying to transfer a video streaming from one browser to another with WebRTC and socket.io. It works just fine in the same network. No image is getting through across different ones.
I use socket-io as a signal server. I register two browsers in a "room" and then start sending signals.
The code which is executed in the browser from which the streaming is sent:
function joinRoom(room) {
if (room === '') {
alert('Please type a room ID')
} else {
data = { room: room};
socket.emit('join', data);
}
}
// SOCKET EVENT CALLBACKS =====================================================
socket.on('room_created', async () => {
console.log('Socket event callback: room_created')
await setLocalStream(mediaConstraints)
socket.emit('startc', {room: roomId, clientip: clientip})
isRoomCreator = true
})
socket.on('full_room', () => {
console.log('Socket event callback: full_room')
alert('The room is full, please try another one')
})
socket.on('startc', async () => {
console.log('Socket event callback: start_call')
if (isRoomCreator) {
rtcPeerConnection = new RTCPeerConnection(iceServers)
addLocalTracks(rtcPeerConnection)
rtcPeerConnection.ontrack = setRemoteStream
rtcPeerConnection.onicecandidate = sendIceCandidate
await createOffer(rtcPeerConnection)
}
})
socket.on('offer', async (event) => {
console.log('Socket event callback: offer')
if (!isRoomCreator) {
rtcPeerConnection = new RTCPeerConnection(iceServers)
addLocalTracks(rtcPeerConnection)
rtcPeerConnection.ontrack = setRemoteStream
rtcPeerConnection.onicecandidate = sendIceCandidate
rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event))
await createAnswer(rtcPeerConnection)
}
})
socket.on('answer', (event) => {
console.log('answer');
console.log('Socket event callback: webrtc_answer')
rtcPeerConnection.setRemoteDescription(new RTCSessionDescription(event))
})
socket.on('webrtc_ice_candidate', (event) => {
console.log('Socket event callback: webrtc_ice_candidate')
// ICE candidate configuration.
var candidate = new RTCIceCandidate({
sdpMLineIndex: event.label,
candidate: event.candidate,
})
rtcPeerConnection.addIceCandidate(candidate)
})
The code that accepts the streamed media (vuejs):
socket.on("offer", (data) => {
this.$emit("closeWaitingToConnect");
this.createAnswer(data);
});
joinMeToRoom() {
console.log("joinToRoom: ", this.room);
this.$socket.emit("join", this.room);
}, //joinMeToRoom ()
createAnswer: function(event) {
var roomId = this.room.room;
let sessionDescription
this.peer.ontrack = this.setRemoteStream
this.peer.onicecandidate = this.sendIceCandidate
this.peer.setRemoteDescription(new RTCSessionDescription(event))
try {
sessionDescription = this.peer.createAnswer().then((answer) => {
var anwer =
console.log('sessionDescription');
console.log(answer);
this.$socket.emit('answer', {
type: 'webrtc_answer',
sdp: answer,
sessionDescription: JSON.stringify(answer),
roomId,
})
return this.peer.setLocalDescription(answer)
});
} catch (error) {
console.error('cae: '+error)
}
},
getScreenPosition() {
const right = this.$refs.screen.getBoundingClientRect().right;
const bottom = this.$refs.screen.getBoundingClientRect().bottom;
return { bottom: bottom, right: right };
},
setRemoteStream(event) {
console.log('event setRemoteStream');
console.log(event);
var stream_screen = document.querySelector("video");
stream_screen.srcObject = event.streams[0];
stream_screen.play();
var remoteStream = event.stream
},
I have setup my own TURN server and tried paid versions. Still can't get the stream across different networks.
What am I missing?
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 }));
};