![JAR search and dependency download from the Maven repository](/logo.png)
static.js.KurentoRoom.js Maven / Gradle / Ivy
// Room --------------------------------
function jq( myid ) {
return "#" + myid.replace( /(@|:|\.|\[|\]|,)/g, "\\$1" );
}
function Room(kurento, options) {
var that = this;
that.name = options.room;
var ee = new EventEmitter();
var streams = {};
var participants = {};
var connected = false;
var localParticipant;
var subscribeToStreams = options.subscribeToStreams || true;
this.getLocalParticipant = function () {
return localParticipant;
}
this.addEventListener = function (eventName, listener) {
ee.addListener(eventName, listener);
}
this.emitEvent = function (eventName, eventsArray) {
ee.emitEvent(eventName, eventsArray);
}
this.connect = function () {
kurento.sendRequest('joinRoom', {
user: options.user,
room: options.room
}, function (error, response) {
if (error) {
ee.emitEvent('error-room', [{
error: error
}]);
//console.error(error);
} else {
connected = true;
var exParticipants = response.value;
var roomEvent = {
participants: [],
streams: []
}
var length = exParticipants.length;
for (var i = 0; i < length; i++) {
var participant = new Participant(kurento, false, that,
exParticipants[i]);
participants[participant.getID()] = participant;
roomEvent.participants.push(participant);
var streams = participant.getStreams();
for (var key in streams) {
roomEvent.streams.push(streams[key]);
if (subscribeToStreams) {
streams[key].subscribe();
}
}
}
ee.emitEvent('room-connected', [roomEvent]);
}
});
}
this.subscribe = function (stream) {
stream.subscribe();
}
this.onParticipantPublished = function (options) {
var participant = new Participant(kurento, false, that, options);
var pid = participant.getID();
if (!(pid in participants)) {
console.info("Publisher not found in participants list by its id", pid);
} else {
console.log("Publisher found in participants list by its id", pid);
}
//replacing old participant (this one has streams)
participants[pid] = participant;
ee.emitEvent('participant-published', [{
participant: participant
}]);
var streams = participant.getStreams();
for (var key in streams) {
var stream = streams[key];
if (subscribeToStreams) {
stream.subscribe();
ee.emitEvent('stream-added', [{
stream: stream
}]);
}
}
}
this.onParticipantJoined = function (msg) {
var participant = new Participant(kurento, false, that, msg);
var pid = participant.getID();
if (!(pid in participants)) {
console.log("New participant to participants list with id", pid);
participants[pid] = participant;
} else {
//use existing so that we don't lose streams info
console.info("Participant already exists in participants list with " +
"the same id, old:", participants[pid], ", joined now:", participant);
participant = participants[pid];
}
ee.emitEvent('participant-joined', [{
participant: participant
}]);
}
this.onParticipantLeft = function (msg) {
var participant = participants[msg.name];
if (participant !== undefined) {
delete participants[msg.name];
ee.emitEvent('participant-left', [{
participant: participant
}]);
var streams = participant.getStreams();
for (var key in streams) {
ee.emitEvent('stream-removed', [{
stream: streams[key]
}]);
}
participant.dispose();
} else {
console.warn("Participant " + msg.name
+ " unknown. Participants: "
+ JSON.stringify(participants));
}
};
this.onNewMessage = function (msg) {
console.log("New message: " + JSON.stringify(msg));
var room = msg.room;
var user = msg.user;
var message = msg.message;
if (user !== undefined) {
ee.emitEvent('newMessage', [{
room: room,
user: user,
message: message
}]);
} else {
console.error("User undefined in new message:", msg);
}
}
this.recvIceCandidate = function (msg) {
var candidate = {
candidate: msg.candidate,
sdpMid: msg.sdpMid,
sdpMLineIndex: msg.sdpMLineIndex
}
var participant = participants[msg.endpointName];
if (!participant) {
console.error("Participant not found for endpoint " +
msg.endpointName + ". Ice candidate will be ignored.",
candidate);
return false;
}
var streams = participant.getStreams();
for (var key in streams) {
var stream = streams[key];
stream.getWebRtcPeer().addIceCandidate(candidate, function (error) {
if (error) {
console.error("Error adding candidate for " + key
+ " stream of endpoint " + msg.endpointName
+ ": " + error);
return;
}
});
}
}
this.onRoomClosed = function (msg) {
console.log("Room closed: " + JSON.stringify(msg));
var room = msg.room;
if (room !== undefined) {
ee.emitEvent('room-closed', [{
room: room
}]);
} else {
console.error("Room undefined in on room closed", msg);
}
}
this.onMediaError = function(params) {
console.error("Media error: " + JSON.stringify(params));
var error = params.error;
if (error) {
ee.emitEvent('error-media', [{
error: error
}]);
} else {
console.error("Received undefined media error. Params:", params);
}
}
this.leave = function (forced) {
forced = !!forced;
console.log("Leaving room (forced=" + forced + ")");
if (connected && !forced) {
kurento.sendRequest('leaveRoom', function (error, response) {
if (error) {
console.error(error);
} else {
connected = false;
}
});
}
for (var key in participants) {
participants[key].dispose();
}
}
this.disconnect = function (stream) {
var participant = stream.getParticipant();
if (!participant) {
console.error("Stream to disconnect has no participant", stream);
return false;
}
delete participants[participant.getID()];
participant.dispose();
if (participant === localParticipant) {
console.log("Unpublishing my media (I'm " + participant.getID() + ")");
delete localParticipant;
kurento.sendRequest('unpublishVideo', function (error, response) {
if (error) {
console.error(error);
} else {
console.info("Media unpublished correctly");
}
});
} else {
console.log("Unsubscribing from " + stream.getGlobalID());
kurento.sendRequest('unsubscribeFromVideo', {
sender: stream.getGlobalID()
},
function (error, response) {
if (error) {
console.error(error);
} else {
console.info("Unsubscribed correctly from " + stream.getGlobalID());
}
});
}
}
this.getStreams = function () {
return streams;
}
localParticipant = new Participant(kurento, true, that, {id: options.user});
participants[options.user] = localParticipant;
}
// Participant --------------------------------
function Participant(kurento, local, room, options) {
var that = this;
var id = options.id;
var streams = {};
var streamsOpts = [];
if (options.streams) {
for (var i = 0; i < options.streams.length; i++) {
var streamOpts = {
id: options.streams[i].id,
participant: that,
recvVideo: (options.streams[i].recvVideo == undefined ? true : options.streams[i].recvVideo),
recvAudio: (options.streams[i].recvAudio == undefined ? true : options.streams[i].recvAudio)
}
var stream = new Stream(kurento, false, room, streamOpts);
addStream(stream);
streamsOpts.push(streamOpts);
}
}
console.log("New " + (local ? "local " : "remote ") + "participant " + id
+ ", streams opts: ", streamsOpts);
that.setId = function (newId) {
id = newId;
}
function addStream(stream) {
streams[stream.getID()] = stream;
room.getStreams()[stream.getID()] = stream;
}
that.addStream = addStream;
that.getStreams = function () {
return streams;
}
that.dispose = function () {
for (var key in streams) {
streams[key].dispose();
}
}
that.getID = function () {
return id;
}
this.sendIceCandidate = function (candidate) {
console.debug((local ? "Local" : "Remote"), "candidate for",
that.getID(), JSON.stringify(candidate));
kurento.sendRequest("onIceCandidate", {
endpointName: that.getID(),
candidate: candidate.candidate,
sdpMid: candidate.sdpMid,
sdpMLineIndex: candidate.sdpMLineIndex
}, function (error, response) {
if (error) {
console.error("Error sending ICE candidate: "
+ JSON.stringify(error));
}
});
}
}
// Stream --------------------------------
/*
* options: name: XXX data: true (Maybe this is based on webrtc) audio: true,
* video: true, url: "file:///..." > Player screen: true > Desktop (implicit
* video:true, audio:false) audio: true, video: true > Webcam
*
* stream.hasAudio(); stream.hasVideo(); stream.hasData();
*/
function Stream(kurento, local, room, options) {
var that = this;
that.room = room;
var ee = new EventEmitter();
var sdpOffer;
var wrStream;
var wp;
var id;
if (options.id) {
id = options.id;
} else {
id = "webcam";
}
var video;
var videoElements = [];
var elements = [];
var participant = options.participant;
var recvVideo = options.recvVideo;
this.getRecvVideo = function () {
return recvVideo;
}
var recvAudio = options.recvAudio;
this.getRecvAudio = function () {
return recvAudio;
}
var showMyRemote = false;
this.subscribeToMyRemote = function () {
showMyRemote = true;
}
this.displayMyRemote = function () {
return showMyRemote;
}
var localMirrored = false;
this.mirrorLocalStream = function (wr) {
showMyRemote = true;
localMirrored = true;
if (wr)
wrStream = wr;
}
this.isLocalMirrored = function () {
return localMirrored;
}
this.getWrStream = function () {
return wrStream;
}
this.getWebRtcPeer = function () {
return wp;
}
this.addEventListener = function (eventName, listener) {
ee.addListener(eventName, listener);
}
function showSpinner(spinnerParentId) {
var progress = document.createElement('div');
progress.id = 'progress-' + that.getGlobalID();
progress.style.background = "center transparent url('img/spinner.gif') no-repeat";
document.getElementById(spinnerParentId).appendChild(progress);
}
function hideSpinner(spinnerId) {
spinnerId = (typeof spinnerId === 'undefined') ? that.getGlobalID() : spinnerId;
$(jq('progress-' + spinnerId)).hide();
}
this.playOnlyVideo = function (parentElement, thumbnailId) {
video = document.createElement('video');
video.id = 'native-video-' + that.getGlobalID();
video.autoplay = true;
video.controls = false;
if (wrStream) {
video.src = URL.createObjectURL(wrStream);
$(jq(thumbnailId)).show();
hideSpinner();
} else
console.log("No wrStream yet for", that.getGlobalID());
videoElements.push({
thumb: thumbnailId,
video: video
});
if (local) {
video.muted = true;
}
if (typeof parentElement === "string") {
document.getElementById(parentElement).appendChild(video);
} else {
parentElement.appendChild(video);
}
}
this.playThumbnail = function (thumbnailId) {
var container = document.createElement('div');
container.className = "participant";
container.id = that.getGlobalID();
document.getElementById(thumbnailId).appendChild(container);
elements.push(container);
var name = document.createElement('div');
container.appendChild(name);
name.appendChild(document.createTextNode(that.getGlobalID()));
name.id = "name-" + that.getGlobalID();
name.className = "name";
showSpinner(thumbnailId);
that.playOnlyVideo(container, thumbnailId);
}
this.getID = function () {
return id;
}
this.getParticipant = function() {
return participant;
}
this.getGlobalID = function () {
if (participant) {
return participant.getID() + "_" + id;
} else {
return id + "_webcam";
}
}
this.init = function () {
participant.addStream(that);
var constraints = {
audio: true,
video: {
mandatory: {
maxWidth: 640
},
optional: [
{maxFrameRate: 15},
{minFrameRate: 15}
]
}
};
getUserMedia(constraints, function (userStream) {
wrStream = userStream;
ee.emitEvent('access-accepted', null);
}, function (error) {
console.error("Access denied", error);
ee.emitEvent('access-denied', null);
});
}
this.publishVideoCallback = function (error, sdpOfferParam, wp) {
if (error) {
return console.error("(publish) SDP offer error: "
+ JSON.stringify(error));
}
console.log("Sending SDP offer to publish as "
+ that.getGlobalID(), sdpOfferParam);
kurento.sendRequest("publishVideo", {
sdpOffer: sdpOfferParam,
doLoopback: that.displayMyRemote() || false
}, function (error, response) {
if (error) {
console.error("Error on publishVideo: " + JSON.stringify(error));
} else {
that.room.emitEvent('stream-published', [{
stream: that
}])
that.processSdpAnswer(response.sdpAnswer);
}
});
}
this.startVideoCallback = function (error, sdpOfferParam, wp) {
if (error) {
return console.error("(subscribe) SDP offer error: "
+ JSON.stringify(error));
}
console.log("Sending SDP offer to subscribe to "
+ that.getGlobalID(), sdpOfferParam);
kurento.sendRequest("receiveVideoFrom", {
sender: that.getGlobalID(),
sdpOffer: sdpOfferParam
}, function (error, response) {
if (error) {
console.error("Error on recvVideoFrom: " + JSON.stringify(error));
} else {
that.processSdpAnswer(response.sdpAnswer);
}
});
}
function initWebRtcPeer(sdpOfferCallback) {
if (local) {
var options = {
videoStream: wrStream,
onicecandidate: participant.sendIceCandidate.bind(participant)
}
if (that.displayMyRemote()) {
wp = new kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options, function (error) {
if(error) {
return console.error(error);
}
this.generateOffer(sdpOfferCallback.bind(that));
});
} else {
wp = new kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, function (error) {
if(error) {
return console.error(error);
}
this.generateOffer(sdpOfferCallback.bind(that));
});
}
} else {
var offerConstraints = {
mandatory : {
OfferToReceiveVideo: recvVideo,
OfferToReceiveAudio: recvAudio
}
};
console.log("Constraints of generate SDP offer (subscribing)",
offerConstraints);
var options = {
onicecandidate: participant.sendIceCandidate.bind(participant),
connectionConstraints: offerConstraints
}
wp = new kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(options, function (error) {
if(error) {
return console.error(error);
}
this.generateOffer(sdpOfferCallback.bind(that));
});
}
console.log("Waiting for SDP offer to be generated ("
+ (local ? "local" : "remote") + " peer: " + that.getGlobalID() + ")");
}
this.publish = function () {
// FIXME: Throw error when stream is not local
initWebRtcPeer(that.publishVideoCallback);
// FIXME: Now we have coupled connecting to a room and adding a
// stream to this room. But in the new API, there are two steps.
// This is the second step. For now, it do nothing.
}
this.subscribe = function () {
// FIXME: In the current implementation all participants are subscribed
// automatically to all other participants. We use this method only to
// negotiate SDP
initWebRtcPeer(that.startVideoCallback);
}
this.processSdpAnswer = function (sdpAnswer) {
var answer = new RTCSessionDescription({
type: 'answer',
sdp: sdpAnswer,
});
console.log(that.getGlobalID() + ": set peer connection with recvd SDP answer",
sdpAnswer);
var pc = wp.peerConnection;
pc.setRemoteDescription(answer, function () {
// Avoids to subscribe to your own stream remotely
// except when showMyRemote is true
if (!local || that.displayMyRemote()) {
wrStream = pc.getRemoteStreams()[0];
console.log("Peer remote stream", wrStream);
for (i = 0; i < videoElements.length; i++) {
var thumbnailId = videoElements[i].thumb;
var video = videoElements[i].video;
video.src = URL.createObjectURL(wrStream);
video.onplay = function() {
//is ('native-video-' + that.getGlobalID())
var elementId = this.id;
var videoId = elementId.split("-");
$(jq(thumbnailId)).show();
hideSpinner(videoId[2]);
};
}
that.room.emitEvent('stream-subscribed', [{
stream: that
}]);
}
}, function (error) {
console.error(that.getGlobalID() + ": Error setting SDP to the peer connection: "
+ JSON.stringify(error));
});
}
this.unpublish = function () {
if (wp) {
wp.dispose();
} else {
if (wrStream) {
wrStream.getAudioTracks().forEach(function (track) {
track.stop && track.stop()
})
wrStream.getVideoTracks().forEach(function (track) {
track.stop && track.stop()
})
}
}
console.log(that.getGlobalID() + ": Stream '" + id + "' unpublished");
}
this.dispose = function () {
function disposeElement(element) {
if (element && element.parentNode) {
element.parentNode.removeChild(element);
}
}
for (i = 0; i < elements.length; i++) {
disposeElement(elements[i]);
}
for (i = 0; i < videoElements.length; i++) {
disposeElement(videoElements[i].video);
}
if (wp) {
wp.dispose();
} else {
if (wrStream) {
wrStream.getAudioTracks().forEach(function (track) {
track.stop && track.stop()
})
wrStream.getVideoTracks().forEach(function (track) {
track.stop && track.stop()
})
}
}
console.log(that.getGlobalID() + ": Stream '" + id + "' disposed");
}
}
// KurentoRoom --------------------------------
function KurentoRoom(wsUri, callback) {
if (!(this instanceof KurentoRoom))
return new KurentoRoom(wsUri, callback);
var that = this;
var userName;
var ws = new WebSocket(wsUri);
ws.onopen = function () {
callback(null, that);
}
ws.onerror = function (evt) {
callback(evt.data);
}
ws.onclose = function () {
console.log("Connection Closed");
}
var options = {
request_timeout: 50000
};
var rpc = new RpcBuilder(RpcBuilder.packers.JsonRPC, options, ws, function (
request) {
console.info('Received request: ' + JSON.stringify(request));
switch (request.method) {
case 'participantJoined':
onParticipantJoined(request.params);
break;
case 'participantPublished':
onParticipantPublished(request.params);
break;
case 'participantUnpublished':
//TODO use a different method, don't delete
// the participant for future reconnection?
onParticipantLeft(request.params);
break;
case 'participantLeft':
onParticipantLeft(request.params);
break;
case 'sendMessage': //CHAT
onNewMessage(request.params);
break;
case 'iceCandidate':
iceCandidateEvent(request.params);
break;
case 'roomClosed':
onRoomClosed(request.params);
break;
case 'mediaError':
onMediaError(request.params);
break;
default:
console.error('Unrecognized request: ' + JSON.stringify(request));
}
;
});
function onParticipantJoined(msg) {
if (room !== undefined) {
room.onParticipantJoined(msg);
}
}
function onParticipantPublished(msg) {
if (room !== undefined) {
room.onParticipantPublished(msg);
}
}
function onParticipantLeft(msg) {
if (room !== undefined) {
room.onParticipantLeft(msg);
}
}
function onNewMessage(msg) {
if (room !== undefined) {
room.onNewMessage(msg);
}
}
function iceCandidateEvent(msg) {
if (room !== undefined) {
room.recvIceCandidate(msg);
}
}
function onRoomClosed(msg) {
if (room !== undefined) {
room.onRoomClosed(msg);
}
}
function onMediaError(params) {
if (room !== undefined) {
room.onMediaError(params);
}
}
var rpcParams;
this.setRpcParams = function (params) {
rpcParams = params;
}
this.sendRequest = function (method, params, callback) {
if (rpcParams && rpcParams !== "null" && rpcParams !== "undefined") {
for(var index in rpcParams) {
if (rpcParams.hasOwnProperty(index)) {
params[index] = rpcParams[index];
}
}
}
rpc.encode(method, params, callback);
console.log('Sent request: { method:"' + method + '", params: '
+ JSON.stringify(params) + ' }');
};
this.close = function (forced) {
if (room !== undefined) {
room.leave(forced);
}
ws.close();
};
this.disconnectParticipant = function(stream) {
if (room !== undefined) {
room.disconnect(stream);
}
}
this.Stream = function (room, options) {
options.participant = room.getLocalParticipant();
return new Stream(that, true, room, options);
};
this.Room = function (options) {
// FIXME Support more than one room
room = new Room(that, options);
// FIXME Include name in stream, not in room
userName = options.userName;
return room;
};
//CHAT
this.sendMessage = function (room, user, message) {
this.sendRequest('sendMessage', {message: message, userMessage: user, roomMessage: room}, function (error, response) {
if (error) {
console.error(error);
} else {
connected = false;
}
});
};
this.sendCustomRequest = function (params, callback) {
this.sendRequest('customRequest', params, callback);
};
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy