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.
Related
Basically what I'm trying to do is locally detect faces while being in a video conference. For this purpose I'm using RTCMultiConnection for the video conference part, this specific part to be exact and OpenCV.js for the computer vision part, specifically this repository.
I know that I need to send the mediaStream object from video element to the canvas element inside the OpenCV function. I was not getting that stream shown on the canvas so I figured I would make the variable containing that mediaStream object global so I can pass it to the OpenCV function but that is not working either.
I would be truly grateful for any insight you can provide, thank you.
Here's the code:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Video Conferencing using RTCMultiConnection</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="shortcut icon" href="/demos/logo.png">
<link rel="stylesheet" href="/demos/stylesheet.css">
<script src="/demos/menu.js"></script>
<script async src="js/opencv.js" ></script>
<script src="js/utils.js"></script>
</head>
<body>
<header>
<!-- <a class="logo" href="/"><img src="/demos/logo.png" alt="RTCMultiConnection"></a>
Menu<img src="/demos/menu-icon.png" alt="Menu">
<!-- <nav>
<li>
Home
</li>
<li>
Demos
</li>
<li>
Getting Started
</li>
<li>
FAQ
</li>
<li>
YouTube
</li>
<li>
Wiki
</li>
<li>
Github
</li>
</nav> -->
</header> -->
<h1>
Video Conferencing using RTCMultiConnection
<p class="no-mobile">
Multi-user (many-to-many) video chat using mesh networking model.
</p>
</h1>
<section class="make-center">
<div>
<label><input type="checkbox" id="record-entire-conference"> Record Entire Conference In The Browser?</label>
<span id="recording-status" style="display: none;"></span>
<button id="btn-stop-recording" style="display: none;">Stop Recording</button>
<br><br>
<input type="text" id="room-id" value="abcdef" autocorrect=off autocapitalize=off size=20>
<button id="open-room">Open Room</button>
<button id="join-room">Join Room</button>
<button id="open-or-join-room">Auto Open Or Join Room</button>
</div>
<div id="videos-container" style="margin: 20px 0;"></div>
<canvas id="canvas_output"></canvas>
<div id="room-urls" style="text-align: center;display: none;background: #F1EDED;margin: 15px -10px;border: 1px solid rgb(189, 189, 189);border-left: 0;border-right: 0;"></div>
</section>
<script src="/dist/RTCMultiConnection.min.js"></script>
<script src="/node_modules/webrtc-adapter/out/adapter.js"></script>
<script src="/socket.io/socket.io.js"></script>
<!-- <script async src="js/opencv.js" onload="openCvReady()"></script> -->
<!-- <script src="js/utils.js"></script> -->
<!-- custom layout for HTML5 audio/video elements -->
<link rel="stylesheet" href="/dev/getHTMLMediaElement.css">
<script src="/dev/getHTMLMediaElement.js"></script>
<script src="/node_modules/recordrtc/RecordRTC.js"></script>
<script>
// ......................................................
// .......................UI Code........................
// ......................................................
document.getElementById('open-room').onclick = function() {
disableInputButtons();
connection.open(document.getElementById('room-id').value, function(isRoomOpened, roomid, error) {
if(isRoomOpened === true) {
showRoomURL(connection.sessionid);
}
else {
disableInputButtons(true);
if(error === 'Room not available') {
alert('Someone already created this room. Please either join or create a separate room.');
return;
}
alert(error);
}
});
};
document.getElementById('join-room').onclick = function() {
disableInputButtons();
connection.join(document.getElementById('room-id').value, function(isJoinedRoom, roomid, error) {
if (error) {
disableInputButtons(true);
if(error === 'Room not available') {
alert('This room does not exist. Please either create it or wait for moderator to enter in the room.');
return;
}
alert(error);
}
});
};
document.getElementById('open-or-join-room').onclick = function() {
disableInputButtons();
connection.openOrJoin(document.getElementById('room-id').value, function(isRoomExist, roomid, error) {
if(error) {
disableInputButtons(true);
alert(error);
}
else if (connection.isInitiator === true) {
// if room doesn't exist, it means that current user will create the room
showRoomURL(roomid);
}
});
};
// ......................................................
// .......................UI Code ENDS........................
// ......................................................
// ***********************************************************
// ......................................................
// ..................RTCMultiConnection Code.............
// ......................................................
var connection = new RTCMultiConnection();
// by default, socket.io server is assumed to be deployed on your own URL
connection.socketURL = '/';
// comment-out below line if you do not have your own socket.io server
// connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/';
connection.socketMessageEvent = 'video-conference-demo';
connection.session = {
audio: true,
video: true
};
connection.sdpConstraints.mandatory = {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
};
// STAR_FIX_VIDEO_AUTO_PAUSE_ISSUES
// via: https://github.com/muaz-khan/RTCMultiConnection/issues/778#issuecomment-524853468
var bitrates = 512;
var resolutions = 'Ultra-HD';
var videoConstraints = {};
if (resolutions == 'HD') {
videoConstraints = {
width: {
ideal: 1280
},
height: {
ideal: 720
},
frameRate: 30
};
}
if (resolutions == 'Ultra-HD') {
videoConstraints = {
width: {
ideal: 1920
},
height: {
ideal: 1080
},
frameRate: 30
};
}
connection.mediaConstraints = {
video: videoConstraints,
audio: true
};
var CodecsHandler = connection.CodecsHandler;
connection.processSdp = function(sdp) {
var codecs = 'vp8';
if (codecs.length) {
sdp = CodecsHandler.preferCodec(sdp, codecs.toLowerCase());
}
if (resolutions == 'HD') {
sdp = CodecsHandler.setApplicationSpecificBandwidth(sdp, {
audio: 128,
video: bitrates,
screen: bitrates
});
sdp = CodecsHandler.setVideoBitrates(sdp, {
min: bitrates * 8 * 1024,
max: bitrates * 8 * 1024,
});
}
if (resolutions == 'Ultra-HD') {
sdp = CodecsHandler.setApplicationSpecificBandwidth(sdp, {
audio: 128,
video: bitrates,
screen: bitrates
});
sdp = CodecsHandler.setVideoBitrates(sdp, {
min: bitrates * 8 * 1024,
max: bitrates * 8 * 1024,
});
}
return sdp;
};
// END_FIX_VIDEO_AUTO_PAUSE_ISSUES
// https://www.rtcmulticonnection.org/docs/iceServers/
// use your own TURN-server here!
connection.iceServers = [{
'urls': [
'stun:stun.l.google.com:19302',
'stun:stun1.l.google.com:19302',
'stun:stun2.l.google.com:19302',
'stun:stun.l.google.com:19302?transport=udp',
]
}];
connection.videosContainer = document.getElementById('videos-container');
connection.onstream = function(specialEvent) {
// its just event but I was debugging it on the console and wanted to change the
// name to specialEvent
var existing = document.getElementById(specialEvent.streamid);
if(existing && existing.parentNode) {
existing.parentNode.removeChild(existing);
}
// specialEvent.mediaElement.removeAttribute('src');
// specialEvent.mediaElement.removeAttribute('srcObject');
specialEvent.mediaElement.muted = true;
specialEvent.mediaElement.volume = 0;
var video = document.createElement('video');
video.setAttribute("id", "cam_input");
window.video = video;
try {
video.setAttributeNode(document.createAttribute('autoplay'));
video.setAttributeNode(document.createAttribute('playsinline'));
} catch (e) {
video.setAttribute('autoplay', true);
video.setAttribute('playsinline', true);
}
if(specialEvent.type === 'local') {
video.volume = 0;
try {
video.setAttributeNode(document.createAttribute('muted'));
} catch (e) {
video.setAttribute('muted', true);
}
}
video.srcObject = specialEvent.stream;
var width = parseInt(connection.videosContainer.clientWidth / 3) - 20;
var mediaElement = getHTMLMediaElement(video, {
title: specialEvent.userid,
buttons: ['full-screen'],
width: width,
showOnMouseEnter: false
});
connection.videosContainer.appendChild(mediaElement);
setTimeout(function() {
mediaElement.media.play();
}, 5000);
mediaElement.id = specialEvent.streamid;
// to keep room-id in cache
localStorage.setItem(connection.socketMessageEvent, connection.sessionid);
chkRecordConference.parentNode.style.display = 'none';
if(chkRecordConference.checked === true) {
btnStopRecording.style.display = 'inline-block';
recordingStatus.style.display = 'inline-block';
var recorder = connection.recorder;
if(!recorder) {
recorder = RecordRTC([specialEvent.stream], {
type: 'video'
});
recorder.startRecording();
connection.recorder = recorder;
}
else {
recorder.getInternalRecorder().addStreams([specialEvent.stream]);
}
if(!connection.recorder.streams) {
connection.recorder.streams = [];
}
connection.recorder.streams.push(specialEvent.stream);
recordingStatus.innerHTML = 'Recording ' + connection.recorder.streams.length + ' streams';
}
if(specialEvent.type === 'local') {
connection.socket.on('disconnect', function() {
if(!connection.getAllParticipants().length) {
location.reload();
}
});
}
window.specialEvent = specialEvent;
};
// ******************************
//
// OPENCV CODE STARTS FROM HERE
//
// ******************************
function openCvReady() {
// cv['onRuntimeInitialized']=()=>{
// let video = document.getElementById("cam_input"); // video is the id of video tag
// navigator.mediaDevices.getUserMedia({ video: true, audio: false })
// .then(function(stream) {
// console.log(stream);
// video.srcObject = stream;
// video.play();
// })
// .catch(function(err) {
// console.log("An error occurred! " + err);
// });
// let cam_input = document.getElementById("cam_input");
let video = document.getElementById("cam_input");
let src = new cv.Mat(video.height, video.width, cv.CV_8UC4);
let dst = new cv.Mat(video.height, video.width, cv.CV_8UC1);
let gray = new cv.Mat();
let cap = new cv.VideoCapture(cam_input);
let faces = new cv.RectVector();
// if (face == true){
// console.log("face shown");
// }
let classifier = new cv.CascadeClassifier();
let utils = new Utils('errorMessage');
let faceCascadeFile = 'haarcascade_frontalface_default.xml'; // path to xml
utils.createFileFromUrl(faceCascadeFile, faceCascadeFile, () => {
classifier.load(faceCascadeFile); // in the callback, load the cascade from file
});
const FPS = 24;
function processVideo() {
let begin = Date.now();
cap.read(src);
src.copyTo(dst);
cv.cvtColor(dst, gray, cv.COLOR_RGBA2GRAY, 0);
try{
classifier.detectMultiScale(gray, faces, 1.1, 3, 0);
console.log(faces.size());
}catch(err){
console.log(err);
}
for (let i = 0; i < faces.size(); ++i) {
let face = faces.get(i);
console.log("face:");
console.log(face);
let point1 = new cv.Point(face.x, face.y);
let point2 = new cv.Point(face.x + face.width, face.y + face.height);
cv.rectangle(dst, point1, point2, [255, 0, 0, 255]);
}
cv.imshow("canvas_output", dst);
// schedule next one.
let delay = 1000/FPS - (Date.now() - begin);
setTimeout(processVideo, delay);
}
// schedule first one.
setTimeout(processVideo, 0);
// };
}
var recordingStatus = document.getElementById('recording-status');
var chkRecordConference = document.getElementById('record-entire-conference');
var btnStopRecording = document.getElementById('btn-stop-recording');
btnStopRecording.onclick = function() {
var recorder = connection.recorder;
if(!recorder) return alert('No recorder found.');
recorder.stopRecording(function() {
var blob = recorder.getBlob();
invokeSaveAsDialog(blob);
connection.recorder = null;
btnStopRecording.style.display = 'none';
recordingStatus.style.display = 'none';
chkRecordConference.parentNode.style.display = 'inline-block';
});
};
connection.onstreamended = function(event) {
var mediaElement = document.getElementById(event.streamid);
if (mediaElement) {
mediaElement.parentNode.removeChild(mediaElement);
}
};
connection.onMediaError = function(e) {
if (e.message === 'Concurrent mic process limit.') {
if (DetectRTC.audioInputDevices.length <= 1) {
alert('Please select external microphone. Check github issue number 483.');
return;
}
var secondaryMic = DetectRTC.audioInputDevices[1].deviceId;
connection.mediaConstraints.audio = {
deviceId: secondaryMic
};
connection.join(connection.sessionid);
}
};
// ..................................
// ALL below scripts are redundant!!!
// ..................................
function disableInputButtons(enable) {
document.getElementById('room-id').onkeyup();
document.getElementById('open-or-join-room').disabled = !enable;
document.getElementById('open-room').disabled = !enable;
document.getElementById('join-room').disabled = !enable;
document.getElementById('room-id').disabled = !enable;
}
// ......................................................
// ......................Handling Room-ID................
// ......................................................
function showRoomURL(roomid) {
var roomHashURL = '#' + roomid;
var roomQueryStringURL = '?roomid=' + roomid;
var html = '<h2>Unique URL for your room:</h2><br>';
html += 'Hash URL: ' + roomHashURL + '';
html += '<br>';
html += 'QueryString URL: ' + roomQueryStringURL + '';
var roomURLsDiv = document.getElementById('room-urls');
roomURLsDiv.innerHTML = html;
roomURLsDiv.style.display = 'block';
}
(function() {
var params = {},
r = /([^&=]+)=?([^&]*)/g;
function d(s) {
return decodeURIComponent(s.replace(/\+/g, ' '));
}
var match, search = window.location.search;
while (match = r.exec(search.substring(1)))
params[d(match[1])] = d(match[2]);
window.params = params;
})();
var roomid = '';
if (localStorage.getItem(connection.socketMessageEvent)) {
roomid = localStorage.getItem(connection.socketMessageEvent);
} else {
roomid = connection.token();
}
var txtRoomId = document.getElementById('room-id');
txtRoomId.value = roomid;
txtRoomId.onkeyup = txtRoomId.oninput = txtRoomId.onpaste = function() {
localStorage.setItem(connection.socketMessageEvent, document.getElementById('room-id').value);
};
var hashString = location.hash.replace('#', '');
if (hashString.length && hashString.indexOf('comment-') == 0) {
hashString = '';
}
var roomid = params.roomid;
if (!roomid && hashString.length) {
roomid = hashString;
}
if (roomid && roomid.length) {
document.getElementById('room-id').value = roomid;
localStorage.setItem(connection.socketMessageEvent, roomid);
// auto-join-room
(function reCheckRoomPresence() {
connection.checkPresence(roomid, function(isRoomExist) {
if (isRoomExist) {
connection.join(roomid);
return;
}
setTimeout(reCheckRoomPresence, 5000);
});
})();
disableInputButtons();
}
// detect 2G
if(navigator.connection &&
navigator.connection.type === 'cellular' &&
navigator.connection.downlinkMax <= 0.115) {
alert('2G is not supported. Please use a better internet service.');
}
</script>
<footer>
<small id="send-message"></small>
</footer>
<script src="https://www.webrtc-experiment.com/common.js"></script>
</body>
</html>
I have found quite a few similar qustions but they are mainly old and outdated and some of them provided a few pointers which I followed to no avail.
Basically, I'm tring to use webrtc and the phonegap iosrtc plugin to create a simple 2 way video chat.
I followed everything to the letter and I managed to show my own video in the app but the 'remote' video is not showing and I ant figure out what the issue is at all!
I have enabled the debugging in phonegap but I don't get any errors.
This is my complete code:
HTML:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://rtcmulticonnection.herokuapp.com/dist/RTCMultiConnection.min.js"></script>
<script src="https://rtcmulticonnection.herokuapp.com/node_modules/webrtc-adapter/out/adapter.js"></script>
<script src="https://rtcmulticonnection.herokuapp.com/socket.io/socket.io.js"></script>
<!-- custom layout for HTML5 audio/video elements -->
<link rel="stylesheet" href="https://rtcmulticonnection.herokuapp.com/dev/getHTMLMediaElement.css">
<script src="https://rtcmulticonnection.herokuapp.com/dev/getHTMLMediaElement.js"></script>
<script src="https://rtcmulticonnection.herokuapp.com/node_modules/recordrtc/RecordRTC.js"></script>
<script src="cordova.js"></script>
<script src="ios-websocket-hack.js"></script>
<section class="make-center">
<div>
<label><input type="checkbox" id="record-entire-conference"> Record Entire Conference In The Browser?</label>
<span id="recording-status" style="display: none;"></span>
<button id="btn-stop-recording" style="display: none;">Stop Recording</button>
<br><br>
<input type="text" id="room-id" value="abcdef" autocorrect=off autocapitalize=off size=20>
<button id="open-room">Open Room</button>
<button id="join-room">Join Room</button>
<button id="open-or-join-room">Auto Open Or Join Room</button>
</div>
<div id="videos-container" style="margin: 20px 0;"></div>
<div id="room-urls" style="text-align: center;display: none;background: #F1EDED;margin: 15px -10px;border: 1px solid rgb(189, 189, 189);border-left: 0;border-right: 0;"></div>
</section>
JAVASCRIPT:
// ......................................................
// .......................UI Code........................
// ......................................................
document.getElementById('open-room').onclick = function() {
disableInputButtons();
connection.open(document.getElementById('room-id').value, function(isRoomOpened, roomid, error) {
if(isRoomOpened === true) {
showRoomURL(connection.sessionid);
}
else {
disableInputButtons(true);
if(error === 'Room not available') {
alert('Someone already created this room. Please either join or create a separate room.');
return;
}
alert(error);
}
});
};
document.getElementById('join-room').onclick = function() {
disableInputButtons();
connection.join(document.getElementById('room-id').value, function(isJoinedRoom, roomid, error) {
if (error) {
disableInputButtons(true);
if(error === 'Room not available') {
alert('This room does not exist. Please either create it or wait for moderator to enter in the room.');
return;
}
alert(error);
}
});
};
document.getElementById('open-or-join-room').onclick = function() {
disableInputButtons();
connection.openOrJoin(document.getElementById('room-id').value, function(isRoomExist, roomid, error) {
if(error) {
disableInputButtons(true);
alert(error);
}
else if (connection.isInitiator === true) {
// if room doesn't exist, it means that current user will create the room
showRoomURL(roomid);
}
});
};
// ......................................................
// ..................RTCMultiConnection Code.............
// ......................................................
var connection = new RTCMultiConnection();
// by default, socket.io server is assumed to be deployed on your own URL
//connection.socketURL = '/';
// comment-out below line if you do not have your own socket.io server
connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/';
connection.socketMessageEvent = 'video-conference-demo';
connection.session = {
audio: true,
video: true
};
connection.sdpConstraints.mandatory = {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
};
connection.videosContainer = document.getElementById('videos-container');
connection.onstream = function(event) {
var existing = document.getElementById(event.streamid);
if(existing && existing.parentNode) {
existing.parentNode.removeChild(existing);
}
/*event.mediaElement.removeAttribute('src');
event.mediaElement.removeAttribute('srcObject');
event.mediaElement.muted = true;
event.mediaElement.volume = 0;*/
var video = document.createElement('video');
try {
video.setAttributeNode(document.createAttribute('autoplay'));
video.setAttributeNode(document.createAttribute('playsinline'));
} catch (e) {
video.setAttribute('autoplay', true);
video.setAttribute('playsinline', true);
}
if(event.type === 'local') {
video.setAttribute('class', 'myvideo');
//$('.yourVideo').attr('src', event.stream);
//$('.yourVideo').attr('id', event.streamid);
alert('local');
video.volume = 0;
try {
video.setAttributeNode(document.createAttribute('muted'));
} catch (e) {
video.setAttribute('muted', true);
}
}
if (event.type === 'remote') {
alert('remote');
video.setAttribute('class', 'othersvideo');
}
video.src = URL.createObjectURL(event.stream);
//var width = parseInt(connection.videosContainer.clientWidth / 3) - 20;
var width = $(document).width();
var height = $(document).height();
var mediaElement = getHTMLMediaElement(video, {
/*title: event.userid,*/
buttons: ['full-screen'],
width: 100,
height: 100,
showOnMouseEnter: false
});
connection.videosContainer.appendChild(mediaElement);
setTimeout(function() {
// mediaElement.media.play();
video.play();
}, 5000);
mediaElement.id = event.streamid;
// to keep room-id in cache
localStorage.setItem(connection.socketMessageEvent, connection.sessionid);
chkRecordConference.parentNode.style.display = 'none';
if(chkRecordConference.checked === true) {
btnStopRecording.style.display = 'inline-block';
recordingStatus.style.display = 'inline-block';
var recorder = connection.recorder;
if(!recorder) {
recorder = RecordRTC([event.stream], {
type: 'video'
});
recorder.startRecording();
connection.recorder = recorder;
}
else {
recorder.getInternalRecorder().addStreams([event.stream]);
}
if(!connection.recorder.streams) {
connection.recorder.streams = [];
}
connection.recorder.streams.push(event.stream);
recordingStatus.innerHTML = 'Recording ' + connection.recorder.streams.length + ' streams';
}
if(event.type === 'local') {
connection.socket.on('disconnect', function() {
if(!connection.getAllParticipants().length) {
location.reload();
}
});
}
};
var recordingStatus = document.getElementById('recording-status');
var chkRecordConference = document.getElementById('record-entire-conference');
var btnStopRecording = document.getElementById('btn-stop-recording');
btnStopRecording.onclick = function() {
var recorder = connection.recorder;
if(!recorder) return alert('No recorder found.');
recorder.stopRecording(function() {
var blob = recorder.getBlob();
invokeSaveAsDialog(blob);
connection.recorder = null;
btnStopRecording.style.display = 'none';
recordingStatus.style.display = 'none';
chkRecordConference.parentNode.style.display = 'inline-block';
});
};
connection.onstreamended = function(event) {
var mediaElement = document.getElementById(event.streamid);
if (mediaElement) {
mediaElement.parentNode.removeChild(mediaElement);
}
};
connection.onMediaError = function(e) {
if (e.message === 'Concurrent mic process limit.') {
if (DetectRTC.audioInputDevices.length <= 1) {
alert('Please select external microphone. Check github issue number 483.');
return;
}
var secondaryMic = DetectRTC.audioInputDevices[1].deviceId;
connection.mediaConstraints.audio = {
deviceId: secondaryMic
};
connection.join(connection.sessionid);
}
};
// ..................................
// ALL below scripts are redundant!!!
// ..................................
function disableInputButtons(enable) {
document.getElementById('room-id').onkeyup();
document.getElementById('open-or-join-room').disabled = !enable;
document.getElementById('open-room').disabled = !enable;
document.getElementById('join-room').disabled = !enable;
document.getElementById('room-id').disabled = !enable;
}
// ......................................................
// ......................Handling Room-ID................
// ......................................................
function showRoomURL(roomid) {
var roomHashURL = '#' + roomid;
var roomQueryStringURL = '?roomid=' + roomid;
var html = '<h2>Unique URL for your room:</h2><br>';
html += 'Hash URL: ' + roomHashURL + '';
html += '<br>';
html += 'QueryString URL: ' + roomQueryStringURL + '';
var roomURLsDiv = document.getElementById('room-urls');
roomURLsDiv.innerHTML = html;
roomURLsDiv.style.display = 'block';
}
(function() {
var params = {},
r = /([^&=]+)=?([^&]*)/g;
function d(s) {
return decodeURIComponent(s.replace(/\+/g, ' '));
}
var match, search = window.location.search;
while (match = r.exec(search.substring(1)))
params[d(match[1])] = d(match[2]);
window.params = params;
})();
var roomid = '';
if (localStorage.getItem(connection.socketMessageEvent)) {
roomid = localStorage.getItem(connection.socketMessageEvent);
} else {
roomid = connection.token();
}
var txtRoomId = document.getElementById('room-id');
txtRoomId.value = roomid;
txtRoomId.onkeyup = txtRoomId.oninput = txtRoomId.onpaste = function() {
localStorage.setItem(connection.socketMessageEvent, document.getElementById('room-id').value);
};
var hashString = location.hash.replace('#', '');
if (hashString.length && hashString.indexOf('comment-') == 0) {
hashString = '';
}
var roomid = params.roomid;
if (!roomid && hashString.length) {
roomid = hashString;
}
if (roomid && roomid.length) {
document.getElementById('room-id').value = roomid;
localStorage.setItem(connection.socketMessageEvent, roomid);
// auto-join-room
(function reCheckRoomPresence() {
connection.checkPresence(roomid, function(isRoomExist) {
if (isRoomExist) {
connection.join(roomid);
return;
}
setTimeout(reCheckRoomPresence, 5000);
});
})();
disableInputButtons();
}
// detect 2G
if(navigator.connection &&
navigator.connection.type === 'cellular' &&
navigator.connection.downlinkMax <= 0.115) {
alert('2G is not supported. Please use a better internet service.');
}
window.cordova.InAppBrowser.open(" https://vps267717.ovh.net/webrtc", "_blank", "location=no,toolbar=yes");
As you can see in my code, I have this:
if (event.type === 'remote') {
alert('remote');
video.setAttribute('class', 'othersvideo');
}
But this never fires for some reason!
I have spent the last 3 days trying to figure this out but I haven't had any luck.
So any help and advice would be appreciated.
Thanks in advance.
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>
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.
This code works in google chrome fine ,
but i tried to convert it to support Firefox
and i always get no error in the console .
the cam it seems ruining but there's no video.
here's the script
var socket = new WebSocket('ws://127.0.0.1:1338/'); // change the IP address to your websocket server
var stunServer = "stun.l.google.com:19302";
var sourcevid = document.getElementById('sourcevid');
var remotevid = document.getElementById('remotevid');
var localStream = null;
var remoteStream;
var peerConn = null;
var started = false;
var isRTCPeerConnection = true;
var mediaConstraints = {mandatory: {
OfferToReceiveAudio:true,
OfferToReceiveVideo:true }};
var logg = function(s) { console.log(s); };
// send the message to websocket server
function sendMessage(message) {
var mymsg = JSON.stringify(message);
logg("SEND: " + mymsg);
socket.send(mymsg);
}
function createPeerConnection() {
try {
logg("Creating peer connection");
var servers = [];
servers.push({'url':'stun:' + stunServer});
var pc_config = {'iceServers':servers};
peerConn = new webkitRTCPeerConnection(pc_config);
peerConn.onicecandidate = onIceCandidate;
} catch (e) {
try {
peerConn = new RTCPeerConnection('STUN ' + stunServer, onIceCandidate00);
isRTCPeerConnection = false;
} catch (e) {
logg("Failed to create PeerConnection, exception: " + e.message);
}
}
peerConn.onaddstream = onRemoteStreamAdded;
peerConn.onremovestream = onRemoteStreamRemoved;
}
// when remote adds a stream, hand it on to the local video element
function onRemoteStreamAdded(event) {
logg("Added remote stream");
remotevid.src = window.webkitURL.createObjectURL(event.stream);
}
function waitForRemoteVideo() {
if (remoteStream.videoTracks.length === 0 || remotevid.currentTime > 0) {
transitionToActive();
} else {
setTimeout(waitForRemoteVideo, 100);
}
}
function transitionToActive() {
remotevid.style.opacity = 1;
card.style.webkitTransform = "rotateY(180deg)";
setTimeout(function() { sourcevid.src = ""; }, 500);
setStatus("<input type=\"button\" id=\"hangup\" value=\"Hang up\" onclick=\"onHangup()\" />");
}
// when remote removes a stream, remove it from the local video element
function onRemoteStreamRemoved(event) {
logg("Remove remote stream");
remotevid.src = "";
}
function onIceCandidate(event) {
if (event.candidate) {
sendMessage({type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate});
} else {
logg("End of candidates.");
}
}
function onIceCandidate00(candidate, moreToFollow) {
if (candidate) {
sendMessage({type: 'candidate', label: candidate.label, candidate: candidate.toSdp()});
}
if (!moreToFollow) {
logg("End of candidates.");
}
}
// start the connection upon user request
function connect() {
if (!started && localStream) {
console.log("Creating PeerConnection.");
createPeerConnection();
logg('Adding local stream...');
peerConn.addStream(localStream);
started = true;
logg("isRTCPeerConnection: " + isRTCPeerConnection);
//create offer
if (isRTCPeerConnection) {
peerConn.createOffer(setLocalAndSendMessage, null, mediaConstraints);
} else {
var offer = peerConn.createOffer(mediaConstraints);
peerConn.setLocalDescription(peerConn.SDP_OFFER, offer);
sendMessage({type: 'offer', sdp: offer.toSdp()});
peerConn.startIce();
}
} else {
alert("Local stream not running yet.");
}
}
// accept connection request
socket.addEventListener("message", onMessage, false);
function onMessage(evt) {
logg("RECEIVED: " + evt.data);
if (isRTCPeerConnection)
processSignalingMessage(evt.data);
else
processSignalingMessage00(evt.data);
}
function processSignalingMessage(message) {
var msg = JSON.parse(message);
if (msg.type === 'offer') {
if (!started && localStream) {
createPeerConnection();
logg('Adding local stream...');
peerConn.addStream(localStream);
started = true;
logg("isRTCPeerConnection: " + isRTCPeerConnection);
if (isRTCPeerConnection) {
//set remote description
peerConn.setRemoteDescription(new RTCSessionDescription(msg));
//create answer
console.log("Sending answer to peer.");
peerConn.createAnswer(setLocalAndSendMessage, null, mediaConstraints);
} else {
//set remote description
peerConn.setRemoteDescription(peerConn.SDP_OFFER, new SessionDescription(msg.sdp));
//create answer
var offer = peerConn.remoteDescription;
var answer = peerConn.createAnswer(offer.toSdp(), mediaConstraints);
console.log("Sending answer to peer.");
setLocalAndSendMessage00(answer);
}
}
} else if (msg.type === 'answer' && started) {
peerConn.setRemoteDescription(new RTCSessionDescription(msg));
} else if (msg.type === 'candidate' && started) {
var candidate = new RTCIceCandidate({sdpMLineIndex:msg.label, candidate:msg.candidate});
peerConn.addIceCandidate(candidate);
} else if (msg.type == 'chat'){
addChatMsg(msg.nick, msg.cid, msg.data);
}
else if (msg.type === 'bye' && started) {
onRemoteHangUp();
}
}
function processSignalingMessage00(message) {
var msg = JSON.parse(message);
// if (msg.type === 'offer') --> will never happened since isRTCPeerConnection=true initially
if (msg.type === 'answer' && started) {
peerConn.setRemoteDescription(peerConn.SDP_ANSWER, new SessionDescription(msg.sdp));
} else if (msg.type === 'candidate' && started) {
var candidate = new IceCandidate(msg.label, msg.candidate);
peerConn.processIceMessage(candidate);
} else if (msg.type === 'bye' && started) {
onRemoteHangUp();
}
}
function setLocalAndSendMessage(sessionDescription) {
peerConn.setLocalDescription(sessionDescription);
sendMessage(sessionDescription);
}
function setLocalAndSendMessage00(answer) {
peerConn.setLocalDescription(peerConn.SDP_ANSWER, answer);
sendMessage({type: 'answer', sdp: answer.toSdp()});
peerConn.startIce();
}
function onRemoteHangUp() {
logg("Remote Hang up.");
closeSession();
}
function onHangUp() {
logg("Hang up.");
if (started) {
sendMessage({type: 'bye'});
closeSession();
}
}
function closeSession() {
peerConn.close();
peerConn = null;
started = false;
remotevid.src = "";
}
window.onbeforeunload = function() {
if (started) {
sendMessage({type: 'bye'});
}
}
function startVideo() {
// Replace the source of the video element with the stream from the camera
if (navigator.mozGetUserMedia) {
try {
navigator.mozGetUserMedia({audio: true, video: true}, successCallback, errorCallback);
} catch (e) {
navigator.mozGetUserMedia("video,audio", successCallback, errorCallback);
}
}
else {
try {
navigator.webkitGetUserMedia({audio: true, video: true}, successCallback, errorCallback);
} catch (e) {
navigator.webkitGetUserMedia("video,audio", successCallback, errorCallback);
}
}
function successCallback(stream) {
if (navigator.mozGetUserMedia) {
sourcevid.mozSrcObject = stream;
sourcevid.style.webkitTransform = "rotateY(180deg)";
localStream = stream;
}
if(navigator.webkitGetUserMedia){
sourcevid.src = window.webkitURL.createObjectURL(stream);
sourcevid.style.webkitTransform = "rotateY(180deg)";
localStream = stream;
}
}
function errorCallback(error) {
logg('An error occurred: [CODE ' + error.code + ']');
}
}
function stopVideo() {
sourcevid.src = "";
}
and here is the html
<script type="text/javascript" src="{{ asset('bundles/PFESiivt/js/visio.js') }}"></script>
<div id="main">
<div id="" style="height:280px;width:700;">
<div id="livevideodivk" style="float:left;">
<video id="sourcevid" style="height:280px;width:320px;" autoplay></video>
</div>
<div id="remotevideodivk" style="float:left;margin-left:10px">
<video id="remotevid" style="height:280px;width:320px;" autoplay></video>
</div>
</div>
<center>
<button id="btn" type="button" onclick="startVideo();">Start video</button>
<button id="btn" type="button" onclick="stopVideo();">Stop video</button>
<button id="btn" type="button" onclick="connect();">Connect</button>
<button id="btn" type="button" onclick="onHangUp();">Hang Up</button>
</center>
</div>
Have not gone through the complete code, but for starters...
for firefox it is mozRTCPeerConnection not RTCPeerConnection.
secondly, for firefox PeerConnection object, the onicecandidate handler is missing.
P. S: I think it is a very bad idea to post complete code, would advice to do bit of debugging yourself( to identify the block causing issue) and then post the relevant block when not able to solve it.