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.
Related
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 used Angular JS : - Getting Room Id/Token from server side to connect web socket
Following code used in application -
app.controller("videoCallingController",
["$scope", "$location", "$rootScope", "$localStorage", 'AuthenticationService', "CommonService", "videoService",
function ($scope, $location, $rootScope, $localStorage, AuthenticationService, CommonService, videoService) {
$scope.UserInfo = getLoggedInUserDetails();
if ($scope.UserInfo == null) {
window.location.replace("/login.html#/");
}
$scope.offerConversationDetails = JSON.parse(localStorage.getItem('offerConversationDetails')); //$rootScope.offerConversationDetails;
$scope.IsCallBtn = false;
$scope.IsCallEnd = false;
$scope.IsRemoteStreaming = false;
//------- start custom Web socket ----------------
var ws;
function initWS(groupname) {
// var groupname = "TestRoom";
ws = new WebSocket("wss://" + location.host + "/api/" + "api/Values/Get?id=" + groupname);
ws.onopen = function () { };
ws.onmessage = function (evt) {
var signal = null;
if (!pc1) { answerCall(); }
signal = JSON.parse(evt.data);
if (signal.sdp) {
console.log("Received SDP from remote peer.");
pc1.setRemoteDescription(new RTCSessionDescription(signal.sdp));
createAndSendAnswer(signal.sdp);
}
else if (signal.candidate) {
console.log("Received ICECandidate from remote peer.");
//pc1.addIceCandidate(new RTCIceCandidate(signal.candidate));
addIceCandidate(signal);
} else if (signal.closeConnection) {
console.log("Received 'close call' signal from remote peer.");
//endCall();
}
};
ws.onerror = function (evt) {
console.log(evt.message);
};
ws.onclose = function () {
console.log("disconnected");
};
}
function sendWS(msg) {
if (ws.readyState == WebSocket.OPEN) {
ws.send(msg);
}
}
function closeWS() {
ws.close();
}
//------- end custom Web socket ----------------
//---- start Html element selectors ------------
//var callButton = document.getElementById('callButton');
//callButton.onclick = initiateCall;
var startTime;
var localVideo = document.getElementById('localVideo');
var 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;
}
};
//---- end Html element selectors ------------
var localStream;
var pc1;
// var pc2;
var offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
};
function getName(pc) {
return 'pc1'; //(pc === pc1) ? 'pc1' : 'pc2';
}
function getOtherPc(pc) {
return pc1; //(pc === pc1) ? pc2 : pc1;
}
function gotStream(stream) {
trace('Received local stream');
//localVideo = attachMediaStream(localVideo, stream);
// attachMediaStream(localVideo, stream);
console.log(attachMediaStream(localVideo, stream));
localStream = stream;
// callButton.disabled = false;
muteAudio(stream);
}
function muteAudio(stream) {
var AudioTrack = stream.getAudioTracks()[0];
//var localAudioBtn = document.getElementById('localAudioBtn');
var muteBtn = document.getElementById('localAudioMuteBtn');
var unMuteBtn = document.getElementById('localAudioUnMuteBtn');
//localAudioBtn.style.visibility = 'visible';
unMuteBtn.onclick = MuteUnMute;
muteBtn.onclick = MuteUnMute;
function MuteUnMute() {
AudioTrack.enabled = !AudioTrack.enabled;
muteBtn.className = 'btn btn-info btn-lg' + (AudioTrack.enabled ? '' : ' hidden');
unMuteBtn.className = 'btn btn-warning btn-lg' + (AudioTrack.enabled ? ' hidden' : '');
//localAudioBtn.className = 'button button-mute' + (AudioTrack.enabled ? '' : ' muted');
};
}
function gumFailed(e) {
alert('getUserMedia() error: ' + e.name);
}
function start() {
trace('Requesting local stream');
// startButton.disabled = true;
var constraints = {
audio: true,
video: { width: 1280, height: 720 } //true
};
if (typeof Promise === 'undefined') {
navigator.getUserMedia(constraints, gotStream, gumFailed);
} else {
navigator.mediaDevices.getUserMedia(constraints)
.then(gotStream)
.catch(gumFailed);
}
}
var Room = {};
Room.createNewRoom = function () {
if ($scope.offerConversationDetails.type == 'owner') {
videoService.CreateNewRoom($scope.offerConversationDetails.offerId, function (resp) {
if (resp.status === iresponseStatus.success) {
console.log("CreateNewRoom");
console.log(resp.result);
$scope.roomDetails = resp.result;
$rootScope.offerRoomId = $scope.roomDetails.RoomId;
Room.getRoomStatusById($scope.roomDetails.RoomId);
}
else if (resp.status === iresponseStatus.error) {
if (resp.error.code == 5) {
callDateTimeExpired(resp.error.message);
}
displayNoty("error", resp.error.message);
console.log(resp.error);
setTimeout(function () { Room.createNewRoom(); }, 10000);
}
});
}
}
Room.getRoomToken = function () {
if ($scope.offerConversationDetails.type == 'winner') {
videoService.GetRoomToken($scope.offerConversationDetails.offerId, function (resp) {
if (resp.status === iresponseStatus.success) {
console.log("Get Room Token");
console.log(resp.result);
$scope.roomDetails = resp.result;
$rootScope.offerRoomId = $scope.roomDetails.RoomId;
initWS($scope.roomDetails.RoomToken);
}
else if (resp.status === iresponseStatus.error) {
console.log(resp.error);
if (resp.error.code == 5) {
callDateTimeExpired(resp.error.message);
}
setTimeout(function () { Room.getRoomToken(); }, 1000);
}
});
}
}
Room.getRoomStatusById = function (roomId) {
if ($scope.offerConversationDetails.type == 'owner') {
videoService.GetRoomStatusById(roomId, function (resp) {
if (resp.status === iresponseStatus.success) {
if (resp.result == 2) //Active
{
initWS($scope.roomDetails.RoomToken);
$scope.IsCallBtn = true;
}
else {
//setTimeout(Room.getRoomStatusById(roomId), 100);
setTimeout(function () { Room.getRoomStatusById(roomId); }, 100);
}
}
else if (resp.status === iresponseStatus.error) {
console.log(resp.error);
if (resp.error.code == 5) {
callDateTimeExpired(resp.error.message);
}
//setTimeout(Room.getRoomStatusById(roomId), 100);
setTimeout(function () { Room.getRoomStatusById(roomId); }, 100);
}
});
}
}
Room.updateRoomConsumption = function () {
if ($scope.offerConversationDetails.type == 'owner') {
updateRoomConsumption_interval = setInterval(UpdateRoomConsumption(), 5000);
}
}
var updateRoomConsumption_interval = null;
function UpdateRoomConsumption() {
if ($scope.offerConversationDetails.ChatDurationSeconds > $scope.seconds) {
videoService.UpdateRoomConsumption($scope.roomDetails.RoomId, $scope.seconds, function (resp) {
if (resp.status === iresponseStatus.success) {
// $scope.seconds += 5;
//setTimeout(function () { Room.updateRoomConsumption(); }, 5000);
}
else if (resp.status === iresponseStatus.error) {
console.log(resp.error);
}
});
}
else {
EndCall();
clearInterval(updateRoomConsumption_interval);
}
$scope.seconds = $scope.seconds + 5;
}
//initWS();
start();
Room.createNewRoom();
Room.getRoomToken();
function prepareCall() {
// callButton.disabled = true;
trace('Starting call');
startTime = 4410; //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);
}
// var servers = null;
var servers = {
iceServers: [
//{ url: "stun:23.21.150.121" },
{ url: "stun:stun.1.google.com:19302" }
//{ url: !isFirefox ? 'stun:stun.l.google.com:19302' : 'stun:23.21.150.121' }
]
};
pc1 = new RTCPeerConnection(servers);
trace('Created local peer connection object pc1');
pc1.onicecandidate = function (e) {
onIceCandidate(pc1, e);
};
// pc2 = new RTCPeerConnection(servers);
//trace('Created remote peer connection object pc2');
//pc2.onicecandidate = function (e) {
// onIceCandidate(pc2, e);
//};
pc1.oniceconnectionstatechange = function (e) {
onIceStateChange(pc1, e);
};
//pc2.oniceconnectionstatechange = function (e) {
// onIceStateChange(pc2, e);
//};
pc1.onaddstream = gotRemoteStream;
};
// run start(true) to initiate a call
$scope.initiateCall = function () {
prepareCall();
pc1.addStream(localStream);
trace('Added local stream to pc1');
trace('pc1 createOffer start');
pc1.createOffer(onCreateOfferSuccess, onCreateSessionDescriptionError,
offerOptions);
};
function answerCall() {
prepareCall();
pc1.addStream(localStream);
trace('Added local stream to pc1');
//createAndSendAnswer();
};
function onCreateSessionDescriptionError(error) {
trace('Failed to create session description: ' + error.toString());
}
function onCreateOfferSuccess(desc) {
trace('Offer from pc1\n' + desc.sdp);
trace('pc1 setLocalDescription start');
pc1.setLocalDescription(desc, function () {
onSetLocalSuccess(pc1);
sendWS(JSON.stringify({ "sdp": desc }));
}, onSetSessionDescriptionError);
}
function createAndSendAnswer(desc) {
// trace('pc2 setRemoteDescription start');
pc1.setRemoteDescription(desc, function () {
onSetRemoteSuccess(pc1);
}, onSetSessionDescriptionError);
trace('pc2 createAnswer start');
//Since the 'remote' side has no media stream we need
// to pass in the right constraints in order for it to
// accept the incoming offer of audio and video.
pc1.createAnswer(onCreateAnswerSuccess, onCreateSessionDescriptionError);
};
function onSetLocalSuccess(pc) {
trace(getName(pc) + ' setLocalDescription complete');
}
function onSetRemoteSuccess(pc) {
trace(getName(pc) + ' setRemoteDescription complete');
}
function onSetSessionDescriptionError(error) {
trace('Failed to set session description: ' + error.toString());
}
function gotRemoteStream(event) {
// remoteVideo = attachMediaStream(remoteVideo, e.stream);
if (event != null && event != undefined && event.stream != null && event.stream != undefined) {
$scope.offerConversationDetails.ChatDurationSeconds = $scope.offerConversationDetails.ChatDuration * 60;
$scope.IsDisplayTimer = true; $scope.IsRemoteStreaming = true; $scope.IsCallBtn = false;
$scope.$apply();
initializeClock('clockdiv', $scope.offerConversationDetails.ChatDurationSeconds);
$scope.seconds = 0;
Room.updateRoomConsumption($scope.seconds);
//event.stream.oninactive = function ()
//{
// if ($scope.IsCallEnd == false)
// hangup();
// alert("remote streaming get stop");
//}
}
console.log(attachMediaStream(remoteVideo, event.stream));
trace('pc2 received remote stream');
}
function onCreateAnswerSuccess(desc) {
//trace('Answer from pc2:\n' + desc.sdp);
//trace('pc2 setLocalDescription start');
//pc2.setLocalDescription(desc, function () {
// onSetLocalSuccess(pc2);
//}, onSetSessionDescriptionError);
trace('pc1 setRemoteDescription start');
pc1.setLocalDescription(desc, function () {
onSetRemoteSuccess(pc1);
sendWS(JSON.stringify({ "sdp": desc }));
}, onSetSessionDescriptionError);
}
function onIceCandidate(pc, event) {
if (!event || !event.candidate) return;
sendWS(JSON.stringify({ "candidate": event.candidate }));
//var candidate = event.candidate;
//if (candidate) {
// getOtherPc(pc).addIceCandidate(new RTCIceCandidate(event.candidate),
// function () {
// onAddIceCandidateSuccess(pc);
// },
// function (err) {
// onAddIceCandidateError(pc, err);
// }
// );
// trace(getName(pc) + ' ICE candidate: \n' + event.candidate.candidate);
//}
}
function addIceCandidate(event) {
pc1.addIceCandidate(new RTCIceCandidate(event.candidate),
function () {
onAddIceCandidateSuccess(pc1);
},
function (err) {
onAddIceCandidateError(pc1, err);
}
);
trace(pc1 + ' ICE candidate: \n' + event.candidate.candidate);
}
function onAddIceCandidateSuccess(pc) {
trace(getName(pc) + ' addIceCandidate success');
}
function onAddIceCandidateError(pc, error) {
trace(getName(pc) + ' failed to add ICE Candidate: ' + error.toString());
}
function onIceStateChange(pc, event) {
if (pc) {
trace(getName(pc) + ' ICE state: ' + pc.iceConnectionState);
console.log('ICE state change event: ', event);
if (pc.iceConnectionState == 'disconnected' || pc.iceConnectionState == 'failed') {
EndCall();
}
}
}
function EndCall() {
hangup();
console.log("chatduration =" + $scope.offerConversationDetails.ChatDurationSeconds);
console.log("consumption =" + $scope.seconds);
//if ($scope.IsManuallyEnded != true)
//{
alertify.alert('Mepleez Conversation', 'Your conversation got finished. Please give conversation feedback to us.',
function () {
//hangup();
window.location.replace("/video.html#/experience");
});
//}
}
function callDateTimeExpired(message) {
alertify.alert('Mepleez Conversation', message,
function () {
// window.location.replace("/video.html#/experience");
window.location.replace("/index.html#/");
});
}
$scope.onEndCallBtnClick = function () {
//alertify.prompt('Cancel call', 'Please enter the reason for cancelling call', 'Busy with other stuff '
// , function (evt, value) {
// }
// , function () { });
alertify.confirm('End Call', 'Are you sure?', function () {
$scope.IsManuallyEnded = true;
hangup();
window.location.replace("/video.html#/experience");
}
, function () { });
}
function hangup() {
$scope.IsCallEnd = true;
trace('Ending call');
pc1.close();
// pc2.close();
pc1 = null;
// pc2 = null;
//hangupButton.disabled = true;
// callButton.disabled = false;
localStream.getTracks().forEach(function (track) {
track.stop();
});
localVideo.src = "";
remoteVideo.src = "";
closeWS();
// window.location.replace("/video.html#/experience");
}
$scope.dragOptions = {
start: function (e) {
console.log("STARTING");
},
drag: function (e) {
console.log("DRAGGING");
},
stop: function (e) {
console.log("STOPPING");
},
container: 'container'
}
There is some special characters will be getting in SDP after serlizing it.
So to remove it use below code
// Workaround
function maybeAddLineBreakToEnd(sdp) {
var endWithLineBreak = new RegExp(/\n$/);
if (!endWithLineBreak.test(sdp)) {
return sdp + '\n';
}
return sdp;
}
function gotDescription(desc) {
var offer = desc;
offerSdpTextarea.value = desc.sdp;
var sdp = offerSdpTextarea.value;
sdp = maybeAddLineBreakToEnd(sdp);
console.log(sdp);
sdp = sdp.replace(/\n/g, '\r\n');
offer.sdp = sdp;
pc1.setLocalDescription(offer,
onSetOfferSDPSuccess,
onSetSDPError);
trace('Modified Offer from localPeerConnection \n' + sdp);
// sendWS(JSON.stringify({ "sdp": desc }));
sendWS(JSON.stringify(offer));
}
I'm setting my video-conference service for academic purposes using WebRTC and everything works fine except for the audio capture. When I go to my website, it will only ask permission for the video camera, but not for the audio, and of course it just won't be any audio...
My main.js
'use strict';
var isChannelReady = false;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var turnReady;
var pcConfig = {
'iceServers': [{
'urls': 'stun:stun.l.google.com:19302'
}]
};
// Set up audio and video regardless of what devices are present.
var sdpConstraints = {
offerToReceiveAudio: true,
offerToReceiveVideo: true
};
/////////////////////////////////////////////
var room = 'foo';
// Could prompt for room name:
// room = prompt('Enter room name:');
var socket = io.connect();
if (room !== '') {
socket.emit('create or join', room);
console.log('Attempted to create or join room', room);
}
socket.on('created', function(room) {
console.log('Created room ' + room);
isInitiator = true;
});
socket.on('full', function(room) {
console.log('Room ' + room + ' is full');
});
socket.on('join', function (room){
console.log('Another peer made a request to join room ' + room);
console.log('This peer is the initiator of room ' + room + '!');
isChannelReady = true;
});
socket.on('joined', function(room) {
console.log('joined: ' + room);
isChannelReady = true;
});
socket.on('log', function(array) {
console.log.apply(console, array);
});
////////////////////////////////////////////////
function sendMessage(message) {
console.log('Client sending message: ', message);
socket.emit('message', message);
}
// This client receives a message
socket.on('message', function(message) {
console.log('Client received message:', message);
if (message === 'got user media') {
maybeStart();
} else if (message.type === 'offer') {
if (!isInitiator && !isStarted) {
maybeStart();
}
pc.setRemoteDescription(new RTCSessionDescription(message));
doAnswer();
} else if (message.type === 'answer' && isStarted) {
pc.setRemoteDescription(new RTCSessionDescription(message));
} else if (message.type === 'candidate' && isStarted) {
var candidate = new RTCIceCandidate({
sdpMLineIndex: message.label,
candidate: message.candidate
});
pc.addIceCandidate(candidate);
} else if (message === 'bye' && isStarted) {
handleRemoteHangup();
}
});
////////////////////////////////////////////////////
var localVideo = document.querySelector('#localVideo');
var remoteVideo = document.querySelector('#remoteVideo');
navigator.mediaDevices.getUserMedia({
audio: true,
video: true
})
.then(gotStream)
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
function gotStream(stream) {
console.log('Adding local stream.');
localVideo.src = window.URL.createObjectURL(stream);
localStream = stream;
sendMessage('got user media');
if (isInitiator) {
maybeStart();
}
}
var constraints = {
audio: true,
video: true
};
console.log('Getting user media with constraints', constraints);
if (location.hostname !== 'localhost') {
requestTurn(
'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913'
);
}
function maybeStart() {
console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
console.log('>>>>>> creating peer connection');
createPeerConnection();
pc.addStream(localStream);
isStarted = true;
console.log('isInitiator', isInitiator);
if (isInitiator) {
doCall();
}
}
}
window.onbeforeunload = function() {
sendMessage('bye');
};
/////////////////////////////////////////////////////////
function createPeerConnection() {
try {
pc = new RTCPeerConnection(null);
pc.onicecandidate = handleIceCandidate;
pc.onaddstream = handleRemoteStreamAdded;
pc.onremovestream = handleRemoteStreamRemoved;
console.log('Created RTCPeerConnnection');
} catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
alert('Cannot create RTCPeerConnection object.');
return;
}
}
function handleIceCandidate(event) {
console.log('icecandidate event: ', event);
if (event.candidate) {
sendMessage({
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate
});
} else {
console.log('End of candidates.');
}
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleCreateOfferError(event) {
console.log('createOffer() error: ', event);
}
function doCall() {
console.log('Sending offer to peer');
pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}
function doAnswer() {
console.log('Sending answer to peer.');
pc.createAnswer().then(
setLocalAndSendMessage,
onCreateSessionDescriptionError
);
}
function setLocalAndSendMessage(sessionDescription) {
// Set Opus as the preferred codec in SDP if Opus is present.
// sessionDescription.sdp = preferOpus(sessionDescription.sdp);
pc.setLocalDescription(sessionDescription);
console.log('setLocalAndSendMessage sending message', sessionDescription);
sendMessage(sessionDescription);
}
function onCreateSessionDescriptionError(error) {
trace('Failed to create session description: ' + error.toString());
}
function requestTurn(turnURL) {
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 ', turnURL);
// No TURN server. Get one from computeengineondemand.appspot.com:
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', turnURL, true);
xhr.send();
}
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleRemoteStreamRemoved(event) {
console.log('Remote stream removed. Event: ', event);
}
function hangup() {
console.log('Hanging up.');
stop();
sendMessage('bye');
}
function handleRemoteHangup() {
console.log('Session terminated.');
stop();
isInitiator = false;
}
function stop() {
isStarted = false;
// isAudioMuted = false;
// isVideoMuted = false;
pc.close();
pc = null;
}
///////////////////////////////////////////
// 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) {
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;
}
and my index.js (server)
var fs = require('fs');
var http = require('http');
var https = require('https');
var cors = require('cors');
var privateKey = fs.readFileSync('host.key', 'utf8');
var certificate = fs.readFileSync('host.cert', 'utf8');
var os = require('os');
var credentials = {key: privateKey, cert: certificate};
var express = require('express');
var app = express();
app.use(cors());
var path = require('path');
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
//console.log('Servidor en marxa.');
});
console.log('Servidor ON');
var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials,app);
httpServer.listen(80,"0.0.0.0");
httpsServer.listen(443,"0.0.0.0");
var io = require("socket.io");
io = io.listen(httpServer);
io = io.listen(httpsServer);
//EL SERVER ESTA CONFIGURAT I FUNCIONA//
var usersConnected = [];
var nUsersConnected = 0;
io.sockets.on('connection',function(socket){
function log() {
var array = ['Message from server:'];
array.push.apply(array, arguments);
socket.emit('log', array);
}
usersConnected.push(socket.id);
nUsersConnected++;
log('Welcome to WebRTC Server: Miguel & Pavel');
console.log('New user connected: '+socket.id+' #Users connected = '+nUsersConnected);
socket.on('message', function(message) {
log('Client said: ', message);
// for a real app, would be room-only (not broadcast)
socket.broadcast.emit('message', message);
});
socket.on('create or join', function(room) {
log('Received request to create or join room ' + room);
var numClients = io.sockets.sockets.length;
log('Room ' + room + ' now has ' + numClients + ' client(s)');
if (numClients === 1) {
socket.join(room);
log('Client ID ' + socket.id + ' created room ' + room);
socket.emit('created', room, socket.id);
} else if (numClients === 2) {
log('Client ID ' + socket.id + ' joined room ' + room);
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room, socket.id);
io.sockets.in(room).emit('ready');
} else { // max two clients
socket.emit('full', room);
}
});
socket.on('ipaddr', function() {
var ifaces = os.networkInterfaces();
for (var dev in ifaces) {
ifaces[dev].forEach(function(details) {
if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
socket.emit('ipaddr', details.address);
}
});
}
});
socket.on('disconnect', function (message) {
nUsersConnected--;
console.log('User: '+socket.id+' disconnected #Users connected = '+nUsersConnected);
/*if(usuari1 == socket.id){
io.to(usuari2).emit('message', {type:'hangup'});
}else{
io.to(usuari1).emit('message', {type:'hangup'});
}*/
//tots els usuaris s'han desconnectat
if(nUsersConnected == 0){
console.log('All users have been deleted');
nUsersConnected = 0;
usersConnected = [];
}
})
socket.on('bye', function(){
console.log('received bye');
});
});
Thanks!
When I go to my website, it will only ask permission for the video camera, but not for the audio, and of course it just won't be any audio
Am suspecting issue with your system microphone.
If your microphone is proper, then your localstream should have audioTracks localStream.getAudioTracks(). And if we unmute localVideo element, it should echo the audio.
Try this demo https://webrtc.github.io/samples/src/content/devices/input-output/ to test your microphone. Update your question with the results.
Google stopped providing turn servers with below url, so you need to setup your TurnServer. Try CoTURN.
requestTurn(
'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913'
);
I am making a WebRTC video chat application and it was working before i started to add or subtract more code and in the process i deleted or changed the order in a way that now i am getting this error. Sadly i don't have a backup code and it has consumed so much of my time.
You will need socket.io and node-static packages installed on your node server.
Okay i managed to solve the issue with the error but now clients are not connecting with each other, it seems two clients are unable to exchange messages via the server.
My server.js code is below
var static = require('node-static');
var http = require('http');
var file = new(static.Server)();
var app = http.createServer(function (req, res) {
file.serve(req, res);
}).listen(8080, '127.0.0.1');
var io = require('socket.io').listen(app);
io.sockets.on('connection', function (socket){
socket.emit('emit(): client ' + socket.id + ' joined room ' + room);
socket.broadcast.emit('broadcast(): client ' + socket.id + ' joined room ' + room);
function log(){
var array = [">>> Message from server: "];
for (var i = 0; i < arguments.length; i++) {
array.push(arguments[i]);
}
socket.emit('log', array);
}
socket.on('message', function (message) {
log('Got message: ', message);
// For a real app, should be room only (not broadcast)
socket.broadcast.emit('message', message);
});
socket.on('create or join', function (room) {
var numClients = io.sockets.clients(room).length;
log('Room ' + room + ' has ' + numClients + ' client(s)');
log('Request to create or join room', room);
if (numClients == 0){
socket.join(room);
socket.emit('created', room);
} else if (numClients == 1) {
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room);
} else { // max two clients
socket.emit('full', room);
}
});
});
my application.js file is as follows
'use strict';
var isChannelReady;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var pc_config = webrtcDetectedBrowser === 'firefox' ?
{'iceServers':[{'url':'stun:23.21.150.121'}]} : // number IP
{'iceServers': [{'url': 'stun:stun.l.google.com:19302'}]};
var pc_constraints = {'optional': [{'DtlsSrtpKeyAgreement': true}]};
// Set up audio and video regardless of what devices are present.
var sdpConstraints = {'mandatory': {
'OfferToReceiveAudio':true,
'OfferToReceiveVideo':true }};
var room = location.pathname.substring(1);
if (room === '') {
room = window.prompt('Enter room name:');
room = '';
}
var socket = io.connect();
if (room !== '') {
console.log('Create or join room', room);
socket.emit('create or join', room);
}
socket.on('created', function (room){
console.log('Created room ' + room);
isInitiator = true;
});
socket.on('full', function (room){
console.log('Room ' + room + ' is full');
});
socket.on('join', function (room){
console.log('Another peer made a request to join room ' + room);
console.log('This peer is the initiator of room ' + room + '!');
isChannelReady = true;
});
socket.on('joined', function (room){
console.log('This peer has joined room ' + room);
isChannelReady = true;
});
socket.on('log', function (array){
console.log.apply(console, array);
});
////////////////////////////////////////////////
function sendMessage(message){
console.log('Client sending message: ', message);
// if (typeof message === 'object') {
// message = JSON.stringify(message);
// }
socket.emit('message', message);
}
socket.on('message', function (message){
console.log('Client received message:', message);
if (message === 'got user media') {
maybeStart();
} else if (message.type === 'offer') {
if (!isInitiator && !isStarted) {
maybeStart();
}
pc.setRemoteDescription(new RTCSessionDescription(message));
doAnswer();
} else if (message.type === 'answer' && isStarted) {
pc.setRemoteDescription(new RTCSessionDescription(message));
} else if (message.type === 'candidate' && isStarted) {
var candidate = new RTCIceCandidate({
sdpMLineIndex: message.label,
candidate: message.candidate
});
pc.addIceCandidate(candidate);
} else if (message === 'bye' && isStarted) {
handleRemoteHangup();
}
});
////////////////////////////////////////////////////
function handleUserMedia(stream) {
console.log('Adding local stream.');
localVideo.src = window.URL.createObjectURL(stream);
localStream = stream;
sendMessage('got user media');
if (isInitiator) {
maybeStart();
}
}
function handleUserMediaError(error){
console.log('getUserMedia error: ', error);
}
var constraints = {video: true, audio:true};
getUserMedia(constraints, handleUserMedia, handleUserMediaError);
console.log('Getting user media with constraints', constraints);
function maybeStart() {
if (!isStarted && typeof localStream != 'undefined' && isChannelReady) {
createPeerConnection();
pc.addStream(localStream);
isStarted = true;
console.log('isInitiator', isInitiator);
if (isInitiator) {
doCall();
}
}
}
window.onbeforeunload = function(e){
sendMessage('bye');
}
/////////////////////////////////////////////////////////
function createPeerConnection() {
try {
pc = new RTCPeerConnection(null);
pc.onicecandidate = handleIceCandidate;
pc.onaddstream = handleRemoteStreamAdded;
pc.onremovestream = handleRemoteStreamRemoved;
console.log('Created RTCPeerConnnection');
} catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
alert('Cannot create RTCPeerConnection object.');
return;
}
}
function handleIceCandidate(event) {
console.log('handleIceCandidate event: ', event);
if (event.candidate) {
sendMessage({
type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate});
} else {
console.log('End of candidates.');
}
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleCreateOfferError(event){
console.log('createOffer() error: ', e);
}
function doCall() {
console.log('Sending offer to peer');
pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}
function doAnswer() {
console.log('Sending answer to peer.');
pc.createAnswer(setLocalAndSendMessage, null, sdpConstraints);
}
function setLocalAndSendMessage(sessionDescription) {
// Set Opus as the preferred codec in SDP if Opus is present.
sessionDescription.sdp = preferOpus(sessionDescription.sdp);
pc.setLocalDescription(sessionDescription);
console.log('setLocalAndSendMessage sending message' , sessionDescription);
sendMessage(sessionDescription);
}
function handleRemoteStreamAdded(event) {
console.log('Remote stream added.');
remoteVideo.src = window.URL.createObjectURL(event.stream);
remoteStream = event.stream;
}
function handleRemoteStreamRemoved(event) {
console.log('Remote stream removed. Event: ', event);
}
function hangup() {
console.log('Hanging up.');
stop();
sendMessage('bye');
}
function handleRemoteHangup() {
// console.log('Session terminated.');
// stop();
// isInitiator = false;
}
function stop() {
isStarted = false;
// isAudioMuted = false;
// isVideoMuted = false;
pc.close();
pc = null;
}
///////////////////////////////////////////
// Set Opus as the default audio codec if it's present.
function preferOpus(sdp) {
var sdpLines = sdp.split('\r\n');
var mLineIndex = null;
// 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;
}
Index.html
<!DOCTYPE html>
<html>
<head>
<meta name='keywords' content='WebRTC, HTML5, JavaScript' />
<meta name='description' content='WebRTC Reference App' />
<meta name='viewport' content='width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1'>
<base target='_blank'>
<title>WebRTC client</title>
<link rel='stylesheet' href='css/style.css' />
</head>
<body id='body'>
<p style=" font-size:24px" align="center">WebRTC Video Share</p>
<div id='container'>
<div>
<video id='localVideo' autoplay muted></video>
<video id='remoteVideo' autoplay></video>
</div>
</div>
<script src='/socket.io/socket.io.js'></script>
<script src='js/lib/adapter.js'></script>
<script src='js/main.js'></script>
</body>
</html>
In your server.js code:
var io = require('socket.io').listen(app);
io.sockets.on('connection', function (socket){
socket.emit('emit(): client ' + socket.id + ' joined room ' + room);
at the end you refer to a variable room, but you haven't created it yet.
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.