Composite is a vide and audio mixer.
You add ports to composite, and each input signal will be added to the output video, creating a GRID.
Viewers get only the composed output: example
And I add "co hosts" as needed with this code using node.js:
function addCompanion(sessionId, ws, sdpOffer, clean_title, callback){
clearCandidatesQueue(sessionId);
if(!Theaters[clean_title].coHosts.hasOwnProperty(sessionId)){
console.log("companion not registered");
return callback(Theaters[clean_title].coHost);
}
Theaters[clean_title].pipeline.create('WebRtcEndpoint', function(error, _webRtcEndpoint) {
if (error) {
return callback(error);
}
coHost=Theaters[clean_title].coHosts[sessionId];
coHost.webRtcEndpoint = _webRtcEndpoint;
if(!_webRtcEndpoint){
sendError(ws, "No WebRTC for coHost");
return callback("No webrtc for coHost");
}
console.log("companionResponse webRtcEndpoint created...");
if (candidatesQueue[sessionId]) {
while(candidatesQueue[sessionId].length) {
var candidate = candidatesQueue[sessionId].shift();
coHost.webRtcEndpoint.addIceCandidate(candidate);
}
}
console.log("companionResponse iceCadantes offered");
coHost.webRtcEndpoint.on('OnIceCandidate', function(event) {
var candidate = kurento.getComplexType('IceCandidate')(event.candidate);
ws.send(JSON.stringify({
id : 'iceCandidate',
candidate : candidate
}));
});
coHost.webRtcEndpoint.processOffer(sdpOffer, function(error, sdpAnswer) {
if (error) {
stop(sessionId, clean_title);
return callback(error);
}
Promise.all([
Theaters[clean_title].composite.createHubPort(function(error, _hubport){
coHost.hubPort=_hubport;
})
]).then(function(){
coHost.webRtcEndpoint.connect(coHost.hubPort); //steram to composite
coHost.hubPort.connect(coHost.webRtcEndpoint); //steram from composite
coHost.ws=ws;
Theaters[clean_title].masterOfCeremony.ws.send(JSON.stringify({ id: "onLine", camera: coHost.camera, sessionId: sessionId }));
}).catch(function(error){
console.log("Error adding cmpanion", error);
});
console.log("coHost added done...");
callback(null, sdpAnswer);
});
coHost.webRtcEndpoint.gatherCandidates(function(error) {
if (error) {
stop(sessionId, clean_title);
return callback(error);
}
});
});
}
And viewers are added like this:
function addViewer(sessionId, ws, sdpOffer, clean_title, callback){
clearCandidatesQueue(sessionId);
if(Theaters[clean_title].atendees.hasOwnProperty(sessionId)){
console.log("Antendee duplicated");
return callback(Theaters[clean_title].atendees[sessionId]);
}
atendee = {
id: sessionId,
ws: ws,
hubPort: false,
webRtcEndpoint: false,
}
///start Vuewer
Theaters[clean_title].pipeline.create('WebRtcEndpoint', function(error, _webRtcEndpoint) {
if (error) {
return callback(error);
}
atendee.webRtcEndpoint = _webRtcEndpoint;
console.log("Atendee webRtcEndpoint created...");
if (candidatesQueue[sessionId]) {
while(candidatesQueue[sessionId].length) {
var candidate = candidatesQueue[sessionId].shift();
atendee.webRtcEndpoint.addIceCandidate(candidate);
}
}
atendee.webRtcEndpoint.on('OnIceCandidate', function(event) {
var candidate = kurento.getComplexType('IceCandidate')(event.candidate);
ws.send(JSON.stringify({
id : 'iceCandidate',
candidate : candidate
}));
});
atendee.webRtcEndpoint.processOffer(sdpOffer, function(error, sdpAnswer) {
if (error) {
stop(sessionId, clean_title);
return callback(error);
}
Theaters[clean_title].atendees[sessionId]=atendee;
if(Theaters[clean_title].master==1){
Theaters[clean_title].masterOfCeremony.webRtcEndpoint.connect(atendee.webRtcEndpoint);
atendee.hubPort=false;
}
if(Theaters[clean_title].master==2){
Theaters[clean_title].composite.createHubPort(function(error, hubPort){
atendee.hubPort=hubPort;
atendee.hubPort.connect(atendee.webRtcEndpoint);
});
}
console.log("Atendee["+sessionId+"] conected to "+clean_title+"to master "+Theaters[clean_title].master);
Theaters[clean_title].atendees[sessionId]=atendee;
atendee.webRtcEndpoint.gatherCandidates(function(error) {
if (error) {
stop(sessionId, clean_title);
return callback(error);
}
});
callback(null, sdpAnswer);
});
});
//eof viewer
}
At front end cohost/companion use send and receive webrtcpeer and for viewers receive only...
Main diffrence, Image providers (co host) connect from and to the composite hub.
coHost.webRtcEndpoint.connect(coHost.hubPort); //steram to composite
coHost.hubPort.connect(coHost.webRtcEndpoint); //steram from composite
mean while, viewers only connects from composite
atendee.hubPort.connect(atendee.webRtcEndpoint);
Hubport in both cases is created from composite, and obviously, everything is created from the same pipeline...