mirror of https://github.com/OpenVidu/openvidu.git
openvidu-server: reconnection to KMS
parent
93b2aebcc7
commit
5e23d1fd16
|
@ -67,10 +67,10 @@ import io.openvidu.server.recording.service.RecordingManager;
|
||||||
* - resolution string
|
* - resolution string
|
||||||
* - recordingLayout: string
|
* - recordingLayout: string
|
||||||
* - size: number
|
* - size: number
|
||||||
* - webrtcConnectionDestroyed.reason: "unsubscribe", "unpublish", "disconnect", "networkDisconnect", "openviduServerStopped"
|
* - webrtcConnectionDestroyed.reason: "unsubscribe", "unpublish", "disconnect", "networkDisconnect", "mediaServerDisconnect", "openviduServerStopped"
|
||||||
* - participantLeft.reason: "unsubscribe", "unpublish", "disconnect", "networkDisconnect", "openviduServerStopped"
|
* - participantLeft.reason: "unsubscribe", "unpublish", "disconnect", "networkDisconnect", "openviduServerStopped"
|
||||||
* - sessionDestroyed.reason: "lastParticipantLeft", "openviduServerStopped"
|
* - sessionDestroyed.reason: "lastParticipantLeft", "openviduServerStopped"
|
||||||
* - recordingStopped.reason: "recordingStoppedByServer", "lastParticipantLeft", "sessionClosedByServer", "automaticStop", "openviduServerStopped"
|
* - recordingStopped.reason: "recordingStoppedByServer", "lastParticipantLeft", "sessionClosedByServer", "automaticStop", "mediaServerDisconnect", "openviduServerStopped"
|
||||||
*
|
*
|
||||||
* [OPTIONAL_PROPERTIES]:
|
* [OPTIONAL_PROPERTIES]:
|
||||||
* - receivingFrom: only if connection = "INBOUND"
|
* - receivingFrom: only if connection = "INBOUND"
|
||||||
|
@ -105,8 +105,10 @@ public class CallDetailRecord {
|
||||||
|
|
||||||
public void recordSessionDestroyed(String sessionId, String reason) {
|
public void recordSessionDestroyed(String sessionId, String reason) {
|
||||||
CDREvent e = this.sessions.remove(sessionId);
|
CDREvent e = this.sessions.remove(sessionId);
|
||||||
|
if (e != null) {
|
||||||
this.log(new CDREventSession(e, RecordingManager.finalReason(reason)));
|
this.log(new CDREventSession(e, RecordingManager.finalReason(reason)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void recordParticipantJoined(Participant participant, String sessionId) {
|
public void recordParticipantJoined(Participant participant, String sessionId) {
|
||||||
CDREventParticipant e = new CDREventParticipant(sessionId, participant);
|
CDREventParticipant e = new CDREventParticipant(sessionId, participant);
|
||||||
|
|
|
@ -414,6 +414,7 @@ public abstract class SessionManager {
|
||||||
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, "Session '" + sessionId + "' not found");
|
throw new OpenViduException(Code.ROOM_NOT_FOUND_ERROR_CODE, "Session '" + sessionId + "' not found");
|
||||||
}
|
}
|
||||||
if (session.isClosed()) {
|
if (session.isClosed()) {
|
||||||
|
this.closeSessionAndEmptyCollections(session, reason);
|
||||||
throw new OpenViduException(Code.ROOM_CLOSED_ERROR_CODE, "Session '" + sessionId + "' already closed");
|
throw new OpenViduException(Code.ROOM_CLOSED_ERROR_CODE, "Session '" + sessionId + "' already closed");
|
||||||
}
|
}
|
||||||
Set<Participant> participants = getParticipants(sessionId);
|
Set<Participant> participants = getParticipants(sessionId);
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
package io.openvidu.server.kurento.core;
|
package io.openvidu.server.kurento.core;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
@ -29,7 +27,6 @@ import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.kurento.client.Continuation;
|
import org.kurento.client.Continuation;
|
||||||
import org.kurento.client.ErrorEvent;
|
import org.kurento.client.ErrorEvent;
|
||||||
import org.kurento.client.Filter;
|
import org.kurento.client.Filter;
|
||||||
import org.kurento.client.GenericMediaElement;
|
|
||||||
import org.kurento.client.IceCandidate;
|
import org.kurento.client.IceCandidate;
|
||||||
import org.kurento.client.IceComponentState;
|
import org.kurento.client.IceComponentState;
|
||||||
import org.kurento.client.MediaElement;
|
import org.kurento.client.MediaElement;
|
||||||
|
@ -46,12 +43,12 @@ import com.google.gson.JsonObject;
|
||||||
import io.openvidu.client.OpenViduException;
|
import io.openvidu.client.OpenViduException;
|
||||||
import io.openvidu.client.OpenViduException.Code;
|
import io.openvidu.client.OpenViduException.Code;
|
||||||
import io.openvidu.client.internal.ProtocolElements;
|
import io.openvidu.client.internal.ProtocolElements;
|
||||||
|
import io.openvidu.java.client.OpenViduRole;
|
||||||
import io.openvidu.server.cdr.CallDetailRecord;
|
import io.openvidu.server.cdr.CallDetailRecord;
|
||||||
import io.openvidu.server.config.InfoHandler;
|
import io.openvidu.server.config.InfoHandler;
|
||||||
import io.openvidu.server.config.OpenviduConfig;
|
import io.openvidu.server.config.OpenviduConfig;
|
||||||
import io.openvidu.server.core.MediaOptions;
|
import io.openvidu.server.core.MediaOptions;
|
||||||
import io.openvidu.server.core.Participant;
|
import io.openvidu.server.core.Participant;
|
||||||
import io.openvidu.server.kurento.TrackType;
|
|
||||||
import io.openvidu.server.kurento.endpoint.KmsEvent;
|
import io.openvidu.server.kurento.endpoint.KmsEvent;
|
||||||
import io.openvidu.server.kurento.endpoint.KmsMediaEvent;
|
import io.openvidu.server.kurento.endpoint.KmsMediaEvent;
|
||||||
import io.openvidu.server.kurento.endpoint.MediaEndpoint;
|
import io.openvidu.server.kurento.endpoint.MediaEndpoint;
|
||||||
|
@ -72,7 +69,6 @@ public class KurentoParticipant extends Participant {
|
||||||
private boolean webParticipant = true;
|
private boolean webParticipant = true;
|
||||||
|
|
||||||
private final KurentoSession session;
|
private final KurentoSession session;
|
||||||
private final MediaPipeline pipeline;
|
|
||||||
|
|
||||||
private PublisherEndpoint publisher;
|
private PublisherEndpoint publisher;
|
||||||
private CountDownLatch endPointLatch = new CountDownLatch(1);
|
private CountDownLatch endPointLatch = new CountDownLatch(1);
|
||||||
|
@ -80,9 +76,8 @@ public class KurentoParticipant extends Participant {
|
||||||
private final ConcurrentMap<String, Filter> filters = new ConcurrentHashMap<>();
|
private final ConcurrentMap<String, Filter> filters = new ConcurrentHashMap<>();
|
||||||
private final ConcurrentMap<String, SubscriberEndpoint> subscribers = new ConcurrentHashMap<String, SubscriberEndpoint>();
|
private final ConcurrentMap<String, SubscriberEndpoint> subscribers = new ConcurrentHashMap<String, SubscriberEndpoint>();
|
||||||
|
|
||||||
public KurentoParticipant(Participant participant, KurentoSession kurentoSession, MediaPipeline pipeline,
|
public KurentoParticipant(Participant participant, KurentoSession kurentoSession, InfoHandler infoHandler,
|
||||||
InfoHandler infoHandler, CallDetailRecord CDR, OpenviduConfig openviduConfig,
|
CallDetailRecord CDR, OpenviduConfig openviduConfig, RecordingManager recordingManager) {
|
||||||
RecordingManager recordingManager) {
|
|
||||||
super(participant.getParticipantPrivateId(), participant.getParticipantPublicId(), participant.getToken(),
|
super(participant.getParticipantPrivateId(), participant.getParticipantPublicId(), participant.getToken(),
|
||||||
participant.getClientMetadata(), participant.getLocation(), participant.getPlatform(),
|
participant.getClientMetadata(), participant.getLocation(), participant.getPlatform(),
|
||||||
participant.getCreatedAt());
|
participant.getCreatedAt());
|
||||||
|
@ -91,9 +86,11 @@ public class KurentoParticipant extends Participant {
|
||||||
this.openviduConfig = openviduConfig;
|
this.openviduConfig = openviduConfig;
|
||||||
this.recordingManager = recordingManager;
|
this.recordingManager = recordingManager;
|
||||||
this.session = kurentoSession;
|
this.session = kurentoSession;
|
||||||
this.pipeline = pipeline;
|
|
||||||
this.publisher = new PublisherEndpoint(webParticipant, this, participant.getParticipantPublicId(), pipeline,
|
if (!OpenViduRole.SUBSCRIBER.equals(participant.getToken().getRole())) {
|
||||||
this.openviduConfig);
|
this.publisher = new PublisherEndpoint(webParticipant, this, participant.getParticipantPublicId(),
|
||||||
|
this.session.getPipeline(), this.openviduConfig);
|
||||||
|
}
|
||||||
|
|
||||||
for (Participant other : session.getParticipants()) {
|
for (Participant other : session.getParticipants()) {
|
||||||
if (!other.getParticipantPublicId().equals(this.getParticipantPublicId())) {
|
if (!other.getParticipantPublicId().equals(this.getParticipantPublicId())) {
|
||||||
|
@ -113,7 +110,7 @@ public class KurentoParticipant extends Participant {
|
||||||
String publisherStreamId = this.getParticipantPublicId() + "_"
|
String publisherStreamId = this.getParticipantPublicId() + "_"
|
||||||
+ (mediaOptions.hasVideo() ? mediaOptions.getTypeOfVideo() : "MICRO") + "_"
|
+ (mediaOptions.hasVideo() ? mediaOptions.getTypeOfVideo() : "MICRO") + "_"
|
||||||
+ RandomStringUtils.random(5, true, false).toUpperCase();
|
+ RandomStringUtils.random(5, true, false).toUpperCase();
|
||||||
this.publisher.getEndpoint().setName(publisherStreamId);
|
this.publisher.setStreamId(publisherStreamId);
|
||||||
addEndpointListeners(this.publisher);
|
addEndpointListeners(this.publisher);
|
||||||
|
|
||||||
// Remove streamId from publisher's map
|
// Remove streamId from publisher's map
|
||||||
|
@ -121,35 +118,10 @@ public class KurentoParticipant extends Participant {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shapePublisherMedia(GenericMediaElement element, MediaType type) {
|
|
||||||
if (type == null) {
|
|
||||||
this.publisher.apply(element);
|
|
||||||
} else {
|
|
||||||
this.publisher.apply(element, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized Filter getFilterElement(String id) {
|
public synchronized Filter getFilterElement(String id) {
|
||||||
return filters.get(id);
|
return filters.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* public synchronized void addFilterElement(String id, Filter filter) {
|
|
||||||
* filters.put(id, filter); shapePublisherMedia(filter, null); }
|
|
||||||
*
|
|
||||||
* public synchronized void disableFilterelement(String filterID, boolean
|
|
||||||
* releaseElement) { Filter filter = getFilterElement(filterID);
|
|
||||||
*
|
|
||||||
* if (filter != null) { try { publisher.revert(filter, releaseElement); } catch
|
|
||||||
* (OpenViduException e) { // Ignore error } } }
|
|
||||||
*
|
|
||||||
* public synchronized void enableFilterelement(String filterID) { Filter filter
|
|
||||||
* = getFilterElement(filterID);
|
|
||||||
*
|
|
||||||
* if (filter != null) { try { publisher.apply(filter); } catch
|
|
||||||
* (OpenViduException e) { // Ignore exception if element is already used } } }
|
|
||||||
*/
|
|
||||||
|
|
||||||
public synchronized void removeFilterElement(String id) {
|
public synchronized void removeFilterElement(String id) {
|
||||||
Filter filter = getFilterElement(id);
|
Filter filter = getFilterElement(id);
|
||||||
filters.remove(id);
|
filters.remove(id);
|
||||||
|
@ -161,7 +133,7 @@ public class KurentoParticipant extends Participant {
|
||||||
public synchronized void releaseAllFilters() {
|
public synchronized void releaseAllFilters() {
|
||||||
// Check this, mutable array?
|
// Check this, mutable array?
|
||||||
filters.forEach((s, filter) -> removeFilterElement(s));
|
filters.forEach((s, filter) -> removeFilterElement(s));
|
||||||
if (this.publisher.getFilter() != null) {
|
if (this.publisher != null && this.publisher.getFilter() != null) {
|
||||||
this.publisher.revert(this.publisher.getFilter());
|
this.publisher.revert(this.publisher.getFilter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,38 +163,6 @@ public class KurentoParticipant extends Participant {
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<SubscriberEndpoint> getAllConnectedSubscribedEndpoints() {
|
|
||||||
Set<SubscriberEndpoint> subscribedToSet = new HashSet<>();
|
|
||||||
for (SubscriberEndpoint se : subscribers.values()) {
|
|
||||||
if (se.isConnectedToPublisher()) {
|
|
||||||
subscribedToSet.add(se);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return subscribedToSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<SubscriberEndpoint> getConnectedSubscribedEndpoints(PublisherEndpoint publisher) {
|
|
||||||
Set<SubscriberEndpoint> subscribedToSet = new HashSet<>();
|
|
||||||
for (SubscriberEndpoint se : subscribers.values()) {
|
|
||||||
if (se.isConnectedToPublisher() && se.getPublisher().equals(publisher)) {
|
|
||||||
subscribedToSet.add(se);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return subscribedToSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String preparePublishConnection() {
|
|
||||||
log.info("PARTICIPANT {}: Request to publish video in room {} by " + "initiating connection from server",
|
|
||||||
this.getParticipantPublicId(), this.session.getSessionId());
|
|
||||||
|
|
||||||
String sdpOffer = this.getPublisher().preparePublishConnection();
|
|
||||||
|
|
||||||
log.trace("PARTICIPANT {}: Publishing SdpOffer is {}", this.getParticipantPublicId(), sdpOffer);
|
|
||||||
log.info("PARTICIPANT {}: Generated Sdp offer for publishing in room {}", this.getParticipantPublicId(),
|
|
||||||
this.session.getSessionId());
|
|
||||||
return sdpOffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String publishToRoom(SdpType sdpType, String sdpString, boolean doLoopback,
|
public String publishToRoom(SdpType sdpType, String sdpString, boolean doLoopback,
|
||||||
MediaElement loopbackAlternativeSrc, MediaType loopbackConnectionType) {
|
MediaElement loopbackAlternativeSrc, MediaType loopbackConnectionType) {
|
||||||
log.info("PARTICIPANT {}: Request to publish video in room {} (sdp type {})", this.getParticipantPublicId(),
|
log.info("PARTICIPANT {}: Request to publish video in room {} (sdp type {})", this.getParticipantPublicId(),
|
||||||
|
@ -252,7 +192,7 @@ public class KurentoParticipant extends Participant {
|
||||||
log.info("PARTICIPANT {}: unpublishing media stream from room {}", this.getParticipantPublicId(),
|
log.info("PARTICIPANT {}: unpublishing media stream from room {}", this.getParticipantPublicId(),
|
||||||
this.session.getSessionId());
|
this.session.getSessionId());
|
||||||
releasePublisherEndpoint(reason);
|
releasePublisherEndpoint(reason);
|
||||||
this.publisher = new PublisherEndpoint(webParticipant, this, this.getParticipantPublicId(), pipeline,
|
this.publisher = new PublisherEndpoint(webParticipant, this, this.getParticipantPublicId(), this.getPipeline(),
|
||||||
this.openviduConfig);
|
this.openviduConfig);
|
||||||
log.info("PARTICIPANT {}: released publisher endpoint and left it initialized (ready for future streaming)",
|
log.info("PARTICIPANT {}: released publisher endpoint and left it initialized (ready for future streaming)",
|
||||||
this.getParticipantPublicId());
|
this.getParticipantPublicId());
|
||||||
|
@ -357,21 +297,13 @@ public class KurentoParticipant extends Participant {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mutePublishedMedia(TrackType trackType) {
|
public void close(String reason, boolean definitelyClosed) {
|
||||||
this.getPublisher().mute(trackType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unmutePublishedMedia(TrackType trackType) {
|
|
||||||
this.getPublisher().unmute(trackType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close(String reason) {
|
|
||||||
log.debug("PARTICIPANT {}: Closing user", this.getParticipantPublicId());
|
log.debug("PARTICIPANT {}: Closing user", this.getParticipantPublicId());
|
||||||
if (isClosed()) {
|
if (isClosed()) {
|
||||||
log.warn("PARTICIPANT {}: Already closed", this.getParticipantPublicId());
|
log.warn("PARTICIPANT {}: Already closed", this.getParticipantPublicId());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.closed = true;
|
this.closed = definitelyClosed;
|
||||||
for (String remoteParticipantName : subscribers.keySet()) {
|
for (String remoteParticipantName : subscribers.keySet()) {
|
||||||
SubscriberEndpoint subscriber = this.subscribers.get(remoteParticipantName);
|
SubscriberEndpoint subscriber = this.subscribers.get(remoteParticipantName);
|
||||||
if (subscriber != null && subscriber.getEndpoint() != null) {
|
if (subscriber != null && subscriber.getEndpoint() != null) {
|
||||||
|
@ -385,6 +317,7 @@ public class KurentoParticipant extends Participant {
|
||||||
this.getParticipantPublicId(), remoteParticipantName);
|
this.getParticipantPublicId(), remoteParticipantName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.subscribers.clear();
|
||||||
releasePublisherEndpoint(reason);
|
releasePublisherEndpoint(reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,8 +330,8 @@ public class KurentoParticipant extends Participant {
|
||||||
*/
|
*/
|
||||||
public SubscriberEndpoint getNewOrExistingSubscriber(String senderPublicId) {
|
public SubscriberEndpoint getNewOrExistingSubscriber(String senderPublicId) {
|
||||||
|
|
||||||
SubscriberEndpoint sendingEndpoint = new SubscriberEndpoint(webParticipant, this, senderPublicId, pipeline,
|
SubscriberEndpoint sendingEndpoint = new SubscriberEndpoint(webParticipant, this, senderPublicId,
|
||||||
this.openviduConfig);
|
this.getPipeline(), this.openviduConfig);
|
||||||
|
|
||||||
SubscriberEndpoint existingSendingEndpoint = this.subscribers.putIfAbsent(senderPublicId, sendingEndpoint);
|
SubscriberEndpoint existingSendingEndpoint = this.subscribers.putIfAbsent(senderPublicId, sendingEndpoint);
|
||||||
if (existingSendingEndpoint != null) {
|
if (existingSendingEndpoint != null) {
|
||||||
|
@ -440,7 +373,7 @@ public class KurentoParticipant extends Participant {
|
||||||
if (this.openviduConfig.isRecordingModuleEnabled()
|
if (this.openviduConfig.isRecordingModuleEnabled()
|
||||||
&& this.recordingManager.sessionIsBeingRecorded(session.getSessionId())) {
|
&& this.recordingManager.sessionIsBeingRecorded(session.getSessionId())) {
|
||||||
this.recordingManager.stopOneIndividualStreamRecording(session.getSessionId(),
|
this.recordingManager.stopOneIndividualStreamRecording(session.getSessionId(),
|
||||||
this.getPublisherStreamId());
|
this.getPublisherStreamId(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
publisher.unregisterErrorListeners();
|
publisher.unregisterErrorListeners();
|
||||||
|
@ -450,6 +383,7 @@ public class KurentoParticipant extends Participant {
|
||||||
releaseElement(getParticipantPublicId(), publisher.getEndpoint());
|
releaseElement(getParticipantPublicId(), publisher.getEndpoint());
|
||||||
this.streaming = false;
|
this.streaming = false;
|
||||||
publisher = null;
|
publisher = null;
|
||||||
|
this.session.deregisterPublisher();
|
||||||
|
|
||||||
CDR.stopPublisher(this.getParticipantPublicId(), reason);
|
CDR.stopPublisher(this.getParticipantPublicId(), reason);
|
||||||
|
|
||||||
|
@ -596,12 +530,18 @@ public class KurentoParticipant extends Participant {
|
||||||
}
|
}
|
||||||
|
|
||||||
public MediaPipeline getPipeline() {
|
public MediaPipeline getPipeline() {
|
||||||
return this.pipeline;
|
return this.session.getPipeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPublisherStreamId() {
|
public String getPublisherStreamId() {
|
||||||
return this.publisher.getEndpoint().getName();
|
return this.publisher.getStreamId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetPublisherEndpoint() {
|
||||||
|
log.info("Reseting publisher endpoint for participant {}", this.getParticipantPublicId());
|
||||||
|
this.publisher = new PublisherEndpoint(webParticipant, this, this.getParticipantPublicId(),
|
||||||
|
this.session.getPipeline(), this.openviduConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -33,8 +33,11 @@ import org.slf4j.LoggerFactory;
|
||||||
import io.openvidu.client.OpenViduException;
|
import io.openvidu.client.OpenViduException;
|
||||||
import io.openvidu.client.OpenViduException.Code;
|
import io.openvidu.client.OpenViduException.Code;
|
||||||
import io.openvidu.client.internal.ProtocolElements;
|
import io.openvidu.client.internal.ProtocolElements;
|
||||||
|
import io.openvidu.java.client.OpenViduRole;
|
||||||
|
import io.openvidu.java.client.Recording.OutputMode;
|
||||||
import io.openvidu.server.core.Participant;
|
import io.openvidu.server.core.Participant;
|
||||||
import io.openvidu.server.core.Session;
|
import io.openvidu.server.core.Session;
|
||||||
|
import io.openvidu.server.recording.Recording;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Pablo Fuente (pablofuenteperez@gmail.com)
|
* @author Pablo Fuente (pablofuenteperez@gmail.com)
|
||||||
|
@ -54,7 +57,6 @@ public class KurentoSession extends Session {
|
||||||
|
|
||||||
private Object pipelineCreateLock = new Object();
|
private Object pipelineCreateLock = new Object();
|
||||||
private Object pipelineReleaseLock = new Object();
|
private Object pipelineReleaseLock = new Object();
|
||||||
private volatile boolean pipelineReleased = false;
|
|
||||||
private boolean destroyKurentoClient;
|
private boolean destroyKurentoClient;
|
||||||
|
|
||||||
public final ConcurrentHashMap<String, String> publishedStreamIds = new ConcurrentHashMap<>();
|
public final ConcurrentHashMap<String, String> publishedStreamIds = new ConcurrentHashMap<>();
|
||||||
|
@ -73,7 +75,7 @@ public class KurentoSession extends Session {
|
||||||
checkClosed();
|
checkClosed();
|
||||||
createPipeline();
|
createPipeline();
|
||||||
|
|
||||||
KurentoParticipant kurentoParticipant = new KurentoParticipant(participant, this, getPipeline(),
|
KurentoParticipant kurentoParticipant = new KurentoParticipant(participant, this,
|
||||||
kurentoSessionHandler.getInfoHandler(), this.CDR, this.openviduConfig, this.recordingManager);
|
kurentoSessionHandler.getInfoHandler(), this.CDR, this.openviduConfig, this.recordingManager);
|
||||||
participants.put(participant.getParticipantPrivateId(), kurentoParticipant);
|
participants.put(participant.getParticipantPrivateId(), kurentoParticipant);
|
||||||
|
|
||||||
|
@ -105,15 +107,12 @@ public class KurentoSession extends Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancelPublisher(Participant participant, String reason) {
|
public void cancelPublisher(Participant participant, String reason) {
|
||||||
deregisterPublisher();
|
// Cancel all subscribers for this publisher
|
||||||
|
|
||||||
// cancel recv video from this publisher
|
|
||||||
for (Participant subscriber : participants.values()) {
|
for (Participant subscriber : participants.values()) {
|
||||||
if (participant.equals(subscriber)) {
|
if (participant.equals(subscriber)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
((KurentoParticipant) subscriber).cancelReceivingMedia(participant.getParticipantPublicId(), reason);
|
((KurentoParticipant) subscriber).cancelReceivingMedia(participant.getParticipantPublicId(), reason);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("SESSION {}: Unsubscribed other participants {} from the publisher {}", sessionId,
|
log.debug("SESSION {}: Unsubscribed other participants {} from the publisher {}", sessionId,
|
||||||
|
@ -134,11 +133,9 @@ public class KurentoSession extends Session {
|
||||||
participant.releaseAllFilters();
|
participant.releaseAllFilters();
|
||||||
|
|
||||||
log.info("PARTICIPANT {}: Leaving session {}", participant.getParticipantPublicId(), this.sessionId);
|
log.info("PARTICIPANT {}: Leaving session {}", participant.getParticipantPublicId(), this.sessionId);
|
||||||
if (participant.isStreaming()) {
|
|
||||||
this.deregisterPublisher();
|
|
||||||
}
|
|
||||||
this.removeParticipant(participant, reason);
|
this.removeParticipant(participant, reason);
|
||||||
participant.close(reason);
|
participant.close(reason, true);
|
||||||
|
|
||||||
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
|
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(participant.getParticipantPublicId())) {
|
||||||
CDR.recordParticipantLeft(participant, participant.getSession().getSessionId(), reason);
|
CDR.recordParticipantLeft(participant, participant.getSession().getSessionId(), reason);
|
||||||
|
@ -151,12 +148,12 @@ public class KurentoSession extends Session {
|
||||||
|
|
||||||
for (Participant participant : participants.values()) {
|
for (Participant participant : participants.values()) {
|
||||||
((KurentoParticipant) participant).releaseAllFilters();
|
((KurentoParticipant) participant).releaseAllFilters();
|
||||||
((KurentoParticipant) participant).close(reason);
|
((KurentoParticipant) participant).close(reason, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
participants.clear();
|
participants.clear();
|
||||||
|
|
||||||
closePipeline();
|
closePipeline(null);
|
||||||
|
|
||||||
log.debug("Session {} closed", this.sessionId);
|
log.debug("Session {} closed", this.sessionId);
|
||||||
|
|
||||||
|
@ -248,23 +245,30 @@ public class KurentoSession extends Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closePipeline() {
|
private void closePipeline(Runnable callback) {
|
||||||
synchronized (pipelineReleaseLock) {
|
synchronized (pipelineReleaseLock) {
|
||||||
if (pipeline == null || pipelineReleased) {
|
if (pipeline == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getPipeline().release(new Continuation<Void>() {
|
getPipeline().release(new Continuation<Void>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Void result) throws Exception {
|
public void onSuccess(Void result) throws Exception {
|
||||||
log.debug("SESSION {}: Released Pipeline", sessionId);
|
log.debug("SESSION {}: Released Pipeline", sessionId);
|
||||||
pipelineReleased = true;
|
pipeline = null;
|
||||||
|
pipelineLatch = new CountDownLatch(1);
|
||||||
|
if (callback != null) {
|
||||||
|
callback.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable cause) throws Exception {
|
public void onError(Throwable cause) throws Exception {
|
||||||
log.warn("SESSION {}: Could not successfully release Pipeline", sessionId, cause);
|
log.warn("SESSION {}: Could not successfully release Pipeline", sessionId, cause);
|
||||||
pipelineReleased = true;
|
pipeline = null;
|
||||||
|
pipelineLatch = new CountDownLatch(1);
|
||||||
|
if (callback != null) {
|
||||||
|
callback.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -274,4 +278,49 @@ public class KurentoSession extends Session {
|
||||||
return this.publishedStreamIds.get(streamId);
|
return this.publishedStreamIds.get(streamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void restartStatusInKurento() {
|
||||||
|
|
||||||
|
log.info("Reseting remote media objects for active session {}", this.sessionId);
|
||||||
|
|
||||||
|
// Stop recording if session is being recorded
|
||||||
|
if (recordingManager.sessionIsBeingRecorded(this.sessionId)) {
|
||||||
|
Recording stoppedRecording = this.recordingManager.forceStopRecording(this, "mediaServerDisconnect");
|
||||||
|
if (OutputMode.COMPOSED.equals(stoppedRecording.getOutputMode()) && stoppedRecording.hasVideo()) {
|
||||||
|
recordingManager.getSessionManager().evictParticipant(
|
||||||
|
this.getParticipantByPublicId(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID), null, null,
|
||||||
|
"EVICT_RECORDER");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close all MediaEndpoints of participants
|
||||||
|
this.getParticipants().forEach(p -> {
|
||||||
|
KurentoParticipant kParticipant = (KurentoParticipant) p;
|
||||||
|
final boolean wasStreaming = kParticipant.isStreaming();
|
||||||
|
kParticipant.releaseAllFilters();
|
||||||
|
kParticipant.close("mediaServerDisconnect", false);
|
||||||
|
if (wasStreaming) {
|
||||||
|
kurentoSessionHandler.onUnpublishMedia(kParticipant, this.getParticipants(), null, null, null,
|
||||||
|
"mediaServerDisconnect");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Release pipeline, create a new one and prepare new PublisherEndpoints for
|
||||||
|
// allowed users
|
||||||
|
this.closePipeline(() -> {
|
||||||
|
createPipeline();
|
||||||
|
try {
|
||||||
|
if (!pipelineLatch.await(20, TimeUnit.SECONDS)) {
|
||||||
|
throw new Exception("MediaPipleine was not created in 20 seconds");
|
||||||
|
}
|
||||||
|
getParticipants().forEach(p -> {
|
||||||
|
if (!OpenViduRole.SUBSCRIBER.equals(p.getToken().getRole())) {
|
||||||
|
((KurentoParticipant) p).resetPublisherEndpoint();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error waiting to new MediaPipeline on KurentoSession restart: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,8 @@ public class PublisherEndpoint extends MediaEndpoint {
|
||||||
|
|
||||||
private Map<String, ListenerSubscription> elementsErrorSubscriptions = new HashMap<String, ListenerSubscription>();
|
private Map<String, ListenerSubscription> elementsErrorSubscriptions = new HashMap<String, ListenerSubscription>();
|
||||||
|
|
||||||
|
private String streamId;
|
||||||
|
|
||||||
public PublisherEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline,
|
public PublisherEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline,
|
||||||
OpenviduConfig openviduConfig) {
|
OpenviduConfig openviduConfig) {
|
||||||
super(web, owner, endpointName, pipeline, openviduConfig, log);
|
super(web, owner, endpointName, pipeline, openviduConfig, log);
|
||||||
|
@ -136,7 +138,7 @@ public class PublisherEndpoint extends MediaEndpoint {
|
||||||
|
|
||||||
public boolean removeParticipantAsListenerOfFilterEvent(String eventType, String participantPublicId) {
|
public boolean removeParticipantAsListenerOfFilterEvent(String eventType, String participantPublicId) {
|
||||||
if (!this.subscribersToFilterEvents.containsKey(eventType)) {
|
if (!this.subscribersToFilterEvents.containsKey(eventType)) {
|
||||||
String streamId = this.getEndpoint().getName();
|
String streamId = this.getStreamId();
|
||||||
log.error("Request to removeFilterEventListener to stream {} gone wrong: Filter {} has no listener added",
|
log.error("Request to removeFilterEventListener to stream {} gone wrong: Filter {} has no listener added",
|
||||||
streamId, eventType);
|
streamId, eventType);
|
||||||
throw new OpenViduException(Code.FILTER_EVENT_LISTENER_NOT_FOUND,
|
throw new OpenViduException(Code.FILTER_EVENT_LISTENER_NOT_FOUND,
|
||||||
|
@ -164,16 +166,12 @@ public class PublisherEndpoint extends MediaEndpoint {
|
||||||
* itself (after applying the intermediate media elements and the
|
* itself (after applying the intermediate media elements and the
|
||||||
* {@link PassThrough}) to allow loopback of the media stream.
|
* {@link PassThrough}) to allow loopback of the media stream.
|
||||||
*
|
*
|
||||||
* @param sdpType
|
* @param sdpType indicates the type of the sdpString (offer or
|
||||||
* indicates the type of the sdpString (offer or answer)
|
* answer)
|
||||||
* @param sdpString
|
* @param sdpString offer or answer from the remote peer
|
||||||
* offer or answer from the remote peer
|
* @param doLoopback loopback flag
|
||||||
* @param doLoopback
|
* @param loopbackAlternativeSrc alternative loopback source
|
||||||
* loopback flag
|
* @param loopbackConnectionType how to connect the loopback source
|
||||||
* @param loopbackAlternativeSrc
|
|
||||||
* alternative loopback source
|
|
||||||
* @param loopbackConnectionType
|
|
||||||
* how to connect the loopback source
|
|
||||||
* @return the SDP response (the answer if processing an offer SDP, otherwise is
|
* @return the SDP response (the answer if processing an offer SDP, otherwise is
|
||||||
* the updated offer generated previously by this endpoint)
|
* the updated offer generated previously by this endpoint)
|
||||||
*/
|
*/
|
||||||
|
@ -238,12 +236,10 @@ public class PublisherEndpoint extends MediaEndpoint {
|
||||||
* is left ready for when the connections between elements will materialize and
|
* is left ready for when the connections between elements will materialize and
|
||||||
* the streaming begins.
|
* the streaming begins.
|
||||||
*
|
*
|
||||||
* @param shaper
|
* @param shaper {@link MediaElement} that will be linked to the end of the
|
||||||
* {@link MediaElement} that will be linked to the end of the chain
|
* chain (e.g. a filter)
|
||||||
* (e.g. a filter)
|
|
||||||
* @return the element's id
|
* @return the element's id
|
||||||
* @throws OpenViduException
|
* @throws OpenViduException if thrown, the media element was not added
|
||||||
* if thrown, the media element was not added
|
|
||||||
*/
|
*/
|
||||||
public String apply(GenericMediaElement shaper) throws OpenViduException {
|
public String apply(GenericMediaElement shaper) throws OpenViduException {
|
||||||
return apply(shaper, null);
|
return apply(shaper, null);
|
||||||
|
@ -253,15 +249,12 @@ public class PublisherEndpoint extends MediaEndpoint {
|
||||||
* Same as {@link #apply(MediaElement)}, can specify the media type that will be
|
* Same as {@link #apply(MediaElement)}, can specify the media type that will be
|
||||||
* streamed through the shaper element.
|
* streamed through the shaper element.
|
||||||
*
|
*
|
||||||
* @param shaper
|
* @param shaper {@link MediaElement} that will be linked to the end of the
|
||||||
* {@link MediaElement} that will be linked to the end of the chain
|
* chain (e.g. a filter)
|
||||||
* (e.g. a filter)
|
* @param type indicates which type of media will be connected to the shaper
|
||||||
* @param type
|
|
||||||
* indicates which type of media will be connected to the shaper
|
|
||||||
* ({@link MediaType}), if null then the connection is mixed
|
* ({@link MediaType}), if null then the connection is mixed
|
||||||
* @return the element's id
|
* @return the element's id
|
||||||
* @throws OpenViduException
|
* @throws OpenViduException if thrown, the media element was not added
|
||||||
* if thrown, the media element was not added
|
|
||||||
*/
|
*/
|
||||||
public synchronized String apply(GenericMediaElement shaper, MediaType type) throws OpenViduException {
|
public synchronized String apply(GenericMediaElement shaper, MediaType type) throws OpenViduException {
|
||||||
String id = shaper.getId();
|
String id = shaper.getId();
|
||||||
|
@ -299,10 +292,8 @@ public class PublisherEndpoint extends MediaEndpoint {
|
||||||
* object is released. If the chain is connected, both adjacent remaining
|
* object is released. If the chain is connected, both adjacent remaining
|
||||||
* elements will be interconnected.
|
* elements will be interconnected.
|
||||||
*
|
*
|
||||||
* @param shaper
|
* @param shaper {@link MediaElement} that will be removed from the chain
|
||||||
* {@link MediaElement} that will be removed from the chain
|
* @throws OpenViduException if thrown, the media element was not removed
|
||||||
* @throws OpenViduException
|
|
||||||
* if thrown, the media element was not removed
|
|
||||||
*/
|
*/
|
||||||
public synchronized void revert(MediaElement shaper) throws OpenViduException {
|
public synchronized void revert(MediaElement shaper) throws OpenViduException {
|
||||||
revert(shaper, true);
|
revert(shaper, true);
|
||||||
|
@ -477,9 +468,9 @@ public class PublisherEndpoint extends MediaEndpoint {
|
||||||
*
|
*
|
||||||
* @param source
|
* @param source
|
||||||
* @param sink
|
* @param sink
|
||||||
* @param type
|
* @param type if null,
|
||||||
* if null, {@link #internalSinkConnect(MediaElement, MediaElement)}
|
* {@link #internalSinkConnect(MediaElement, MediaElement)} will
|
||||||
* will be used instead
|
* be used instead
|
||||||
* @see #internalSinkConnect(MediaElement, MediaElement)
|
* @see #internalSinkConnect(MediaElement, MediaElement)
|
||||||
*/
|
*/
|
||||||
private void internalSinkConnect(final MediaElement source, final MediaElement sink, final MediaType type) {
|
private void internalSinkConnect(final MediaElement source, final MediaElement sink, final MediaType type) {
|
||||||
|
@ -524,9 +515,9 @@ public class PublisherEndpoint extends MediaEndpoint {
|
||||||
*
|
*
|
||||||
* @param source
|
* @param source
|
||||||
* @param sink
|
* @param sink
|
||||||
* @param type
|
* @param type if null,
|
||||||
* if null, {@link #internalSinkConnect(MediaElement, MediaElement)}
|
* {@link #internalSinkConnect(MediaElement, MediaElement)} will
|
||||||
* will be used instead
|
* be used instead
|
||||||
* @see #internalSinkConnect(MediaElement, MediaElement)
|
* @see #internalSinkConnect(MediaElement, MediaElement)
|
||||||
*/
|
*/
|
||||||
private void internalSinkDisconnect(final MediaElement source, final MediaElement sink, final MediaType type) {
|
private void internalSinkDisconnect(final MediaElement source, final MediaElement sink, final MediaType type) {
|
||||||
|
@ -565,7 +556,7 @@ public class PublisherEndpoint extends MediaEndpoint {
|
||||||
@Override
|
@Override
|
||||||
public JsonObject toJson() {
|
public JsonObject toJson() {
|
||||||
JsonObject json = super.toJson();
|
JsonObject json = super.toJson();
|
||||||
json.addProperty("streamId", this.getEndpoint().getName());
|
json.addProperty("streamId", this.getStreamId());
|
||||||
json.add("mediaOptions", this.mediaOptions.toJson());
|
json.add("mediaOptions", this.mediaOptions.toJson());
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
@ -584,4 +575,13 @@ public class PublisherEndpoint extends MediaEndpoint {
|
||||||
return "{filter: " + ((this.filter != null) ? this.filter.getName() : "null") + ", listener: "
|
return "{filter: " + ((this.filter != null) ? this.filter.getName() : "null") + ", listener: "
|
||||||
+ this.filterListeners.toString() + ", subscribers: " + this.subscribersToFilterEvents.toString() + "}";
|
+ this.filterListeners.toString() + ", subscribers: " + this.subscribersToFilterEvents.toString() + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setStreamId(String publisherStreamId) {
|
||||||
|
this.streamId = publisherStreamId;
|
||||||
|
this.getEndpoint().setName(publisherStreamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStreamId() {
|
||||||
|
return this.streamId != null ? this.streamId : this.getEndpoint().getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class SubscriberEndpoint extends MediaEndpoint {
|
||||||
public JsonObject toJson() {
|
public JsonObject toJson() {
|
||||||
JsonObject json = super.toJson();
|
JsonObject json = super.toJson();
|
||||||
try {
|
try {
|
||||||
json.addProperty("streamId", this.publisher.getEndpoint().getName());
|
json.addProperty("streamId", this.publisher.getStreamId());
|
||||||
} catch (NullPointerException ex) {
|
} catch (NullPointerException ex) {
|
||||||
json.addProperty("streamId", "NOT_FOUND");
|
json.addProperty("streamId", "NOT_FOUND");
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,15 +17,28 @@
|
||||||
|
|
||||||
package io.openvidu.server.kurento.kms;
|
package io.openvidu.server.kurento.kms;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import org.kurento.client.KurentoClient;
|
import org.kurento.client.KurentoClient;
|
||||||
import org.kurento.client.KurentoConnectionListener;
|
import org.kurento.client.KurentoConnectionListener;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import io.openvidu.server.core.SessionManager;
|
||||||
|
import io.openvidu.server.kurento.core.KurentoSession;
|
||||||
|
|
||||||
public class FixedOneKmsManager extends KmsManager {
|
public class FixedOneKmsManager extends KmsManager {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(FixedOneKmsManager.class);
|
private static final Logger log = LoggerFactory.getLogger(FixedOneKmsManager.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
SessionManager sessionManager;
|
||||||
|
|
||||||
|
public static final AtomicBoolean CONNECTED_TO_KMS = new AtomicBoolean(false);
|
||||||
|
public static final AtomicLong TIME_OF_DISCONNECTION = new AtomicLong(0);
|
||||||
|
|
||||||
public FixedOneKmsManager(String kmsWsUri) {
|
public FixedOneKmsManager(String kmsWsUri) {
|
||||||
this(kmsWsUri, 1);
|
this(kmsWsUri, 1);
|
||||||
}
|
}
|
||||||
|
@ -36,21 +49,37 @@ public class FixedOneKmsManager extends KmsManager {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reconnected(boolean isReconnected) {
|
public void reconnected(boolean isReconnected) {
|
||||||
log.warn("Kurento Client reconnected ({}) to KMS with uri {}", isReconnected, kmsWsUri);
|
CONNECTED_TO_KMS.compareAndSet(false, true);
|
||||||
|
if (!isReconnected) {
|
||||||
|
// Different KMS. Reset sessions status (no Publisher or SUbscriber endpoints)
|
||||||
|
log.warn("Kurento Client reconnected to a different KMS instance, with uri {}", kmsWsUri);
|
||||||
|
log.warn("Updating all webrtc endpoints for active sessions");
|
||||||
|
sessionManager.getSessions().forEach(s -> {
|
||||||
|
((KurentoSession) s).restartStatusInKurento();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Same KMS. We can infer that openvidu-server/KMS connection has been lost, but
|
||||||
|
// not the clients/KMS connections
|
||||||
|
log.warn("Kurento Client reconnected to same KMS with uri {}", kmsWsUri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnected() {
|
public void disconnected() {
|
||||||
|
CONNECTED_TO_KMS.compareAndSet(true, false);
|
||||||
|
TIME_OF_DISCONNECTION.set(System.currentTimeMillis());
|
||||||
log.warn("Kurento Client disconnected from KMS with uri {}", kmsWsUri);
|
log.warn("Kurento Client disconnected from KMS with uri {}", kmsWsUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connectionFailed() {
|
public void connectionFailed() {
|
||||||
|
CONNECTED_TO_KMS.set(false);
|
||||||
log.warn("Kurento Client failed connecting to KMS with uri {}", kmsWsUri);
|
log.warn("Kurento Client failed connecting to KMS with uri {}", kmsWsUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connected() {
|
public void connected() {
|
||||||
|
CONNECTED_TO_KMS.compareAndSet(false, true);
|
||||||
log.warn("Kurento Client is now connected to KMS with uri {}", kmsWsUri);
|
log.warn("Kurento Client is now connected to KMS with uri {}", kmsWsUri);
|
||||||
}
|
}
|
||||||
}), kmsWsUri));
|
}), kmsWsUri));
|
||||||
|
|
|
@ -38,6 +38,7 @@ import io.openvidu.client.OpenViduException;
|
||||||
import io.openvidu.client.OpenViduException.Code;
|
import io.openvidu.client.OpenViduException.Code;
|
||||||
import io.openvidu.server.kurento.core.KurentoSession;
|
import io.openvidu.server.kurento.core.KurentoSession;
|
||||||
import io.openvidu.server.kurento.endpoint.PublisherEndpoint;
|
import io.openvidu.server.kurento.endpoint.PublisherEndpoint;
|
||||||
|
import io.openvidu.server.kurento.kms.FixedOneKmsManager;
|
||||||
|
|
||||||
public class CompositeWrapper {
|
public class CompositeWrapper {
|
||||||
|
|
||||||
|
@ -86,7 +87,8 @@ public class CompositeWrapper {
|
||||||
this.recorderEndpoint.record();
|
this.recorderEndpoint.record();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void stopCompositeRecording(CountDownLatch stopLatch) {
|
public synchronized void stopCompositeRecording(CountDownLatch stopLatch, boolean forceAfterKmsRestart) {
|
||||||
|
if (!forceAfterKmsRestart) {
|
||||||
this.recorderEndpoint.addStoppedListener(new EventListener<StoppedEvent>() {
|
this.recorderEndpoint.addStoppedListener(new EventListener<StoppedEvent>() {
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(StoppedEvent event) {
|
public void onEvent(StoppedEvent event) {
|
||||||
|
@ -99,6 +101,13 @@ public class CompositeWrapper {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.recorderEndpoint.stop();
|
this.recorderEndpoint.stop();
|
||||||
|
} else {
|
||||||
|
endTime = FixedOneKmsManager.TIME_OF_DISCONNECTION.get();
|
||||||
|
stopLatch.countDown();
|
||||||
|
log.warn("Forcing composed audio-only recording stop after KMS restart in session {}",
|
||||||
|
this.session.getSessionId());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connectPublisherEndpoint(PublisherEndpoint endpoint) throws OpenViduException {
|
public void connectPublisherEndpoint(PublisherEndpoint endpoint) throws OpenViduException {
|
||||||
|
|
|
@ -100,10 +100,14 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Recording stopRecording(Session session, Recording recording, String reason) {
|
public Recording stopRecording(Session session, Recording recording, String reason) {
|
||||||
|
return this.stopRecording(session, recording, reason, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Recording stopRecording(Session session, Recording recording, String reason, boolean forceAfterKmsRestart) {
|
||||||
if (recording.hasVideo()) {
|
if (recording.hasVideo()) {
|
||||||
return this.stopRecordingWithVideo(session, recording, reason);
|
return this.stopRecordingWithVideo(session, recording, reason);
|
||||||
} else {
|
} else {
|
||||||
return this.stopRecordingAudioOnly(session, recording, reason);
|
return this.stopRecordingAudioOnly(session, recording, reason, forceAfterKmsRestart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,7 +334,8 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
return recording;
|
return recording;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Recording stopRecordingAudioOnly(Session session, Recording recording, String reason) {
|
private Recording stopRecordingAudioOnly(Session session, Recording recording, String reason,
|
||||||
|
boolean forceAfterKmsRestart) {
|
||||||
|
|
||||||
log.info("Stopping composed (audio-only) recording {} of session {}. Reason: {}", recording.getId(),
|
log.info("Stopping composed (audio-only) recording {} of session {}. Reason: {}", recording.getId(),
|
||||||
recording.getSessionId(), reason);
|
recording.getSessionId(), reason);
|
||||||
|
@ -349,7 +354,8 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
CompositeWrapper compositeWrapper = this.composites.remove(sessionId);
|
CompositeWrapper compositeWrapper = this.composites.remove(sessionId);
|
||||||
|
|
||||||
final CountDownLatch stoppedCountDown = new CountDownLatch(1);
|
final CountDownLatch stoppedCountDown = new CountDownLatch(1);
|
||||||
compositeWrapper.stopCompositeRecording(stoppedCountDown);
|
|
||||||
|
compositeWrapper.stopCompositeRecording(stoppedCountDown, forceAfterKmsRestart);
|
||||||
try {
|
try {
|
||||||
if (!stoppedCountDown.await(5, TimeUnit.SECONDS)) {
|
if (!stoppedCountDown.await(5, TimeUnit.SECONDS)) {
|
||||||
recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
|
recording.setStatus(io.openvidu.java.client.Recording.Status.failed);
|
||||||
|
|
|
@ -114,6 +114,10 @@ public class RecordingManager {
|
||||||
return this.sessionHandler;
|
return this.sessionHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SessionManager getSessionManager() {
|
||||||
|
return this.sessionManager;
|
||||||
|
}
|
||||||
|
|
||||||
public void initializeRecordingManager() throws OpenViduException {
|
public void initializeRecordingManager() throws OpenViduException {
|
||||||
|
|
||||||
RecordingManager.IMAGE_TAG = openviduConfig.getOpenViduRecordingVersion();
|
RecordingManager.IMAGE_TAG = openviduConfig.getOpenViduRecordingVersion();
|
||||||
|
@ -207,6 +211,21 @@ public class RecordingManager {
|
||||||
return recording;
|
return recording;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Recording forceStopRecording(Session session, String reason) {
|
||||||
|
Recording recording;
|
||||||
|
recording = this.sessionsRecordings.get(session.getSessionId());
|
||||||
|
switch (recording.getOutputMode()) {
|
||||||
|
case COMPOSED:
|
||||||
|
recording = this.composedRecordingService.stopRecording(session, recording, reason, true);
|
||||||
|
break;
|
||||||
|
case INDIVIDUAL:
|
||||||
|
recording = this.singleStreamRecordingService.stopRecording(session, recording, reason, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.abortAutomaticRecordingStopThread(session);
|
||||||
|
return recording;
|
||||||
|
}
|
||||||
|
|
||||||
public void startOneIndividualStreamRecording(Session session, String recordingId, MediaProfileSpecType profile,
|
public void startOneIndividualStreamRecording(Session session, String recordingId, MediaProfileSpecType profile,
|
||||||
Participant participant) {
|
Participant participant) {
|
||||||
Recording recording = this.sessionsRecordings.get(session.getSessionId());
|
Recording recording = this.sessionsRecordings.get(session.getSessionId());
|
||||||
|
@ -230,7 +249,7 @@ public class RecordingManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopOneIndividualStreamRecording(String sessionId, String streamId) {
|
public void stopOneIndividualStreamRecording(String sessionId, String streamId, boolean forceAfterKmsRestart) {
|
||||||
Recording recording = this.sessionsRecordings.get(sessionId);
|
Recording recording = this.sessionsRecordings.get(sessionId);
|
||||||
if (recording == null) {
|
if (recording == null) {
|
||||||
log.error("Cannot stop recording of existing stream {}. Session {} is not being recorded", streamId,
|
log.error("Cannot stop recording of existing stream {}. Session {} is not being recorded", streamId,
|
||||||
|
@ -241,7 +260,7 @@ public class RecordingManager {
|
||||||
log.info("Stopping RecorderEndpoint in session {} for stream of participant {}", sessionId, streamId);
|
log.info("Stopping RecorderEndpoint in session {} for stream of participant {}", sessionId, streamId);
|
||||||
final CountDownLatch stoppedCountDown = new CountDownLatch(1);
|
final CountDownLatch stoppedCountDown = new CountDownLatch(1);
|
||||||
this.singleStreamRecordingService.stopRecorderEndpointOfPublisherEndpoint(sessionId, streamId,
|
this.singleStreamRecordingService.stopRecorderEndpointOfPublisherEndpoint(sessionId, streamId,
|
||||||
stoppedCountDown);
|
stoppedCountDown, forceAfterKmsRestart);
|
||||||
try {
|
try {
|
||||||
if (!stoppedCountDown.await(5, TimeUnit.SECONDS)) {
|
if (!stoppedCountDown.await(5, TimeUnit.SECONDS)) {
|
||||||
log.error("Error waiting for recorder endpoint of stream {} to stop in session {}", streamId,
|
log.error("Error waiting for recorder endpoint of stream {} to stop in session {}", streamId,
|
||||||
|
@ -362,7 +381,7 @@ public class RecordingManager {
|
||||||
sessionManager.closeSessionAndEmptyCollections(session, "automaticStop");
|
sessionManager.closeSessionAndEmptyCollections(session, "automaticStop");
|
||||||
sessionManager.showTokens();
|
sessionManager.showTokens();
|
||||||
} else {
|
} else {
|
||||||
this.stopRecording(null, recordingId, "automaticStop");
|
this.stopRecording(session, recordingId, "automaticStop");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This code is reachable if there already was an automatic stop of a recording
|
// This code is reachable if there already was an automatic stop of a recording
|
||||||
|
|
|
@ -56,6 +56,7 @@ import io.openvidu.server.core.Participant;
|
||||||
import io.openvidu.server.core.Session;
|
import io.openvidu.server.core.Session;
|
||||||
import io.openvidu.server.kurento.core.KurentoParticipant;
|
import io.openvidu.server.kurento.core.KurentoParticipant;
|
||||||
import io.openvidu.server.kurento.endpoint.PublisherEndpoint;
|
import io.openvidu.server.kurento.endpoint.PublisherEndpoint;
|
||||||
|
import io.openvidu.server.kurento.kms.FixedOneKmsManager;
|
||||||
import io.openvidu.server.recording.RecorderEndpointWrapper;
|
import io.openvidu.server.recording.RecorderEndpointWrapper;
|
||||||
import io.openvidu.server.recording.Recording;
|
import io.openvidu.server.recording.Recording;
|
||||||
|
|
||||||
|
@ -127,7 +128,10 @@ public class SingleStreamRecordingService extends RecordingService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Recording stopRecording(Session session, Recording recording, String reason) {
|
public Recording stopRecording(Session session, Recording recording, String reason) {
|
||||||
|
return this.stopRecording(session, recording, reason, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Recording stopRecording(Session session, Recording recording, String reason, boolean forceAfterKmsRestart) {
|
||||||
log.info("Stopping individual ({}) recording {} of session {}. Reason: {}",
|
log.info("Stopping individual ({}) recording {} of session {}. Reason: {}",
|
||||||
recording.hasVideo() ? (recording.hasAudio() ? "video+audio" : "video-only") : "audioOnly",
|
recording.hasVideo() ? (recording.hasAudio() ? "video+audio" : "video-only") : "audioOnly",
|
||||||
recording.getId(), recording.getSessionId(), reason);
|
recording.getId(), recording.getSessionId(), reason);
|
||||||
|
@ -137,7 +141,7 @@ public class SingleStreamRecordingService extends RecordingService {
|
||||||
|
|
||||||
for (RecorderEndpointWrapper wrapper : recorders.get(recording.getSessionId()).values()) {
|
for (RecorderEndpointWrapper wrapper : recorders.get(recording.getSessionId()).values()) {
|
||||||
this.stopRecorderEndpointOfPublisherEndpoint(recording.getSessionId(), wrapper.getStreamId(),
|
this.stopRecorderEndpointOfPublisherEndpoint(recording.getSessionId(), wrapper.getStreamId(),
|
||||||
stoppedCountDown);
|
stoppedCountDown, forceAfterKmsRestart);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (!stoppedCountDown.await(5, TimeUnit.SECONDS)) {
|
if (!stoppedCountDown.await(5, TimeUnit.SECONDS)) {
|
||||||
|
@ -219,10 +223,10 @@ public class SingleStreamRecordingService extends RecordingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopRecorderEndpointOfPublisherEndpoint(String sessionId, String streamId,
|
public void stopRecorderEndpointOfPublisherEndpoint(String sessionId, String streamId,
|
||||||
CountDownLatch globalStopLatch) {
|
CountDownLatch globalStopLatch, boolean forceAfterKmsRestart) {
|
||||||
log.info("Stopping single stream recorder for stream {} in session {}", streamId, sessionId);
|
log.info("Stopping single stream recorder for stream {} in session {}", streamId, sessionId);
|
||||||
final RecorderEndpointWrapper finalWrapper = this.recorders.get(sessionId).remove(streamId);
|
final RecorderEndpointWrapper finalWrapper = this.recorders.get(sessionId).remove(streamId);
|
||||||
if (finalWrapper != null) {
|
if (finalWrapper != null && !forceAfterKmsRestart) {
|
||||||
finalWrapper.getRecorder().addStoppedListener(new EventListener<StoppedEvent>() {
|
finalWrapper.getRecorder().addStoppedListener(new EventListener<StoppedEvent>() {
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(StoppedEvent event) {
|
public void onEvent(StoppedEvent event) {
|
||||||
|
@ -234,8 +238,15 @@ public class SingleStreamRecordingService extends RecordingService {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
finalWrapper.getRecorder().stop();
|
finalWrapper.getRecorder().stop();
|
||||||
|
} else {
|
||||||
|
if (forceAfterKmsRestart) {
|
||||||
|
finalWrapper.setEndTime(FixedOneKmsManager.TIME_OF_DISCONNECTION.get());
|
||||||
|
generateIndividualMetadataFile(finalWrapper);
|
||||||
|
log.warn("Forcing individual recording stop after KMS restart for stream {} in session {}", streamId,
|
||||||
|
sessionId);
|
||||||
} else {
|
} else {
|
||||||
log.error("Stream {} wasn't being recorded in session {}", streamId, sessionId);
|
log.error("Stream {} wasn't being recorded in session {}", streamId, sessionId);
|
||||||
|
}
|
||||||
globalStopLatch.countDown();
|
globalStopLatch.countDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue