我正在使用kurento-client进行一对一的视频通话。我的应用程序的用例是,将有两个 用户。任何用户都可以是主叫方和被叫方。首先创建房间的是呼叫者,另一个加入的是 在那个房间是被呼叫者。呼叫者加入会议室时,他/她应该看到他/她的网络摄像头以及被呼叫者 加入该房间,则应显示两个用户的网络摄像头。一个用户最多只能有2位用户 房间。
为此,我尝试通过自己创建房间来执行以下操作,但是我无法显示 远程对等体。
这是我的工作方式
class Studio extends Component {
constructor(props) {
super(props);
this.state = {
startCall: false,
room: ''
};
this.localStreamRef = React.createRef();
this.remoteStreamRef = React.createRef();
this.handleCallStart = this.handleCallStart.bind(this);
this.socket = null;
this.webRTC = null;
this.loginName=null;
}
componentDidMount() {
this.socket = new socketIOClient("https://localhost:8443/sockets");
this.socket.on("ROOM_CREATED", room => this.grabWebcam(room))
this.socket.on("ROOM_JOINED", async room => {
await this.grabWebcam(room, true);
await this.handleCreateOffer(room, true, true);
})
this.socket.on("OFFER_RESPONSE", async response => await this.offerResponse(response));
this.socket.on("ICE_CANDIDATE", async candidate => {
console.log('candidate in listener', candidate);
await this.webRTC.addIceCandidate(candidate)
}
);
this.socket.on("ERROR", error => this.onError(error));
}
componentWillUnmount() {
this.socket = null;
}
handleCallStart() {
const { room } = this.state;
console.log('room', room)
this.socket.emit("room:join", { interviewId: room, userEmail: 'abc' });
}
grabWebcam = async (room, joined = false) => {
if(joined){
this.loginName = "interviewee";
}else {
this.loginName = "interviewer"
}
const options = {
localVideo: this.localStreamRef.current,
remoteVideo: this.remoteStreamRef.current,
onicecandidate: this.onIceCandidate,
mediaConstraints: CONSTRAINTS
};
this.webRTC = kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options, err => {
if (err) return this.onError(err);
// this will be needed once another user joins the created room
}
);
}
handleCreateOffer = async (room, initial = false, joined = false) => {
this.webRTC.generateOffer((error, offerSdp) => {
if (error) {
console.error(error);
}
const offer = {
role: joined ? 'interviewee' : 'interviwer',
room,
sdpOffer : offerSdp
}
this.onOffer(offer);
});
}
// sending offer to other peer using SDP which is a signaling mechanism
onOffer = (offer) => {
console.info(
"STEP 2 -> Invoking SDP offer callback function " + window.location.host
);
// We've made this function up sendOfferToRemotePeer(sdpOffer,
this.socket.emit("OFFER_CREATE", JSON.stringify(offer));
};
offerResponse = async answer => {
await this.webRTC.processAnswer(answer.sdpAnswer);
};
onIceCandidate = candidate => {
console.log("Local candidate" + JSON.stringify(candidate));
};
onError = error => console.error(error);
handleRoomChange = event => {
this.setState({ room: event.target.value })
}
render() {
return (
<div className="App">
<input type="text" name="room" onChange={this.handleRoomChange} value={this.state.room} />
<button type="button" onClick={this.handleCallStart}>
{!this.state.startCall && "Start"} Call
</button>
<h3>Local stream</h3>
<video
id="videoInput"
ref={this.localStreamRef}
autoPlay
/>
<h3>Remote stream</h3>
<video
id="videoOutput"
ref={this.remoteStreamRef}
autoPlay
/>
</div>
);
}
}
export default Studio;
server.js
const sessionHandler = session({
secret : 'none',
rolling : true,
resave : true,
saveUninitialized : true
});
app.use(sessionHandler);
/*
* Definition of global variables.
*/
const users = {};
const sessions = {};
const candidatesQueue = {};
let kurentoClient = null;
/*
* Server startup
*/
const asUrl = url.parse(argv.as_uri);
const port = asUrl.port;
const server = https.createServer(options, app).listen(port, function() {
console.log('Open ' + url.format(asUrl) + ' with a WebRTC capable browser');
});
const io = require("socket.io")(server);
io.of('/sockets').on('connection', socket => {
const sessionId = socket.id;
socket.on("room:join", ({ interviewId: room, userEmail }) => {
console.log('room join socket listened')
const clientsInRoom = io.of('sockets').adapter.rooms[room];
const numberOfClientsInRoom = clientsInRoom
? Object.keys(clientsInRoom.sockets).length
: 0;
console.log(`room: ${room}, number: ${numberOfClientsInRoom}, user: ${userEmail}`);
if (!users[userEmail]) {
users[userEmail] = {
email: userEmail,
interviewId: room,
socketId: sessionId
};
}
if (numberOfClientsInRoom === 0) {
// enters the room and waits for other user
console.log(`joining ${room}, waiting for others to join`);
socket.join(room);
socket.room = room;
socket.emit('ROOM_CREATED', room, socket.id);
} else if (numberOfClientsInRoom === 1) {
// user arrived, now start sharing
console.log(`joining ${room}, establishing connection...`);
socket.join(room);
socket.room = room;
socket.emit('ROOM_JOINED', room, socket.id);
} else {
console.log(`cant join room::room ${socket.room}`);
// max two clients
socket.emit('ROOM_FULL', room);
}
});
// OFFER_CREATE
socket.on('OFFER_CREATE', offer => {
const { room } = offer;
console.log(`creating offer to ${room}`);
start(sessionId, socket, offer, function(error, sdpAnswer) {
if (error) {
return socket.emit('ERROR', error);
}
const answer = {
room: room || socket.room,
sdpAnswer
}
socket.broadcast.to(room).emit('OFFER_RESPONSE', answer);
})
})
// leaving session
socket.on('STOP', () => stop(sessionId))
//
socket.on('CANDIDATE_NEW', response => onIceCandidate(socket.id, response.candidate))
})
// Definition of functions
// Recover kurentoClient for the first time.
function getKurentoClient(callback) {
if (kurentoClient !== null) {
return callback(null, kurentoClient);
}
kurento(argv.ws_uri, function(error, _kurentoClient) {
if (error) {
console.log("Could not find media server at address " + argv.ws_uri);
return callback("Could not find media server at address" + argv.ws_uri
+ ". Exiting with error " + error);
}
kurentoClient = _kurentoClient;
callback(null, kurentoClient);
});
}
function start(socketId, socket, offer, callback) {
console.log('start function', offer, callback);
if (!socketId) {
return callback('Cannot use undefined socketId');
}
getKurentoClient(function(error, kurentoClient) {
if (error) {
return callback(error);
}
kurentoClient.create('MediaPipeline', function(error, pipeline) {
console.log('pipeline', pipeline);
if (error) {
return callback(error);
}
createMediaElements(pipeline, socket, function(error, webRtcEndpoint) {
if (error) {
pipeline.release();
return callback(error);
}
if (candidatesQueue[socketId]) {
while(candidatesQueue[socketId].length) {
const candidate = candidatesQueue[socketId].shift();
webRtcEndpoint.addIceCandidate(candidate);
}
}
connectMediaElements(webRtcEndpoint, function(error) {
if (error) {
pipeline.release();
return callback(error);
}
webRtcEndpoint.on('OnIceCandidate', function(event) {
var candidate = kurento.getComplexType('IceCandidate')(event.candidate);
// console.log('candidate', candidate);
socket.emit('ICE_CANDIDATE', candidate);
});
webRtcEndpoint.processOffer(offer, function(error, sdpAnswer) {
if (error) {
pipeline.release();
return callback(error);
}
sessions[socketId] = {
'pipeline' : pipeline,
'webRtcEndpoint' : webRtcEndpoint
}
console.log('sessions', sessions);
return callback(null, sdpAnswer);
});
webRtcEndpoint.gatherCandidates(function(error) {
if (error) {
return callback(error);
}
});
});
});
});
});
}
function createMediaElements(pipeline, socket, callback) {
pipeline.create('WebRtcEndpoint', function(error, webRtcEndpoint) {
if (error) {
return callback(error);
}
return callback(null, webRtcEndpoint);
});
}
function connectMediaElements(webRtcEndpoint, callback) {
webRtcEndpoint.connect(webRtcEndpoint, function(error) {
if (error) {
return callback(error);
}
return callback(null);
});
}
function stop(sessionId) {
if (sessions[sessionId]) {
var pipeline = sessions[sessionId].pipeline;
console.info('Releasing pipeline');
pipeline.release();
delete sessions[sessionId];
delete candidatesQueue[sessionId];
}
}
function onIceCandidate(sessionId, _candidate) {
var candidate = kurento.getComplexType('IceCandidate')(_candidate);
if (sessions[sessionId]) {
console.info('Sending candidate');
const webRtcEndpoint = sessions[sessionId].webRtcEndpoint;
webRtcEndpoint.addIceCandidate(candidate);
}
else {
console.info('Queueing candidate');
if (!candidatesQueue[sessionId]) {
candidatesQueue[sessionId] = [];
}
candidatesQueue[sessionId].push(candidate);
}
}