mirror of https://github.com/OpenVidu/openvidu.git
KMS events stored for each MediaEndpoint
parent
2c90f65535
commit
65cdea70f6
|
@ -27,11 +27,6 @@ public class Participant {
|
||||||
private String serverMetadata = ""; // Metadata provided on server side
|
private String serverMetadata = ""; // Metadata provided on server side
|
||||||
private Token token; // Token associated to this participant
|
private Token token; // Token associated to this participant
|
||||||
|
|
||||||
protected boolean audioActive = true;
|
|
||||||
protected boolean videoActive = true;
|
|
||||||
protected String typeOfVideo; // CAMERA, SCREEN
|
|
||||||
protected int frameRate;
|
|
||||||
|
|
||||||
protected boolean streaming = false;
|
protected boolean streaming = false;
|
||||||
protected volatile boolean closed;
|
protected volatile boolean closed;
|
||||||
|
|
||||||
|
@ -98,38 +93,6 @@ public class Participant {
|
||||||
this.streaming = streaming;
|
this.streaming = streaming;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAudioActive() {
|
|
||||||
return audioActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAudioActive(boolean active) {
|
|
||||||
this.audioActive = active;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVideoActive() {
|
|
||||||
return videoActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVideoActive(boolean active) {
|
|
||||||
this.videoActive = active;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTypeOfVideo() {
|
|
||||||
return this.typeOfVideo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTypeOfVideo(String typeOfVideo) {
|
|
||||||
this.typeOfVideo = typeOfVideo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFrameRate() {
|
|
||||||
return this.frameRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFrameRate(int frameRate) {
|
|
||||||
this.frameRate = frameRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPublisherStremId() {
|
public String getPublisherStremId() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import io.openvidu.client.internal.ProtocolElements;
|
||||||
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.kurento.core.KurentoParticipant;
|
||||||
import io.openvidu.server.recording.Recording;
|
import io.openvidu.server.recording.Recording;
|
||||||
import io.openvidu.server.rpc.RpcNotificationService;
|
import io.openvidu.server.rpc.RpcNotificationService;
|
||||||
|
|
||||||
|
@ -94,17 +95,20 @@ public class SessionEventsHandler {
|
||||||
existingParticipant.getFullMetadata());
|
existingParticipant.getFullMetadata());
|
||||||
|
|
||||||
if (existingParticipant.isStreaming()) {
|
if (existingParticipant.isStreaming()) {
|
||||||
|
|
||||||
|
KurentoParticipant kParticipant = (KurentoParticipant) existingParticipant;
|
||||||
|
|
||||||
JsonObject stream = new JsonObject();
|
JsonObject stream = new JsonObject();
|
||||||
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMID_PARAM,
|
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMID_PARAM,
|
||||||
existingParticipant.getPublisherStremId());
|
existingParticipant.getPublisherStremId());
|
||||||
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMAUDIOACTIVE_PARAM,
|
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMAUDIOACTIVE_PARAM,
|
||||||
existingParticipant.isAudioActive());
|
kParticipant.getPublisherMediaOptions().audioActive);
|
||||||
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMVIDEOACTIVE_PARAM,
|
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMVIDEOACTIVE_PARAM,
|
||||||
existingParticipant.isVideoActive());
|
kParticipant.getPublisherMediaOptions().videoActive);
|
||||||
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMTYPEOFVIDEO_PARAM,
|
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMTYPEOFVIDEO_PARAM,
|
||||||
existingParticipant.getTypeOfVideo());
|
kParticipant.getPublisherMediaOptions().typeOfVideo);
|
||||||
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMFRAMERATE_PARAM,
|
stream.addProperty(ProtocolElements.JOINROOM_PEERSTREAMFRAMERATE_PARAM,
|
||||||
existingParticipant.getFrameRate());
|
kParticipant.getPublisherMediaOptions().frameRate);
|
||||||
|
|
||||||
JsonArray streamsArray = new JsonArray();
|
JsonArray streamsArray = new JsonArray();
|
||||||
streamsArray.add(stream);
|
streamsArray.add(stream);
|
||||||
|
|
|
@ -47,6 +47,7 @@ import io.openvidu.server.config.InfoHandler;
|
||||||
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.MutedMediaType;
|
import io.openvidu.server.kurento.MutedMediaType;
|
||||||
|
import io.openvidu.server.kurento.endpoint.KmsEvent;
|
||||||
import io.openvidu.server.kurento.endpoint.MediaEndpoint;
|
import io.openvidu.server.kurento.endpoint.MediaEndpoint;
|
||||||
import io.openvidu.server.kurento.endpoint.PublisherEndpoint;
|
import io.openvidu.server.kurento.endpoint.PublisherEndpoint;
|
||||||
import io.openvidu.server.kurento.endpoint.SdpType;
|
import io.openvidu.server.kurento.endpoint.SdpType;
|
||||||
|
@ -70,13 +71,13 @@ 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, InfoHandler infoHandler, CallDetailRecord CDR) {
|
public KurentoParticipant(Participant participant, KurentoSession kurentoSession, MediaPipeline pipeline,
|
||||||
|
InfoHandler infoHandler, CallDetailRecord CDR) {
|
||||||
super(participant.getParticipantPrivateId(), participant.getParticipantPublicId(), participant.getToken(),
|
super(participant.getParticipantPrivateId(), participant.getParticipantPublicId(), participant.getToken(),
|
||||||
participant.getClientMetadata());
|
participant.getClientMetadata());
|
||||||
this.session = kurentoSession;
|
this.session = kurentoSession;
|
||||||
this.pipeline = pipeline;
|
this.pipeline = pipeline;
|
||||||
this.publisher = new PublisherEndpoint(webParticipant, this, participant.getParticipantPublicId(),
|
this.publisher = new PublisherEndpoint(webParticipant, this, participant.getParticipantPublicId(), pipeline);
|
||||||
pipeline);
|
|
||||||
|
|
||||||
for (Participant other : session.getParticipants()) {
|
for (Participant other : session.getParticipants()) {
|
||||||
if (!other.getParticipantPublicId().equals(this.getParticipantPublicId())) {
|
if (!other.getParticipantPublicId().equals(this.getParticipantPublicId())) {
|
||||||
|
@ -88,20 +89,21 @@ public class KurentoParticipant extends Participant {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createPublishingEndpoint(MediaOptions mediaOptions) {
|
public void createPublishingEndpoint(MediaOptions mediaOptions) {
|
||||||
|
|
||||||
publisher.createEndpoint(endPointLatch);
|
publisher.createEndpoint(endPointLatch);
|
||||||
if (getPublisher().getEndpoint() == null) {
|
if (getPublisher().getEndpoint() == null) {
|
||||||
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE, "Unable to create publisher endpoint");
|
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE, "Unable to create publisher endpoint");
|
||||||
}
|
}
|
||||||
|
publisher.setMediaOptions(mediaOptions);
|
||||||
String publisherStreamId = this.getParticipantPublicId() + "_" +
|
|
||||||
(mediaOptions.videoActive ? mediaOptions.typeOfVideo : "MICRO") + "_" +
|
String publisherStreamId = this.getParticipantPublicId() + "_"
|
||||||
RandomStringUtils.random(5, true, false).toUpperCase();
|
+ (mediaOptions.videoActive ? mediaOptions.typeOfVideo : "MICRO") + "_"
|
||||||
|
+ RandomStringUtils.random(5, true, false).toUpperCase();
|
||||||
this.publisher.getEndpoint().addTag("name", publisherStreamId);
|
this.publisher.getEndpoint().addTag("name", publisherStreamId);
|
||||||
addEndpointListeners(this.publisher);
|
addEndpointListeners(this.publisher);
|
||||||
|
|
||||||
CDR.recordNewPublisher(this, this.session.getSessionId(), mediaOptions);
|
CDR.recordNewPublisher(this, this.session.getSessionId(), mediaOptions);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shapePublisherMedia(MediaElement element, MediaType type) {
|
public void shapePublisherMedia(MediaElement element, MediaType type) {
|
||||||
|
@ -173,6 +175,10 @@ public class KurentoParticipant extends Participant {
|
||||||
}
|
}
|
||||||
return this.publisher;
|
return this.publisher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MediaOptions getPublisherMediaOptions() {
|
||||||
|
return this.publisher.getMediaOptions();
|
||||||
|
}
|
||||||
|
|
||||||
public KurentoSession getSession() {
|
public KurentoSession getSession() {
|
||||||
return session;
|
return session;
|
||||||
|
@ -230,18 +236,16 @@ 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(),
|
this.publisher = new PublisherEndpoint(webParticipant, this, this.getParticipantPublicId(), pipeline);
|
||||||
pipeline);
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String receiveMediaFrom(Participant sender, String sdpOffer) {
|
public String receiveMediaFrom(Participant sender, String sdpOffer) {
|
||||||
final String senderName = sender.getParticipantPublicId();
|
final String senderName = sender.getParticipantPublicId();
|
||||||
|
|
||||||
log.info("PARTICIPANT {}: Request to receive media from {} in room {}", this.getParticipantPublicId(), senderName,
|
log.info("PARTICIPANT {}: Request to receive media from {} in room {}", this.getParticipantPublicId(),
|
||||||
this.session.getSessionId());
|
senderName, this.session.getSessionId());
|
||||||
log.trace("PARTICIPANT {}: SdpOffer for {} is {}", this.getParticipantPublicId(), senderName, sdpOffer);
|
log.trace("PARTICIPANT {}: SdpOffer for {} is {}", this.getParticipantPublicId(), senderName, sdpOffer);
|
||||||
|
|
||||||
if (senderName.equals(this.getParticipantPublicId())) {
|
if (senderName.equals(this.getParticipantPublicId())) {
|
||||||
|
@ -286,7 +290,7 @@ public class KurentoParticipant extends Participant {
|
||||||
}
|
}
|
||||||
|
|
||||||
String subscriberStreamId = this.getParticipantPublicId() + "_" + kSender.getPublisherStremId();
|
String subscriberStreamId = this.getParticipantPublicId() + "_" + kSender.getPublisherStremId();
|
||||||
|
|
||||||
subscriber.getEndpoint().addTag("name", subscriberStreamId);
|
subscriber.getEndpoint().addTag("name", subscriberStreamId);
|
||||||
|
|
||||||
addEndpointListeners(subscriber);
|
addEndpointListeners(subscriber);
|
||||||
|
@ -300,13 +304,13 @@ public class KurentoParticipant extends Participant {
|
||||||
try {
|
try {
|
||||||
String sdpAnswer = subscriber.subscribe(sdpOffer, kSender.getPublisher());
|
String sdpAnswer = subscriber.subscribe(sdpOffer, kSender.getPublisher());
|
||||||
log.trace("PARTICIPANT {}: Subscribing SdpAnswer is {}", this.getParticipantPublicId(), sdpAnswer);
|
log.trace("PARTICIPANT {}: Subscribing SdpAnswer is {}", this.getParticipantPublicId(), sdpAnswer);
|
||||||
log.info("PARTICIPANT {}: Is now receiving video from {} in room {}", this.getParticipantPublicId(), senderName,
|
log.info("PARTICIPANT {}: Is now receiving video from {} in room {}", this.getParticipantPublicId(),
|
||||||
this.session.getSessionId());
|
senderName, this.session.getSessionId());
|
||||||
|
|
||||||
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(this.getParticipantPublicId())) {
|
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(this.getParticipantPublicId())) {
|
||||||
CDR.recordNewSubscriber(this, this.session.getSessionId(), sender.getParticipantPublicId());
|
CDR.recordNewSubscriber(this, this.session.getSessionId(), sender.getParticipantPublicId());
|
||||||
}
|
}
|
||||||
|
|
||||||
return sdpAnswer;
|
return sdpAnswer;
|
||||||
} catch (KurentoServerException e) {
|
} catch (KurentoServerException e) {
|
||||||
// TODO Check object status when KurentoClient sets this info in the object
|
// TODO Check object status when KurentoClient sets this info in the object
|
||||||
|
@ -330,8 +334,8 @@ public class KurentoParticipant extends Participant {
|
||||||
+ "But there is no such subscriber endpoint.", this.getParticipantPublicId(), senderName);
|
+ "But there is no such subscriber endpoint.", this.getParticipantPublicId(), senderName);
|
||||||
} else {
|
} else {
|
||||||
releaseSubscriberEndpoint(senderName, subscriberEndpoint, reason);
|
releaseSubscriberEndpoint(senderName, subscriberEndpoint, reason);
|
||||||
log.info("PARTICIPANT {}: stopped receiving media from {} in room {}", this.getParticipantPublicId(), senderName,
|
log.info("PARTICIPANT {}: stopped receiving media from {} in room {}", this.getParticipantPublicId(),
|
||||||
this.session.getSessionId());
|
senderName, this.session.getSessionId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,17 +420,23 @@ public class KurentoParticipant extends Participant {
|
||||||
* id of another user
|
* id of another user
|
||||||
* @return the endpoint instance
|
* @return the endpoint instance
|
||||||
*/
|
*/
|
||||||
public SubscriberEndpoint getNewOrExistingSubscriber(String remotePublicId) {
|
public SubscriberEndpoint getNewOrExistingSubscriber(String senderPublicId) {
|
||||||
SubscriberEndpoint sendingEndpoint = new SubscriberEndpoint(webParticipant, this, remotePublicId, pipeline);
|
|
||||||
SubscriberEndpoint existingSendingEndpoint = this.subscribers.putIfAbsent(remotePublicId, sendingEndpoint);
|
KurentoParticipant kSender = (KurentoParticipant) this.session.getParticipantByPublicId(senderPublicId);
|
||||||
|
SubscriberEndpoint sendingEndpoint = new SubscriberEndpoint(webParticipant, this, senderPublicId, pipeline);
|
||||||
|
|
||||||
|
SubscriberEndpoint existingSendingEndpoint = this.subscribers.putIfAbsent(senderPublicId, sendingEndpoint);
|
||||||
if (existingSendingEndpoint != null) {
|
if (existingSendingEndpoint != null) {
|
||||||
sendingEndpoint = existingSendingEndpoint;
|
sendingEndpoint = existingSendingEndpoint;
|
||||||
log.trace("PARTICIPANT {}: Already exists a subscriber endpoint to user {}", this.getParticipantPublicId(),
|
log.trace("PARTICIPANT {}: Already exists a subscriber endpoint to user {}", this.getParticipantPublicId(),
|
||||||
remotePublicId);
|
senderPublicId);
|
||||||
} else {
|
} else {
|
||||||
log.debug("PARTICIPANT {}: New subscriber endpoint to user {}", this.getParticipantPublicId(),
|
log.debug("PARTICIPANT {}: New subscriber endpoint to user {}", this.getParticipantPublicId(),
|
||||||
remotePublicId);
|
senderPublicId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendingEndpoint.setMediaOptions(kSender.getPublisherMediaOptions());
|
||||||
|
|
||||||
return sendingEndpoint;
|
return sendingEndpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,9 +467,9 @@ public class KurentoParticipant extends Participant {
|
||||||
releaseElement(getParticipantPublicId(), publisher.getEndpoint());
|
releaseElement(getParticipantPublicId(), publisher.getEndpoint());
|
||||||
this.streaming = false;
|
this.streaming = false;
|
||||||
publisher = null;
|
publisher = null;
|
||||||
|
|
||||||
CDR.stopPublisher(this.getParticipantPublicId(), reason);
|
CDR.stopPublisher(this.getParticipantPublicId(), reason);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.warn("PARTICIPANT {}: Trying to release publisher endpoint but is null", getParticipantPublicId());
|
log.warn("PARTICIPANT {}: Trying to release publisher endpoint but is null", getParticipantPublicId());
|
||||||
}
|
}
|
||||||
|
@ -469,11 +479,11 @@ public class KurentoParticipant extends Participant {
|
||||||
if (subscriber != null) {
|
if (subscriber != null) {
|
||||||
subscriber.unregisterErrorListeners();
|
subscriber.unregisterErrorListeners();
|
||||||
releaseElement(senderName, subscriber.getEndpoint());
|
releaseElement(senderName, subscriber.getEndpoint());
|
||||||
|
|
||||||
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(this.getParticipantPublicId())) {
|
if (!ProtocolElements.RECORDER_PARTICIPANT_PUBLICID.equals(this.getParticipantPublicId())) {
|
||||||
CDR.stopSubscriber(this.getParticipantPublicId(), senderName, reason);
|
CDR.stopSubscriber(this.getParticipantPublicId(), senderName, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.warn("PARTICIPANT {}: Trying to release subscriber endpoint for '{}' but is null",
|
log.warn("PARTICIPANT {}: Trying to release subscriber endpoint for '{}' but is null",
|
||||||
this.getParticipantPublicId(), senderName);
|
this.getParticipantPublicId(), senderName);
|
||||||
|
@ -522,125 +532,158 @@ public class KurentoParticipant extends Participant {
|
||||||
* System.out.println(msg); this.infoHandler.sendInfo(msg); });
|
* System.out.println(msg); this.infoHandler.sendInfo(msg); });
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*endpoint.getWebEndpoint().addErrorListener((event) -> {
|
/*
|
||||||
String msg = " Error (PUBLISHER) -> " + "ERRORCODE: " + event.getErrorCode()
|
* endpoint.getWebEndpoint().addErrorListener((event) -> { String msg =
|
||||||
+ " | DESCRIPTION: " + event.getDescription() + " | TIMESTAMP: " + System.currentTimeMillis();
|
* " Error (PUBLISHER) -> " + "ERRORCODE: " +
|
||||||
log.debug(msg);
|
* event.getErrorCode() + " | DESCRIPTION: " + event.getDescription() +
|
||||||
this.infoHandler.sendInfo(msg);
|
* " | TIMESTAMP: " + System.currentTimeMillis(); log.debug(msg);
|
||||||
});
|
* this.infoHandler.sendInfo(msg); });
|
||||||
|
*
|
||||||
|
* endpoint.getWebEndpoint().addMediaFlowInStateChangeListener((event) -> {
|
||||||
|
* String msg1 = " Media flow in state change (" +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + ") -> " + "STATE: " +
|
||||||
|
* event.getState() + " | SOURCE: " + event.getSource().getName() + " | PAD: " +
|
||||||
|
* event.getPadName() + " | MEDIATYPE: " + event.getMediaType() +
|
||||||
|
* " | TIMESTAMP: " + System.currentTimeMillis();
|
||||||
|
*
|
||||||
|
* endpoint.flowInMedia.put(event.getSource().getName() + "/" +
|
||||||
|
* event.getMediaType(), event.getSource());
|
||||||
|
*
|
||||||
|
* String msg2;
|
||||||
|
*
|
||||||
|
* if (endpoint.flowInMedia.values().size() != 2) { msg2 =
|
||||||
|
* " THERE ARE LESS FLOW IN MEDIA'S THAN EXPECTED IN " +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + " (" +
|
||||||
|
* endpoint.flowInMedia.values().size() + ")"; } else { msg2 =
|
||||||
|
* " NUMBER OF FLOW IN MEDIA'S IS NOW CORRECT IN " +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + " (" +
|
||||||
|
* endpoint.flowInMedia.values().size() + ")"; }
|
||||||
|
*
|
||||||
|
* log.debug(msg1); log.debug(msg2); this.infoHandler.sendInfo(msg1);
|
||||||
|
* this.infoHandler.sendInfo(msg2); });
|
||||||
|
*
|
||||||
|
* endpoint.getWebEndpoint().addMediaFlowOutStateChangeListener((event) -> {
|
||||||
|
* String msg1 = " Media flow out state change (" +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + ") -> " + "STATE: " +
|
||||||
|
* event.getState() + " | SOURCE: " + event.getSource().getName() + " | PAD: " +
|
||||||
|
* event.getPadName() + " | MEDIATYPE: " + event.getMediaType() +
|
||||||
|
* " | TIMESTAMP: " + System.currentTimeMillis();
|
||||||
|
*
|
||||||
|
* endpoint.flowOutMedia.put(event.getSource().getName() + "/" +
|
||||||
|
* event.getMediaType(), event.getSource());
|
||||||
|
*
|
||||||
|
* String msg2;
|
||||||
|
*
|
||||||
|
* if (endpoint.flowOutMedia.values().size() != 2) { msg2 =
|
||||||
|
* " THERE ARE LESS FLOW OUT MEDIA'S THAN EXPECTED IN " +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + " (" +
|
||||||
|
* endpoint.flowOutMedia.values().size() + ")"; } else { msg2 =
|
||||||
|
* " NUMBER OF FLOW OUT MEDIA'S IS NOW CORRECT IN " +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + " (" +
|
||||||
|
* endpoint.flowOutMedia.values().size() + ")"; }
|
||||||
|
*
|
||||||
|
* log.debug(msg1); log.debug(msg2); this.infoHandler.sendInfo(msg1);
|
||||||
|
* this.infoHandler.sendInfo(msg2); });
|
||||||
|
*
|
||||||
|
* endpoint.getWebEndpoint().addMediaSessionStartedListener((event) -> { String
|
||||||
|
* msg = " Media session started (" +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + ") | TIMESTAMP: " +
|
||||||
|
* System.currentTimeMillis(); log.debug(msg); this.infoHandler.sendInfo(msg);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* endpoint.getWebEndpoint().addMediaSessionTerminatedListener((event) -> {
|
||||||
|
* String msg = " Media session terminated (" +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + ") | TIMESTAMP: " +
|
||||||
|
* System.currentTimeMillis(); log.debug(msg); this.infoHandler.sendInfo(msg);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* endpoint.getWebEndpoint().addMediaStateChangedListener((event) -> { String
|
||||||
|
* msg = " Media state changed (" +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + ") from " + event.getOldState() +
|
||||||
|
* " to " + event.getNewState(); log.debug(msg); this.infoHandler.sendInfo(msg);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* endpoint.getWebEndpoint().addConnectionStateChangedListener((event) -> {
|
||||||
|
* String msg = " Connection state changed (" +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + ") from " + event.getOldState() +
|
||||||
|
* " to " + event.getNewState() + " | TIMESTAMP: " + System.currentTimeMillis();
|
||||||
|
* log.debug(msg); this.infoHandler.sendInfo(msg); });
|
||||||
|
*
|
||||||
|
* endpoint.getWebEndpoint().addIceCandidateFoundListener((event) -> { String
|
||||||
|
* msg = " ICE CANDIDATE FOUND (" +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + "): CANDIDATE: " +
|
||||||
|
* event.getCandidate().getCandidate() + " | TIMESTAMP: " +
|
||||||
|
* System.currentTimeMillis(); log.debug(msg); this.infoHandler.sendInfo(msg);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* endpoint.getWebEndpoint().addIceComponentStateChangeListener((event) -> {
|
||||||
|
* String msg = " ICE COMPONENT STATE CHANGE (" +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + "): for component " +
|
||||||
|
* event.getComponentId() + " - STATE: " + event.getState() + " | TIMESTAMP: " +
|
||||||
|
* System.currentTimeMillis(); log.debug(msg); this.infoHandler.sendInfo(msg);
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* endpoint.getWebEndpoint().addIceGatheringDoneListener((event) -> { String msg
|
||||||
|
* = " ICE GATHERING DONE! (" +
|
||||||
|
* endpoint.getEndpoint().getTag("name") + ")" + " | TIMESTAMP: " +
|
||||||
|
* System.currentTimeMillis(); log.debug(msg); this.infoHandler.sendInfo(msg);
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
|
||||||
endpoint.getWebEndpoint().addMediaFlowInStateChangeListener((event) -> {
|
endpoint.getWebEndpoint().addMediaFlowInStateChangeListener(event -> {
|
||||||
String msg1 = " Media flow in state change (" + endpoint.getEndpoint().getTag("name")
|
String msg1 = "Media flow in state change (" + endpoint.getEndpoint().getTag("name") + ") -> " + "STATE: "
|
||||||
+ ") -> " + "STATE: " + event.getState() + " | SOURCE: " + event.getSource().getName() + " | PAD: "
|
+ event.getState() + " | SOURCE: " + event.getSource().getName() + " | PAD: " + event.getPadName()
|
||||||
+ event.getPadName() + " | MEDIATYPE: " + event.getMediaType() + " | TIMESTAMP: "
|
+ " | MEDIATYPE: " + event.getMediaType() + " | TIMESTAMP: " + System.currentTimeMillis();
|
||||||
+ System.currentTimeMillis();
|
|
||||||
|
|
||||||
endpoint.flowInMedia.put(event.getSource().getName() + "/" + event.getMediaType(), event.getSource());
|
endpoint.flowInMedia.put(event.getSource().getName(), event.getMediaType());
|
||||||
|
if (endpoint.getMediaOptions().audioActive && endpoint.getMediaOptions().videoActive
|
||||||
String msg2;
|
&& endpoint.flowInMedia.values().size() == 2) {
|
||||||
|
endpoint.kmsEvents.add(new KmsEvent(event));
|
||||||
if (endpoint.flowInMedia.values().size() != 2) {
|
} else if (endpoint.flowInMedia.values().size() == 1) {
|
||||||
msg2 = " THERE ARE LESS FLOW IN MEDIA'S THAN EXPECTED IN "
|
endpoint.kmsEvents.add(new KmsEvent(event));
|
||||||
+ endpoint.getEndpoint().getTag("name") + " (" + endpoint.flowInMedia.values().size() + ")";
|
|
||||||
} else {
|
|
||||||
msg2 = " NUMBER OF FLOW IN MEDIA'S IS NOW CORRECT IN "
|
|
||||||
+ endpoint.getEndpoint().getTag("name") + " (" + endpoint.flowInMedia.values().size() + ")";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug(msg1);
|
log.info(msg1);
|
||||||
log.debug(msg2);
|
|
||||||
this.infoHandler.sendInfo(msg1);
|
this.infoHandler.sendInfo(msg1);
|
||||||
this.infoHandler.sendInfo(msg2);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
endpoint.getWebEndpoint().addMediaFlowOutStateChangeListener((event) -> {
|
endpoint.getWebEndpoint().addMediaFlowOutStateChangeListener(event -> {
|
||||||
String msg1 = " Media flow out state change (" + endpoint.getEndpoint().getTag("name")
|
String msg1 = "Media flow out state change (" + endpoint.getEndpoint().getTag("name") + ") -> " + "STATE: "
|
||||||
+ ") -> " + "STATE: " + event.getState() + " | SOURCE: " + event.getSource().getName() + " | PAD: "
|
+ event.getState() + " | SOURCE: " + event.getSource().getName() + " | PAD: " + event.getPadName()
|
||||||
+ event.getPadName() + " | MEDIATYPE: " + event.getMediaType() + " | TIMESTAMP: "
|
+ " | MEDIATYPE: " + event.getMediaType() + " | TIMESTAMP: " + System.currentTimeMillis();
|
||||||
+ System.currentTimeMillis();
|
|
||||||
|
|
||||||
endpoint.flowOutMedia.put(event.getSource().getName() + "/" + event.getMediaType(), event.getSource());
|
endpoint.flowOutMedia.put(event.getSource().getName(), event.getMediaType());
|
||||||
|
if (endpoint.getMediaOptions().audioActive && endpoint.getMediaOptions().videoActive
|
||||||
String msg2;
|
&& endpoint.flowOutMedia.values().size() == 2) {
|
||||||
|
endpoint.kmsEvents.add(new KmsEvent(event));
|
||||||
if (endpoint.flowOutMedia.values().size() != 2) {
|
} else if (endpoint.flowOutMedia.values().size() == 1) {
|
||||||
msg2 = " THERE ARE LESS FLOW OUT MEDIA'S THAN EXPECTED IN "
|
endpoint.kmsEvents.add(new KmsEvent(event));
|
||||||
+ endpoint.getEndpoint().getTag("name") + " (" + endpoint.flowOutMedia.values().size() + ")";
|
|
||||||
} else {
|
|
||||||
msg2 = " NUMBER OF FLOW OUT MEDIA'S IS NOW CORRECT IN "
|
|
||||||
+ endpoint.getEndpoint().getTag("name") + " (" + endpoint.flowOutMedia.values().size() + ")";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug(msg1);
|
log.info(msg1);
|
||||||
log.debug(msg2);
|
|
||||||
this.infoHandler.sendInfo(msg1);
|
this.infoHandler.sendInfo(msg1);
|
||||||
this.infoHandler.sendInfo(msg2);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
endpoint.getWebEndpoint().addMediaSessionStartedListener((event) -> {
|
endpoint.getWebEndpoint().addIceGatheringDoneListener(event -> {
|
||||||
String msg = " Media session started (" + endpoint.getEndpoint().getTag("name")
|
endpoint.kmsEvents.add(new KmsEvent(event));
|
||||||
+ ") | TIMESTAMP: " + System.currentTimeMillis();
|
|
||||||
log.debug(msg);
|
|
||||||
this.infoHandler.sendInfo(msg);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
endpoint.getWebEndpoint().addMediaSessionTerminatedListener((event) -> {
|
endpoint.getWebEndpoint().addConnectionStateChangedListener(event -> {
|
||||||
String msg = " Media session terminated (" + endpoint.getEndpoint().getTag("name")
|
endpoint.kmsEvents.add(new KmsEvent(event));
|
||||||
+ ") | TIMESTAMP: " + System.currentTimeMillis();
|
|
||||||
log.debug(msg);
|
|
||||||
this.infoHandler.sendInfo(msg);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
endpoint.getWebEndpoint().addMediaStateChangedListener((event) -> {
|
endpoint.getWebEndpoint().addNewCandidatePairSelectedListener(event -> {
|
||||||
String msg = " Media state changed (" + endpoint.getEndpoint().getTag("name") + ") from "
|
|
||||||
+ event.getOldState() + " to " + event.getNewState();
|
|
||||||
log.debug(msg);
|
|
||||||
this.infoHandler.sendInfo(msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
endpoint.getWebEndpoint().addConnectionStateChangedListener((event) -> {
|
|
||||||
String msg = " Connection state changed (" + endpoint.getEndpoint().getTag("name")
|
|
||||||
+ ") from " + event.getOldState() + " to " + event.getNewState() + " | TIMESTAMP: "
|
|
||||||
+ System.currentTimeMillis();
|
|
||||||
log.debug(msg);
|
|
||||||
this.infoHandler.sendInfo(msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
endpoint.getWebEndpoint().addIceCandidateFoundListener((event) -> {
|
|
||||||
String msg = " ICE CANDIDATE FOUND (" + endpoint.getEndpoint().getTag("name")
|
|
||||||
+ "): CANDIDATE: " + event.getCandidate().getCandidate() + " | TIMESTAMP: "
|
|
||||||
+ System.currentTimeMillis();
|
|
||||||
log.debug(msg);
|
|
||||||
this.infoHandler.sendInfo(msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
endpoint.getWebEndpoint().addIceComponentStateChangeListener((event) -> {
|
|
||||||
String msg = " ICE COMPONENT STATE CHANGE (" + endpoint.getEndpoint().getTag("name")
|
|
||||||
+ "): for component " + event.getComponentId() + " - STATE: " + event.getState() + " | TIMESTAMP: "
|
|
||||||
+ System.currentTimeMillis();
|
|
||||||
log.debug(msg);
|
|
||||||
this.infoHandler.sendInfo(msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
endpoint.getWebEndpoint().addIceGatheringDoneListener((event) -> {
|
|
||||||
String msg = " ICE GATHERING DONE! (" + endpoint.getEndpoint().getTag("name") + ")"
|
|
||||||
+ " | TIMESTAMP: " + System.currentTimeMillis();
|
|
||||||
log.debug(msg);
|
|
||||||
this.infoHandler.sendInfo(msg);
|
|
||||||
});*/
|
|
||||||
|
|
||||||
endpoint.getWebEndpoint().addNewCandidatePairSelectedListener((event) -> {
|
|
||||||
endpoint.selectedLocalIceCandidate = event.getCandidatePair().getLocalCandidate();
|
endpoint.selectedLocalIceCandidate = event.getCandidatePair().getLocalCandidate();
|
||||||
endpoint.selectedRemoteIceCandidate = event.getCandidatePair().getRemoteCandidate();
|
endpoint.selectedRemoteIceCandidate = event.getCandidatePair().getRemoteCandidate();
|
||||||
String msg = "ICE CANDIDATE SELECTED (" + endpoint.getEndpoint().getTag("name")
|
endpoint.kmsEvents.add(new KmsEvent(event));
|
||||||
+ "): LOCAL CANDIDATE: " + endpoint.selectedLocalIceCandidate +
|
String msg = "ICE CANDIDATE SELECTED (" + endpoint.getEndpoint().getTag("name") + "): LOCAL CANDIDATE: "
|
||||||
" | REMOTE CANDIDATE: " + endpoint.selectedRemoteIceCandidate +
|
+ endpoint.selectedLocalIceCandidate + " | REMOTE CANDIDATE: " + endpoint.selectedRemoteIceCandidate
|
||||||
" | TIMESTAMP: " + System.currentTimeMillis();
|
+ " | TIMESTAMP: " + System.currentTimeMillis();
|
||||||
log.warn(msg);
|
log.warn(msg);
|
||||||
this.infoHandler.sendInfo(msg);
|
this.infoHandler.sendInfo(msg);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public JSONObject toJSON() {
|
public JSONObject toJSON() {
|
||||||
|
|
|
@ -268,11 +268,6 @@ public class KurentoSessionManager extends SessionManager {
|
||||||
|
|
||||||
session.newPublisher(participant);
|
session.newPublisher(participant);
|
||||||
|
|
||||||
kurentoParticipant.setAudioActive(kurentoOptions.audioActive);
|
|
||||||
kurentoParticipant.setVideoActive(kurentoOptions.videoActive);
|
|
||||||
kurentoParticipant.setTypeOfVideo(kurentoOptions.typeOfVideo);
|
|
||||||
kurentoParticipant.setFrameRate(kurentoOptions.frameRate);
|
|
||||||
|
|
||||||
participants = kurentoParticipant.getSession().getParticipants();
|
participants = kurentoParticipant.getSession().getParticipants();
|
||||||
|
|
||||||
if (sdpAnswer != null) {
|
if (sdpAnswer != null) {
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package io.openvidu.server.kurento.endpoint;
|
||||||
|
|
||||||
|
import org.kurento.client.MediaEvent;
|
||||||
|
|
||||||
|
public class KmsEvent {
|
||||||
|
|
||||||
|
long timestamp;
|
||||||
|
MediaEvent event;
|
||||||
|
|
||||||
|
public KmsEvent(MediaEvent event) {
|
||||||
|
this.event = event;
|
||||||
|
this.timestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,9 +19,12 @@ package io.openvidu.server.kurento.endpoint;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
import org.json.simple.JSONArray;
|
||||||
import org.json.simple.JSONObject;
|
import org.json.simple.JSONObject;
|
||||||
import org.kurento.client.Continuation;
|
import org.kurento.client.Continuation;
|
||||||
import org.kurento.client.ErrorEvent;
|
import org.kurento.client.ErrorEvent;
|
||||||
|
@ -29,8 +32,8 @@ import org.kurento.client.EventListener;
|
||||||
import org.kurento.client.IceCandidate;
|
import org.kurento.client.IceCandidate;
|
||||||
import org.kurento.client.ListenerSubscription;
|
import org.kurento.client.ListenerSubscription;
|
||||||
import org.kurento.client.MediaElement;
|
import org.kurento.client.MediaElement;
|
||||||
import org.kurento.client.MediaObject;
|
|
||||||
import org.kurento.client.MediaPipeline;
|
import org.kurento.client.MediaPipeline;
|
||||||
|
import org.kurento.client.MediaType;
|
||||||
import org.kurento.client.OnIceCandidateEvent;
|
import org.kurento.client.OnIceCandidateEvent;
|
||||||
import org.kurento.client.RtpEndpoint;
|
import org.kurento.client.RtpEndpoint;
|
||||||
import org.kurento.client.SdpEndpoint;
|
import org.kurento.client.SdpEndpoint;
|
||||||
|
@ -40,6 +43,7 @@ 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.server.core.MediaOptions;
|
||||||
import io.openvidu.server.core.Participant;
|
import io.openvidu.server.core.Participant;
|
||||||
import io.openvidu.server.kurento.MutedMediaType;
|
import io.openvidu.server.kurento.MutedMediaType;
|
||||||
import io.openvidu.server.kurento.core.KurentoParticipant;
|
import io.openvidu.server.kurento.core.KurentoParticipant;
|
||||||
|
@ -70,11 +74,13 @@ public abstract class MediaEndpoint {
|
||||||
|
|
||||||
private MutedMediaType muteType;
|
private MutedMediaType muteType;
|
||||||
|
|
||||||
public Map<String, MediaObject> flowInMedia = new ConcurrentHashMap<>();
|
private MediaOptions mediaOptions;
|
||||||
public Map<String, MediaObject> flowOutMedia = new ConcurrentHashMap<>();
|
public Map<String, MediaType> flowInMedia = new ConcurrentHashMap<>();
|
||||||
|
public Map<String, MediaType> flowOutMedia = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public String selectedLocalIceCandidate;
|
public String selectedLocalIceCandidate;
|
||||||
public String selectedRemoteIceCandidate;
|
public String selectedRemoteIceCandidate;
|
||||||
|
public Queue<KmsEvent> kmsEvents = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor to set the owner, the endpoint's name and the media pipeline.
|
* Constructor to set the owner, the endpoint's name and the media pipeline.
|
||||||
|
@ -98,6 +104,14 @@ public abstract class MediaEndpoint {
|
||||||
this.setMediaPipeline(pipeline);
|
this.setMediaPipeline(pipeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MediaOptions getMediaOptions() {
|
||||||
|
return mediaOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMediaOptions(MediaOptions mediaOptions) {
|
||||||
|
this.mediaOptions = mediaOptions;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isWeb() {
|
public boolean isWeb() {
|
||||||
return web;
|
return web;
|
||||||
}
|
}
|
||||||
|
@ -495,6 +509,16 @@ public abstract class MediaEndpoint {
|
||||||
json.put("webrtcTagName", this.getEndpoint().getTag("name"));
|
json.put("webrtcTagName", this.getEndpoint().getTag("name"));
|
||||||
json.put("localCandidate", this.selectedLocalIceCandidate);
|
json.put("localCandidate", this.selectedLocalIceCandidate);
|
||||||
json.put("remoteCandidate", this.selectedRemoteIceCandidate);
|
json.put("remoteCandidate", this.selectedRemoteIceCandidate);
|
||||||
|
|
||||||
|
JSONArray jsonArray = new JSONArray();
|
||||||
|
|
||||||
|
for (KmsEvent event : this.kmsEvents) {
|
||||||
|
JSONObject jsonKmsEvent = new JSONObject();
|
||||||
|
jsonKmsEvent.put(event.event.getType(), event.timestamp);
|
||||||
|
jsonArray.add(jsonKmsEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
json.put("events", jsonArray);
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,410 +44,422 @@ import io.openvidu.server.kurento.core.KurentoParticipant;
|
||||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||||
*/
|
*/
|
||||||
public class PublisherEndpoint extends MediaEndpoint {
|
public class PublisherEndpoint extends MediaEndpoint {
|
||||||
private final static Logger log = LoggerFactory.getLogger(PublisherEndpoint.class);
|
private final static Logger log = LoggerFactory.getLogger(PublisherEndpoint.class);
|
||||||
|
|
||||||
private PassThrough passThru = null;
|
private PassThrough passThru = null;
|
||||||
private ListenerSubscription passThruSubscription = null;
|
private ListenerSubscription passThruSubscription = null;
|
||||||
|
|
||||||
private Map<String, MediaElement> elements = new HashMap<String, MediaElement>();
|
private Map<String, MediaElement> elements = new HashMap<String, MediaElement>();
|
||||||
private LinkedList<String> elementIds = new LinkedList<String>();
|
private LinkedList<String> elementIds = new LinkedList<String>();
|
||||||
private boolean connected = false;
|
private boolean connected = false;
|
||||||
|
|
||||||
private Map<String, ListenerSubscription> elementsErrorSubscriptions =
|
private Map<String, ListenerSubscription> elementsErrorSubscriptions = new HashMap<String, ListenerSubscription>();
|
||||||
new HashMap<String, ListenerSubscription>();
|
|
||||||
|
|
||||||
public PublisherEndpoint(boolean web, KurentoParticipant owner,
|
public PublisherEndpoint(boolean web, KurentoParticipant owner, String endpointName, MediaPipeline pipeline) {
|
||||||
String endpointName, MediaPipeline pipeline) {
|
super(web, owner, endpointName, pipeline, log);
|
||||||
super(web, owner, endpointName, pipeline, log);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void internalEndpointInitialization(final CountDownLatch endpointLatch) {
|
protected void internalEndpointInitialization(final CountDownLatch endpointLatch) {
|
||||||
super.internalEndpointInitialization(endpointLatch);
|
super.internalEndpointInitialization(endpointLatch);
|
||||||
passThru = new PassThrough.Builder(getPipeline()).build();
|
passThru = new PassThrough.Builder(getPipeline()).build();
|
||||||
passThruSubscription = registerElemErrListener(passThru);
|
passThruSubscription = registerElemErrListener(passThru);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void unregisterErrorListeners() {
|
public synchronized void unregisterErrorListeners() {
|
||||||
super.unregisterErrorListeners();
|
super.unregisterErrorListeners();
|
||||||
unregisterElementErrListener(passThru, passThruSubscription);
|
unregisterElementErrListener(passThru, passThruSubscription);
|
||||||
for (String elemId : elementIds) {
|
for (String elemId : elementIds) {
|
||||||
unregisterElementErrListener(elements.get(elemId), elementsErrorSubscriptions.remove(elemId));
|
unregisterElementErrListener(elements.get(elemId), elementsErrorSubscriptions.remove(elemId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return all media elements created for this publisher, except for the main element (
|
* @return all media elements created for this publisher, except for the main
|
||||||
* {@link WebRtcEndpoint})
|
* element ( {@link WebRtcEndpoint})
|
||||||
*/
|
*/
|
||||||
public synchronized Collection<MediaElement> getMediaElements() {
|
public synchronized Collection<MediaElement> getMediaElements() {
|
||||||
if (passThru != null) {
|
if (passThru != null) {
|
||||||
elements.put(passThru.getId(), passThru);
|
elements.put(passThru.getId(), passThru);
|
||||||
}
|
}
|
||||||
return elements.values();
|
return elements.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes this media endpoint for publishing media and processes the SDP offer or answer. If
|
* Initializes this media endpoint for publishing media and processes the SDP
|
||||||
* the internal endpoint is an {@link WebRtcEndpoint}, it first registers an event listener for
|
* offer or answer. If the internal endpoint is an {@link WebRtcEndpoint}, it
|
||||||
* the ICE candidates and instructs the endpoint to start gathering the candidates. If required,
|
* first registers an event listener for the ICE candidates and instructs the
|
||||||
* it connects to itself (after applying the intermediate media elements and the
|
* endpoint to start gathering the candidates. If required, it connects to
|
||||||
* {@link PassThrough}) to allow loopback of the media stream.
|
* itself (after applying the intermediate media elements and the
|
||||||
*
|
* {@link PassThrough}) to allow loopback of the media stream.
|
||||||
* @param sdpType indicates the type of the sdpString (offer or answer)
|
*
|
||||||
* @param sdpString offer or answer from the remote peer
|
* @param sdpType
|
||||||
* @param doLoopback loopback flag
|
* indicates the type of the sdpString (offer or answer)
|
||||||
* @param loopbackAlternativeSrc alternative loopback source
|
* @param sdpString
|
||||||
* @param loopbackConnectionType how to connect the loopback source
|
* offer or answer from the remote peer
|
||||||
* @return the SDP response (the answer if processing an offer SDP, otherwise is the updated offer
|
* @param doLoopback
|
||||||
* generated previously by this endpoint)
|
* loopback flag
|
||||||
*/
|
* @param loopbackAlternativeSrc
|
||||||
public synchronized String publish(SdpType sdpType, String sdpString, boolean doLoopback,
|
* alternative loopback source
|
||||||
MediaElement loopbackAlternativeSrc, MediaType loopbackConnectionType) {
|
* @param loopbackConnectionType
|
||||||
registerOnIceCandidateEventListener();
|
* how to connect the loopback source
|
||||||
if (doLoopback) {
|
* @return the SDP response (the answer if processing an offer SDP, otherwise is
|
||||||
if (loopbackAlternativeSrc == null) {
|
* the updated offer generated previously by this endpoint)
|
||||||
connect(this.getEndpoint(), loopbackConnectionType);
|
*/
|
||||||
} else {
|
public synchronized String publish(SdpType sdpType, String sdpString, boolean doLoopback,
|
||||||
connectAltLoopbackSrc(loopbackAlternativeSrc, loopbackConnectionType);
|
MediaElement loopbackAlternativeSrc, MediaType loopbackConnectionType) {
|
||||||
}
|
registerOnIceCandidateEventListener();
|
||||||
} else {
|
if (doLoopback) {
|
||||||
innerConnect();
|
if (loopbackAlternativeSrc == null) {
|
||||||
}
|
connect(this.getEndpoint(), loopbackConnectionType);
|
||||||
String sdpResponse = null;
|
} else {
|
||||||
switch (sdpType) {
|
connectAltLoopbackSrc(loopbackAlternativeSrc, loopbackConnectionType);
|
||||||
case ANSWER:
|
}
|
||||||
sdpResponse = processAnswer(sdpString);
|
} else {
|
||||||
break;
|
innerConnect();
|
||||||
case OFFER:
|
}
|
||||||
sdpResponse = processOffer(sdpString);
|
String sdpResponse = null;
|
||||||
break;
|
switch (sdpType) {
|
||||||
default:
|
case ANSWER:
|
||||||
throw new OpenViduException(Code.MEDIA_SDP_ERROR_CODE, "Sdp type not supported: " + sdpType);
|
sdpResponse = processAnswer(sdpString);
|
||||||
}
|
break;
|
||||||
gatherCandidates();
|
case OFFER:
|
||||||
return sdpResponse;
|
sdpResponse = processOffer(sdpString);
|
||||||
}
|
break;
|
||||||
|
default:
|
||||||
|
throw new OpenViduException(Code.MEDIA_SDP_ERROR_CODE, "Sdp type not supported: " + sdpType);
|
||||||
|
}
|
||||||
|
gatherCandidates();
|
||||||
|
return sdpResponse;
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized String preparePublishConnection() {
|
public synchronized String preparePublishConnection() {
|
||||||
return generateOffer();
|
return generateOffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void connect(MediaElement sink) {
|
public synchronized void connect(MediaElement sink) {
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
innerConnect();
|
innerConnect();
|
||||||
}
|
}
|
||||||
internalSinkConnect(passThru, sink);
|
internalSinkConnect(passThru, sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void connect(MediaElement sink, MediaType type) {
|
public synchronized void connect(MediaElement sink, MediaType type) {
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
innerConnect();
|
innerConnect();
|
||||||
}
|
}
|
||||||
internalSinkConnect(passThru, sink, type);
|
internalSinkConnect(passThru, sink, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void disconnectFrom(MediaElement sink) {
|
public synchronized void disconnectFrom(MediaElement sink) {
|
||||||
internalSinkDisconnect(passThru, sink);
|
internalSinkDisconnect(passThru, sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void disconnectFrom(MediaElement sink, MediaType type) {
|
public synchronized void disconnectFrom(MediaElement sink, MediaType type) {
|
||||||
internalSinkDisconnect(passThru, sink, type);
|
internalSinkDisconnect(passThru, sink, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the media passing through a chain of media elements by applying the specified
|
* Changes the media passing through a chain of media elements by applying the
|
||||||
* element/shaper. The element is plugged into the stream only if the chain has been initialized
|
* specified element/shaper. The element is plugged into the stream only if the
|
||||||
* (a.k.a. media streaming has started), otherwise it is left ready for when the connections
|
* chain has been initialized (a.k.a. media streaming has started), otherwise it
|
||||||
* between elements will materialize and the streaming begins.
|
* is left ready for when the connections between elements will materialize and
|
||||||
*
|
* the streaming begins.
|
||||||
* @param shaper {@link MediaElement} that will be linked to the end of the chain (e.g. a filter)
|
*
|
||||||
* @return the element's id
|
* @param shaper
|
||||||
* @throws OpenViduException if thrown, the media element was not added
|
* {@link MediaElement} that will be linked to the end of the chain
|
||||||
*/
|
* (e.g. a filter)
|
||||||
public String apply(MediaElement shaper) throws OpenViduException {
|
* @return the element's id
|
||||||
return apply(shaper, null);
|
* @throws OpenViduException
|
||||||
}
|
* if thrown, the media element was not added
|
||||||
|
*/
|
||||||
|
public String apply(MediaElement shaper) throws OpenViduException {
|
||||||
|
return apply(shaper, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link #apply(MediaElement)}, can specify the media type that will be streamed through
|
* Same as {@link #apply(MediaElement)}, can specify the media type that will be
|
||||||
* the shaper element.
|
* streamed through the shaper element.
|
||||||
*
|
*
|
||||||
* @param shaper {@link MediaElement} that will be linked to the end of the chain (e.g. a filter)
|
* @param shaper
|
||||||
* @param type indicates which type of media will be connected to the shaper
|
* {@link MediaElement} that will be linked to the end of the chain
|
||||||
* ({@link MediaType}), if
|
* (e.g. a filter)
|
||||||
* null then the connection is mixed
|
* @param type
|
||||||
* @return the element's id
|
* indicates which type of media will be connected to the shaper
|
||||||
* @throws OpenViduException if thrown, the media element was not added
|
* ({@link MediaType}), if null then the connection is mixed
|
||||||
*/
|
* @return the element's id
|
||||||
public synchronized String apply(MediaElement shaper, MediaType type) throws OpenViduException {
|
* @throws OpenViduException
|
||||||
String id = shaper.getId();
|
* if thrown, the media element was not added
|
||||||
if (id == null) {
|
*/
|
||||||
throw new OpenViduException(Code.MEDIA_WEBRTC_ENDPOINT_ERROR_CODE,
|
public synchronized String apply(MediaElement shaper, MediaType type) throws OpenViduException {
|
||||||
"Unable to connect media element with null id");
|
String id = shaper.getId();
|
||||||
}
|
if (id == null) {
|
||||||
if (elements.containsKey(id)) {
|
throw new OpenViduException(Code.MEDIA_WEBRTC_ENDPOINT_ERROR_CODE,
|
||||||
throw new OpenViduException(Code.MEDIA_WEBRTC_ENDPOINT_ERROR_CODE,
|
"Unable to connect media element with null id");
|
||||||
"This endpoint already has a media element with id " + id);
|
}
|
||||||
}
|
if (elements.containsKey(id)) {
|
||||||
MediaElement first = null;
|
throw new OpenViduException(Code.MEDIA_WEBRTC_ENDPOINT_ERROR_CODE,
|
||||||
if (!elementIds.isEmpty()) {
|
"This endpoint already has a media element with id " + id);
|
||||||
first = elements.get(elementIds.getFirst());
|
}
|
||||||
}
|
MediaElement first = null;
|
||||||
if (connected) {
|
if (!elementIds.isEmpty()) {
|
||||||
if (first != null) {
|
first = elements.get(elementIds.getFirst());
|
||||||
internalSinkConnect(first, shaper, type);
|
}
|
||||||
} else {
|
if (connected) {
|
||||||
internalSinkConnect(this.getEndpoint(), shaper, type);
|
if (first != null) {
|
||||||
}
|
internalSinkConnect(first, shaper, type);
|
||||||
internalSinkConnect(shaper, passThru, type);
|
} else {
|
||||||
}
|
internalSinkConnect(this.getEndpoint(), shaper, type);
|
||||||
elementIds.addFirst(id);
|
}
|
||||||
elements.put(id, shaper);
|
internalSinkConnect(shaper, passThru, type);
|
||||||
elementsErrorSubscriptions.put(id, registerElemErrListener(shaper));
|
}
|
||||||
return id;
|
elementIds.addFirst(id);
|
||||||
}
|
elements.put(id, shaper);
|
||||||
|
elementsErrorSubscriptions.put(id, registerElemErrListener(shaper));
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the media element object found from the media chain structure. The object is released.
|
* Removes the media element object found from the media chain structure. The
|
||||||
* If the chain is connected, both adjacent remaining elements will be interconnected.
|
* object is released. If the chain is connected, both adjacent remaining
|
||||||
*
|
* elements will be interconnected.
|
||||||
* @param shaper {@link MediaElement} that will be removed from the chain
|
*
|
||||||
* @throws OpenViduException if thrown, the media element was not removed
|
* @param shaper
|
||||||
*/
|
* {@link MediaElement} that will be removed from the chain
|
||||||
public synchronized void revert(MediaElement shaper) throws OpenViduException {
|
* @throws OpenViduException
|
||||||
revert (shaper, true);
|
* if thrown, the media element was not removed
|
||||||
}
|
*/
|
||||||
|
public synchronized void revert(MediaElement shaper) throws OpenViduException {
|
||||||
|
revert(shaper, true);
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void revert(MediaElement shaper, boolean releaseElement) throws
|
public synchronized void revert(MediaElement shaper, boolean releaseElement) throws OpenViduException {
|
||||||
OpenViduException {
|
final String elementId = shaper.getId();
|
||||||
final String elementId = shaper.getId();
|
if (!elements.containsKey(elementId)) {
|
||||||
if (!elements.containsKey(elementId)) {
|
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE,
|
||||||
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE,
|
"This endpoint (" + getEndpointName() + ") has no media element with id " + elementId);
|
||||||
"This endpoint (" + getEndpointName() + ") has no media element with id " + elementId);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
MediaElement element = elements.remove(elementId);
|
MediaElement element = elements.remove(elementId);
|
||||||
unregisterElementErrListener(element, elementsErrorSubscriptions.remove(elementId));
|
unregisterElementErrListener(element, elementsErrorSubscriptions.remove(elementId));
|
||||||
|
|
||||||
// careful, the order in the elems list is reverted
|
// careful, the order in the elems list is reverted
|
||||||
if (connected) {
|
if (connected) {
|
||||||
String nextId = getNext(elementId);
|
String nextId = getNext(elementId);
|
||||||
String prevId = getPrevious(elementId);
|
String prevId = getPrevious(elementId);
|
||||||
// next connects to prev
|
// next connects to prev
|
||||||
MediaElement prev = null;
|
MediaElement prev = null;
|
||||||
MediaElement next = null;
|
MediaElement next = null;
|
||||||
if (nextId != null) {
|
if (nextId != null) {
|
||||||
next = elements.get(nextId);
|
next = elements.get(nextId);
|
||||||
} else {
|
} else {
|
||||||
next = this.getEndpoint();
|
next = this.getEndpoint();
|
||||||
}
|
}
|
||||||
if (prevId != null) {
|
if (prevId != null) {
|
||||||
prev = elements.get(prevId);
|
prev = elements.get(prevId);
|
||||||
} else {
|
} else {
|
||||||
prev = passThru;
|
prev = passThru;
|
||||||
}
|
}
|
||||||
internalSinkConnect(next, prev);
|
internalSinkConnect(next, prev);
|
||||||
}
|
}
|
||||||
elementIds.remove(elementId);
|
elementIds.remove(elementId);
|
||||||
if (releaseElement) {
|
if (releaseElement) {
|
||||||
element.release(new Continuation<Void>() {
|
element.release(new Continuation<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Void result) throws Exception {
|
public void onSuccess(Void result) throws Exception {
|
||||||
log.trace("EP {}: Released media element {}", getEndpointName(), elementId);
|
log.trace("EP {}: Released media element {}", getEndpointName(), elementId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable cause) throws Exception {
|
public void onError(Throwable cause) throws Exception {
|
||||||
log.error("EP {}: Failed to release media element {}", getEndpointName(), elementId, cause);
|
log.error("EP {}: Failed to release media element {}", getEndpointName(), elementId, cause);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void mute(MutedMediaType muteType) {
|
public synchronized void mute(MutedMediaType muteType) {
|
||||||
MediaElement sink = passThru;
|
MediaElement sink = passThru;
|
||||||
if (!elements.isEmpty()) {
|
if (!elements.isEmpty()) {
|
||||||
String sinkId = elementIds.peekLast();
|
String sinkId = elementIds.peekLast();
|
||||||
if (!elements.containsKey(sinkId)) {
|
if (!elements.containsKey(sinkId)) {
|
||||||
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE,
|
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE,
|
||||||
"This endpoint (" + getEndpointName() + ") has no media element with id " + sinkId
|
"This endpoint (" + getEndpointName() + ") has no media element with id " + sinkId
|
||||||
+ " (should've been connected to the internal ep)");
|
+ " (should've been connected to the internal ep)");
|
||||||
}
|
}
|
||||||
sink = elements.get(sinkId);
|
sink = elements.get(sinkId);
|
||||||
} else {
|
} else {
|
||||||
log.debug("Will mute connection of WebRTC and PassThrough (no other elems)");
|
log.debug("Will mute connection of WebRTC and PassThrough (no other elems)");
|
||||||
}
|
}
|
||||||
switch (muteType) {
|
switch (muteType) {
|
||||||
case ALL:
|
case ALL:
|
||||||
internalSinkDisconnect(this.getEndpoint(), sink);
|
internalSinkDisconnect(this.getEndpoint(), sink);
|
||||||
break;
|
break;
|
||||||
case AUDIO:
|
case AUDIO:
|
||||||
internalSinkDisconnect(this.getEndpoint(), sink, MediaType.AUDIO);
|
internalSinkDisconnect(this.getEndpoint(), sink, MediaType.AUDIO);
|
||||||
break;
|
break;
|
||||||
case VIDEO:
|
case VIDEO:
|
||||||
internalSinkDisconnect(this.getEndpoint(), sink, MediaType.VIDEO);
|
internalSinkDisconnect(this.getEndpoint(), sink, MediaType.VIDEO);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
resolveCurrentMuteType(muteType);
|
resolveCurrentMuteType(muteType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void unmute() {
|
public synchronized void unmute() {
|
||||||
MediaElement sink = passThru;
|
MediaElement sink = passThru;
|
||||||
if (!elements.isEmpty()) {
|
if (!elements.isEmpty()) {
|
||||||
String sinkId = elementIds.peekLast();
|
String sinkId = elementIds.peekLast();
|
||||||
if (!elements.containsKey(sinkId)) {
|
if (!elements.containsKey(sinkId)) {
|
||||||
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE,
|
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE,
|
||||||
"This endpoint (" + getEndpointName() + ") has no media element with id " + sinkId
|
"This endpoint (" + getEndpointName() + ") has no media element with id " + sinkId
|
||||||
+ " (should've been connected to the internal ep)");
|
+ " (should've been connected to the internal ep)");
|
||||||
}
|
}
|
||||||
sink = elements.get(sinkId);
|
sink = elements.get(sinkId);
|
||||||
} else {
|
} else {
|
||||||
log.debug("Will unmute connection of WebRTC and PassThrough (no other elems)");
|
log.debug("Will unmute connection of WebRTC and PassThrough (no other elems)");
|
||||||
}
|
}
|
||||||
internalSinkConnect(this.getEndpoint(), sink);
|
internalSinkConnect(this.getEndpoint(), sink);
|
||||||
setMuteType(null);
|
setMuteType(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getNext(String uid) {
|
private String getNext(String uid) {
|
||||||
int idx = elementIds.indexOf(uid);
|
int idx = elementIds.indexOf(uid);
|
||||||
if (idx < 0 || idx + 1 == elementIds.size()) {
|
if (idx < 0 || idx + 1 == elementIds.size()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return elementIds.get(idx + 1);
|
return elementIds.get(idx + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPrevious(String uid) {
|
private String getPrevious(String uid) {
|
||||||
int idx = elementIds.indexOf(uid);
|
int idx = elementIds.indexOf(uid);
|
||||||
if (idx <= 0) {
|
if (idx <= 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return elementIds.get(idx - 1);
|
return elementIds.get(idx - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connectAltLoopbackSrc(MediaElement loopbackAlternativeSrc,
|
private void connectAltLoopbackSrc(MediaElement loopbackAlternativeSrc, MediaType loopbackConnectionType) {
|
||||||
MediaType loopbackConnectionType) {
|
if (!connected) {
|
||||||
if (!connected) {
|
innerConnect();
|
||||||
innerConnect();
|
}
|
||||||
}
|
internalSinkConnect(loopbackAlternativeSrc, this.getEndpoint(), loopbackConnectionType);
|
||||||
internalSinkConnect(loopbackAlternativeSrc, this.getEndpoint(), loopbackConnectionType);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void innerConnect() {
|
private void innerConnect() {
|
||||||
if (this.getEndpoint() == null) {
|
if (this.getEndpoint() == null) {
|
||||||
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE,
|
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE,
|
||||||
"Can't connect null endpoint (ep: " + getEndpointName() + ")");
|
"Can't connect null endpoint (ep: " + getEndpointName() + ")");
|
||||||
}
|
}
|
||||||
MediaElement current = this.getEndpoint();
|
MediaElement current = this.getEndpoint();
|
||||||
String prevId = elementIds.peekLast();
|
String prevId = elementIds.peekLast();
|
||||||
while (prevId != null) {
|
while (prevId != null) {
|
||||||
MediaElement prev = elements.get(prevId);
|
MediaElement prev = elements.get(prevId);
|
||||||
if (prev == null) {
|
if (prev == null) {
|
||||||
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE,
|
throw new OpenViduException(Code.MEDIA_ENDPOINT_ERROR_CODE,
|
||||||
"No media element with id " + prevId + " (ep: " + getEndpointName() + ")");
|
"No media element with id " + prevId + " (ep: " + getEndpointName() + ")");
|
||||||
}
|
}
|
||||||
internalSinkConnect(current, prev);
|
internalSinkConnect(current, prev);
|
||||||
current = prev;
|
current = prev;
|
||||||
prevId = getPrevious(prevId);
|
prevId = getPrevious(prevId);
|
||||||
}
|
}
|
||||||
internalSinkConnect(current, passThru);
|
internalSinkConnect(current, passThru);
|
||||||
connected = true;
|
connected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void internalSinkConnect(final MediaElement source, final MediaElement sink) {
|
private void internalSinkConnect(final MediaElement source, final MediaElement sink) {
|
||||||
source.connect(sink, new Continuation<Void>() {
|
source.connect(sink, new Continuation<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Void result) throws Exception {
|
public void onSuccess(Void result) throws Exception {
|
||||||
log.debug("EP {}: Elements have been connected (source {} -> sink {})", getEndpointName(),
|
log.debug("EP {}: Elements have been connected (source {} -> sink {})", getEndpointName(),
|
||||||
source.getId(), sink.getId());
|
source.getId(), sink.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable cause) throws Exception {
|
public void onError(Throwable cause) throws Exception {
|
||||||
log.warn("EP {}: Failed to connect media elements (source {} -> sink {})",
|
log.warn("EP {}: Failed to connect media elements (source {} -> sink {})", getEndpointName(),
|
||||||
getEndpointName(), source.getId(), sink.getId(), cause);
|
source.getId(), sink.getId(), cause);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link #internalSinkConnect(MediaElement, MediaElement)}, but can specify the type of
|
* Same as {@link #internalSinkConnect(MediaElement, MediaElement)}, but can
|
||||||
* the media that will be streamed.
|
* specify the type of the media that will be streamed.
|
||||||
*
|
*
|
||||||
* @param source
|
* @param source
|
||||||
* @param sink
|
* @param sink
|
||||||
* @param type if null, {@link #internalSinkConnect(MediaElement, MediaElement)} will be used
|
* @param type
|
||||||
* instead
|
* if null, {@link #internalSinkConnect(MediaElement, MediaElement)}
|
||||||
* @see #internalSinkConnect(MediaElement, MediaElement)
|
* will be used instead
|
||||||
*/
|
* @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) {
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
internalSinkConnect(source, sink);
|
internalSinkConnect(source, sink);
|
||||||
} else {
|
} else {
|
||||||
source.connect(sink, type, new Continuation<Void>() {
|
source.connect(sink, type, new Continuation<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Void result) throws Exception {
|
public void onSuccess(Void result) throws Exception {
|
||||||
log.debug("EP {}: {} media elements have been connected (source {} -> sink {})",
|
log.debug("EP {}: {} media elements have been connected (source {} -> sink {})", getEndpointName(),
|
||||||
getEndpointName(), type, source.getId(), sink.getId());
|
type, source.getId(), sink.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable cause) throws Exception {
|
public void onError(Throwable cause) throws Exception {
|
||||||
log.warn("EP {}: Failed to connect {} media elements (source {} -> sink {})",
|
log.warn("EP {}: Failed to connect {} media elements (source {} -> sink {})", getEndpointName(),
|
||||||
getEndpointName(), type, source.getId(), sink.getId(), cause);
|
type, source.getId(), sink.getId(), cause);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void internalSinkDisconnect(final MediaElement source, final MediaElement sink) {
|
private void internalSinkDisconnect(final MediaElement source, final MediaElement sink) {
|
||||||
source.disconnect(sink, new Continuation<Void>() {
|
source.disconnect(sink, new Continuation<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Void result) throws Exception {
|
public void onSuccess(Void result) throws Exception {
|
||||||
log.debug("EP {}: Elements have been disconnected (source {} -> sink {})",
|
log.debug("EP {}: Elements have been disconnected (source {} -> sink {})", getEndpointName(),
|
||||||
getEndpointName(), source.getId(), sink.getId());
|
source.getId(), sink.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable cause) throws Exception {
|
public void onError(Throwable cause) throws Exception {
|
||||||
log.warn("EP {}: Failed to disconnect media elements (source {} -> sink {})",
|
log.warn("EP {}: Failed to disconnect media elements (source {} -> sink {})", getEndpointName(),
|
||||||
getEndpointName(), source.getId(), sink.getId(), cause);
|
source.getId(), sink.getId(), cause);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link #internalSinkDisconnect(MediaElement, MediaElement)}, but can specify the type
|
* Same as {@link #internalSinkDisconnect(MediaElement, MediaElement)}, but can
|
||||||
* of the media that will be disconnected.
|
* specify the type of the media that will be disconnected.
|
||||||
*
|
*
|
||||||
* @param source
|
* @param source
|
||||||
* @param sink
|
* @param sink
|
||||||
* @param type if null, {@link #internalSinkConnect(MediaElement, MediaElement)} will be used
|
* @param type
|
||||||
* instead
|
* if null, {@link #internalSinkConnect(MediaElement, MediaElement)}
|
||||||
* @see #internalSinkConnect(MediaElement, MediaElement)
|
* will be used instead
|
||||||
*/
|
* @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) {
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
internalSinkDisconnect(source, sink);
|
internalSinkDisconnect(source, sink);
|
||||||
} else {
|
} else {
|
||||||
source.disconnect(sink, type, new Continuation<Void>() {
|
source.disconnect(sink, type, new Continuation<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Void result) throws Exception {
|
public void onSuccess(Void result) throws Exception {
|
||||||
log.debug("EP {}: {} media elements have been disconnected (source {} -> sink {})",
|
log.debug("EP {}: {} media elements have been disconnected (source {} -> sink {})",
|
||||||
getEndpointName(), type, source.getId(), sink.getId());
|
getEndpointName(), type, source.getId(), sink.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable cause) throws Exception {
|
public void onError(Throwable cause) throws Exception {
|
||||||
log.warn("EP {}: Failed to disconnect {} media elements (source {} -> sink {})",
|
log.warn("EP {}: Failed to disconnect {} media elements (source {} -> sink {})", getEndpointName(),
|
||||||
getEndpointName(), type, source.getId(), sink.getId(), cause);
|
type, source.getId(), sink.getId(), cause);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,8 +179,10 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
private startSession() {
|
private startSession() {
|
||||||
for (const user of this.users) {
|
for (const user of this.users) {
|
||||||
|
|
||||||
this.getToken().then(token => {
|
this.getToken().then(token => {
|
||||||
|
|
||||||
|
const startTimeForUser = Date.now();
|
||||||
|
|
||||||
const OV = new OpenVidu();
|
const OV = new OpenVidu();
|
||||||
|
|
||||||
if (this.turnConf === 'freeice') {
|
if (this.turnConf === 'freeice') {
|
||||||
|
@ -206,9 +208,9 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
|
||||||
.find(s => s.connectionId === session.connection.connectionId).subs
|
.find(s => s.connectionId === session.connection.connectionId).subs
|
||||||
.find(s => s.streamManager.stream.connection.connectionId === subscriber.stream.connection.connectionId);
|
.find(s => s.streamManager.stream.connection.connectionId === subscriber.stream.connection.connectionId);
|
||||||
if (!!error) {
|
if (!!error) {
|
||||||
subAux.state['errorConnecting'] = Date.now();
|
subAux.state['errorConnecting'] = (Date.now() - startTimeForUser);
|
||||||
} else {
|
} else {
|
||||||
subAux.state['connected'] = Date.now();
|
subAux.state['connected'] = (Date.now() - startTimeForUser);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -217,16 +219,18 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
|
||||||
this.subscribers.push({
|
this.subscribers.push({
|
||||||
connectionId: session.connection.connectionId,
|
connectionId: session.connection.connectionId,
|
||||||
subs: [{
|
subs: [{
|
||||||
|
startTime: startTimeForUser,
|
||||||
connectionId: session.connection.connectionId,
|
connectionId: session.connection.connectionId,
|
||||||
streamManager: subscriber,
|
streamManager: subscriber,
|
||||||
state: { 'connecting': Date.now() }
|
state: { 'connecting': (Date.now() - startTimeForUser) }
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
sub.subs.push({
|
sub.subs.push({
|
||||||
|
startTime: startTimeForUser,
|
||||||
connectionId: session.connection.connectionId,
|
connectionId: session.connection.connectionId,
|
||||||
streamManager: subscriber,
|
streamManager: subscriber,
|
||||||
state: { 'connecting': Date.now() }
|
state: { 'connecting': (Date.now() - startTimeForUser) }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +238,7 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
|
||||||
this.subscribers
|
this.subscribers
|
||||||
.find(s => s.connectionId === session.connection.connectionId).subs
|
.find(s => s.connectionId === session.connection.connectionId).subs
|
||||||
.find(s => s.streamManager.stream.connection.connectionId === subscriber.stream.connection.connectionId)
|
.find(s => s.streamManager.stream.connection.connectionId === subscriber.stream.connection.connectionId)
|
||||||
.state['playing'] = Date.now();
|
.state['playing'] = (Date.now() - startTimeForUser);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -245,19 +249,20 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
const publisher = OV.initPublisher(undefined, this.publisherProperties);
|
const publisher = OV.initPublisher(undefined, this.publisherProperties);
|
||||||
const publisherWrapper = {
|
const publisherWrapper = {
|
||||||
|
startTime: startTimeForUser,
|
||||||
connectionId: session.connection.connectionId,
|
connectionId: session.connection.connectionId,
|
||||||
streamManager: publisher,
|
streamManager: publisher,
|
||||||
state: { 'connecting': Date.now() }
|
state: { 'connecting': (Date.now() - startTimeForUser) }
|
||||||
};
|
};
|
||||||
|
|
||||||
publisher.on('streamCreated', () => {
|
publisher.on('streamCreated', () => {
|
||||||
publisherWrapper.state['connected'] = Date.now();
|
publisherWrapper.state['connected'] = (Date.now() - startTimeForUser);
|
||||||
});
|
});
|
||||||
publisher.on('streamPlaying', () => {
|
publisher.on('streamPlaying', () => {
|
||||||
publisherWrapper.state['playing'] = Date.now();
|
publisherWrapper.state['playing'] = (Date.now() - startTimeForUser);
|
||||||
});
|
});
|
||||||
session.publish(publisher).catch(() => {
|
session.publish(publisher).catch(() => {
|
||||||
publisherWrapper.state['errorConnecting'] = Date.now();
|
publisherWrapper.state['errorConnecting'] = (Date.now() - startTimeForUser);
|
||||||
});
|
});
|
||||||
this.publishers.push(publisherWrapper);
|
this.publishers.push(publisherWrapper);
|
||||||
|
|
||||||
|
@ -308,6 +313,7 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
|
||||||
if (event.streamManager.remote) {
|
if (event.streamManager.remote) {
|
||||||
newReport = {
|
newReport = {
|
||||||
connectionId: event.connectionId,
|
connectionId: event.connectionId,
|
||||||
|
startTime: event.startTime,
|
||||||
streamId: event.streamManager.stream.streamId,
|
streamId: event.streamManager.stream.streamId,
|
||||||
state: event.state,
|
state: event.state,
|
||||||
candidatePairSelectedByBrowser: {
|
candidatePairSelectedByBrowser: {
|
||||||
|
@ -318,8 +324,10 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
|
||||||
localCandidate: {},
|
localCandidate: {},
|
||||||
remoteCandidate: {}
|
remoteCandidate: {}
|
||||||
},
|
},
|
||||||
iceCandidatesSentByBrowser: event.streamManager.stream.getLocalIceCandidateList(),
|
iceCandidatesSentByBrowser:
|
||||||
iceCandidatesReceivedByBrowser: event.streamManager.stream.getRemoteIceCandidateList()
|
event.streamManager.stream.getLocalIceCandidateList().map((c: RTCIceCandidate) => c.candidate),
|
||||||
|
iceCandidatesReceivedByBrowser:
|
||||||
|
event.streamManager.stream.getRemoteIceCandidateList().map((c: RTCIceCandidate) => c.candidate),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.report.streamsIn.count++;
|
this.report.streamsIn.count++;
|
||||||
|
@ -327,6 +335,7 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
|
||||||
} else {
|
} else {
|
||||||
newReport = {
|
newReport = {
|
||||||
connectionId: event.connectionId,
|
connectionId: event.connectionId,
|
||||||
|
startTime: event.startTime,
|
||||||
streamId: event.streamManager.stream.streamId,
|
streamId: event.streamManager.stream.streamId,
|
||||||
state: event.state,
|
state: event.state,
|
||||||
candidatePairSelectedByBrowser: {
|
candidatePairSelectedByBrowser: {
|
||||||
|
@ -337,8 +346,10 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
|
||||||
localCandidate: {},
|
localCandidate: {},
|
||||||
remoteCandidate: {}
|
remoteCandidate: {}
|
||||||
},
|
},
|
||||||
iceCandidatesSentByBrowser: event.streamManager.stream.getLocalIceCandidateList(),
|
iceCandidatesSentByBrowser:
|
||||||
iceCandidatesReceivedByBrowser: event.streamManager.stream.getRemoteIceCandidateList()
|
event.streamManager.stream.getLocalIceCandidateList().map((c: RTCIceCandidate) => c.candidate),
|
||||||
|
iceCandidatesReceivedByBrowser:
|
||||||
|
event.streamManager.stream.getRemoteIceCandidateList().map((c: RTCIceCandidate) => c.candidate)
|
||||||
};
|
};
|
||||||
|
|
||||||
this.report.streamsOut.count++;
|
this.report.streamsOut.count++;
|
||||||
|
@ -370,6 +381,12 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
|
||||||
localCandidate: this.parseRemoteCandidatePair(streamOutRemoteInfo.localCandidate),
|
localCandidate: this.parseRemoteCandidatePair(streamOutRemoteInfo.localCandidate),
|
||||||
remoteCandidate: this.parseRemoteCandidatePair(streamOutRemoteInfo.remoteCandidate)
|
remoteCandidate: this.parseRemoteCandidatePair(streamOutRemoteInfo.remoteCandidate)
|
||||||
};
|
};
|
||||||
|
report.serverEvents = streamOutRemoteInfo.events;
|
||||||
|
for (const ev of report.serverEvents) {
|
||||||
|
for (const key of Object.keys(ev)) {
|
||||||
|
ev[key] = Number(ev[key]) - report.startTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.report.streamsIn.items.forEach(report => {
|
this.report.streamsIn.items.forEach(report => {
|
||||||
|
@ -383,6 +400,12 @@ export class TestScenariosComponent implements OnInit, OnDestroy {
|
||||||
localCandidate: this.parseRemoteCandidatePair(streamInRemoteInfo.localCandidate),
|
localCandidate: this.parseRemoteCandidatePair(streamInRemoteInfo.localCandidate),
|
||||||
remoteCandidate: this.parseRemoteCandidatePair(streamInRemoteInfo.remoteCandidate)
|
remoteCandidate: this.parseRemoteCandidatePair(streamInRemoteInfo.remoteCandidate)
|
||||||
};
|
};
|
||||||
|
report.serverEvents = streamInRemoteInfo.events;
|
||||||
|
for (const ev of report.serverEvents) {
|
||||||
|
for (const key of Object.keys(ev)) {
|
||||||
|
ev[key] = Number(ev[key]) - report.startTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.stringifyAllReports = JSON.stringify(this.report, null, '\t');
|
this.stringifyAllReports = JSON.stringify(this.report, null, '\t');
|
||||||
|
|
|
@ -31,8 +31,9 @@ export class TableVideoComponent implements AfterViewInit, DoCheck {
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
this.playingTimeout = setTimeout(() => {
|
this.playingTimeout = setTimeout(() => {
|
||||||
if (!this.state['playing']) {
|
if (!this.state['playing']) {
|
||||||
this.state['timeoutPlaying'] = Date.now();
|
this.state['timeoutPlaying'] = Date.now() - this.streamManager.startTime;
|
||||||
this.readyForReport.emit({
|
this.readyForReport.emit({
|
||||||
|
startTime: this.streamManager.startTime,
|
||||||
connectionId: this.streamManager.connectionId,
|
connectionId: this.streamManager.connectionId,
|
||||||
state: this.state,
|
state: this.state,
|
||||||
streamManager: this.streamManager.streamManager
|
streamManager: this.streamManager.streamManager
|
||||||
|
@ -48,6 +49,7 @@ export class TableVideoComponent implements AfterViewInit, DoCheck {
|
||||||
if (this.success() || this.fail()) {
|
if (this.success() || this.fail()) {
|
||||||
clearTimeout(this.playingTimeout);
|
clearTimeout(this.playingTimeout);
|
||||||
this.readyForReport.emit({
|
this.readyForReport.emit({
|
||||||
|
startTime: this.streamManager.startTime,
|
||||||
connectionId: this.streamManager.connectionId,
|
connectionId: this.streamManager.connectionId,
|
||||||
state: this.state,
|
state: this.state,
|
||||||
streamManager: this.streamManager.streamManager
|
streamManager: this.streamManager.streamManager
|
||||||
|
@ -78,6 +80,7 @@ export class TableVideoComponent implements AfterViewInit, DoCheck {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StreamManagerWrapper {
|
export interface StreamManagerWrapper {
|
||||||
|
startTime: number;
|
||||||
connectionId: string;
|
connectionId: string;
|
||||||
streamManager: StreamManager;
|
streamManager: StreamManager;
|
||||||
state: any;
|
state: any;
|
||||||
|
|
Loading…
Reference in New Issue