mirror of https://github.com/OpenVidu/openvidu.git
Merge branch 'master' of github.com:OpenVidu/openvidu
commit
0a36927836
|
@ -268,7 +268,7 @@ public class OpenViduServer implements JsonRpcConfigurer {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
String msg = "\n\n\n" + " Configuration properties\n" + " ----------------------\n" + "\n";
|
String msg = "\n\n\n" + " Configuration properties\n" + " ------------------------\n" + "\n";
|
||||||
|
|
||||||
Map<String, String> configProps = config.getConfigProps();
|
Map<String, String> configProps = config.getConfigProps();
|
||||||
List<String> configPropNames = new ArrayList<>(config.getUserProperties());
|
List<String> configPropNames = new ArrayList<>(config.getUserProperties());
|
||||||
|
@ -290,10 +290,10 @@ public class OpenViduServer implements JsonRpcConfigurer {
|
||||||
String dashboardUrl = httpUrl + "dashboard/";
|
String dashboardUrl = httpUrl + "dashboard/";
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
String msg = "\n\n----------------------------------------------------\n" + "\n"
|
String msg = "\n\n----------------------------------------------------\n" + "\n" + " OpenVidu is ready!\n"
|
||||||
+ " OpenVidu Platform is ready!\n" + " ---------------------------\n" + "\n"
|
+ " ---------------------------\n" + "\n" + " * OpenVidu Server: " + httpUrl + "\n" + "\n"
|
||||||
+ " * OpenVidu Server: " + httpUrl + "\n" + "\n" + " * OpenVidu Dashboard: " + dashboardUrl + "\n"
|
+ " * OpenVidu Dashboard: " + dashboardUrl + "\n" + "\n"
|
||||||
+ "\n" + "----------------------------------------------------\n";
|
+ "----------------------------------------------------\n";
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
log.info(msg);
|
log.info(msg);
|
||||||
|
|
|
@ -81,6 +81,11 @@ public class OpenviduConfig {
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Error [property=" + property + ", value=" + value + ", message=" + message + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(OpenviduConfig.class);
|
private static final Logger log = LoggerFactory.getLogger(OpenviduConfig.class);
|
||||||
|
@ -95,6 +100,8 @@ public class OpenviduConfig {
|
||||||
|
|
||||||
private List<String> userConfigProps;
|
private List<String> userConfigProps;
|
||||||
|
|
||||||
|
private Map<String, ?> propertiesSource;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected Environment env;
|
protected Environment env;
|
||||||
|
|
||||||
|
@ -333,6 +340,17 @@ public class OpenviduConfig {
|
||||||
|
|
||||||
// Properties management methods
|
// Properties management methods
|
||||||
|
|
||||||
|
public OpenviduConfig deriveWithAdditionalPropertiesSource(Map<String, ?> propertiesSource) {
|
||||||
|
OpenviduConfig config = newOpenviduConfig();
|
||||||
|
config.propertiesSource = propertiesSource;
|
||||||
|
config.env = env;
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OpenviduConfig newOpenviduConfig() {
|
||||||
|
return new OpenviduConfig();
|
||||||
|
}
|
||||||
|
|
||||||
public List<Error> getConfigErrors() {
|
public List<Error> getConfigErrors() {
|
||||||
return configErrors;
|
return configErrors;
|
||||||
}
|
}
|
||||||
|
@ -345,9 +363,25 @@ public class OpenviduConfig {
|
||||||
return userConfigProps;
|
return userConfigProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getConfigValue(String property) {
|
private String getValue(String property) {
|
||||||
|
|
||||||
String value = env.getProperty(property);
|
String value = null;
|
||||||
|
|
||||||
|
if (propertiesSource != null) {
|
||||||
|
|
||||||
|
Object valueObj = propertiesSource.get(property);
|
||||||
|
if (valueObj != null) {
|
||||||
|
if(valueObj instanceof Iterable) {
|
||||||
|
value = JsonUtils.toJson(valueObj);
|
||||||
|
} else {
|
||||||
|
value = valueObj.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
value = env.getProperty(property);
|
||||||
|
}
|
||||||
|
|
||||||
this.configProps.put(property, value);
|
this.configProps.put(property, value);
|
||||||
|
|
||||||
|
@ -367,14 +401,14 @@ public class OpenviduConfig {
|
||||||
String value = null;
|
String value = null;
|
||||||
|
|
||||||
if (property != null) {
|
if (property != null) {
|
||||||
value = getConfigValue(property);
|
value = getValue(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.configErrors.add(new Error(property, value, msg));
|
this.configErrors.add(new Error(property, value, msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
protected void checkConfigurationProperties() {
|
public void checkConfiguration() {
|
||||||
|
|
||||||
dotenv = new Dotenv();
|
dotenv = new Dotenv();
|
||||||
|
|
||||||
|
@ -387,7 +421,7 @@ public class OpenviduConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.checkConfigurationParameters();
|
this.checkConfigurationProperties();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Exception checking configuration", e);
|
log.error("Exception checking configuration", e);
|
||||||
addError(null, "Exception checking configuration." + e.getClass().getName() + ":" + e.getMessage());
|
addError(null, "Exception checking configuration." + e.getClass().getName() + ":" + e.getMessage());
|
||||||
|
@ -405,15 +439,15 @@ public class OpenviduConfig {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
protected void checkConfigurationParameters() {
|
protected void checkConfigurationProperties() {
|
||||||
|
|
||||||
serverPort = getConfigValue("server.port");
|
serverPort = getValue("server.port");
|
||||||
|
|
||||||
coturnRedisDbname = getConfigValue("coturn.redis.dbname");
|
coturnRedisDbname = getValue("coturn.redis.dbname");
|
||||||
|
|
||||||
coturnRedisPassword = getConfigValue("coturn.redis.password");
|
coturnRedisPassword = getValue("coturn.redis.password");
|
||||||
|
|
||||||
coturnRedisConnectTimeout = getConfigValue("coturn.redis.connect-timeout");
|
coturnRedisConnectTimeout = getValue("coturn.redis.connect-timeout");
|
||||||
|
|
||||||
openviduSecret = asNonEmptyString("openvidu.secret");
|
openviduSecret = asNonEmptyString("openvidu.secret");
|
||||||
|
|
||||||
|
@ -437,6 +471,9 @@ public class OpenviduConfig {
|
||||||
openviduStreamsVideoMaxSendBandwidth = asNonNegativeInteger("openvidu.streams.video.max-send-bandwidth");
|
openviduStreamsVideoMaxSendBandwidth = asNonNegativeInteger("openvidu.streams.video.max-send-bandwidth");
|
||||||
openviduStreamsVideoMinSendBandwidth = asNonNegativeInteger("openvidu.streams.video.min-send-bandwidth");
|
openviduStreamsVideoMinSendBandwidth = asNonNegativeInteger("openvidu.streams.video.min-send-bandwidth");
|
||||||
|
|
||||||
|
openviduSessionsGarbageInterval = asNonNegativeInteger("openvidu.sessions.garbage.interval");
|
||||||
|
openviduSessionsGarbageThreshold = asNonNegativeInteger("openvidu.sessions.garbage.threshold");
|
||||||
|
|
||||||
kmsUrisList = checkKmsUris();
|
kmsUrisList = checkKmsUris();
|
||||||
|
|
||||||
checkCoturnIp();
|
checkCoturnIp();
|
||||||
|
@ -447,9 +484,6 @@ public class OpenviduConfig {
|
||||||
|
|
||||||
checkCertificateType();
|
checkCertificateType();
|
||||||
|
|
||||||
openviduSessionsGarbageInterval = asNonNegativeInteger("openvidu.sessions.garbage.interval");
|
|
||||||
openviduSessionsGarbageThreshold = asNonNegativeInteger("openvidu.sessions.garbage.threshold");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkCertificateType() {
|
private void checkCertificateType() {
|
||||||
|
@ -465,7 +499,7 @@ public class OpenviduConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkCoturnIp() {
|
private void checkCoturnIp() {
|
||||||
coturnIp = getConfigValue("coturn.ip");
|
coturnIp = getValue("coturn.ip");
|
||||||
|
|
||||||
if (coturnIp == null || this.coturnIp.isEmpty()) {
|
if (coturnIp == null || this.coturnIp.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
|
@ -501,13 +535,13 @@ public class OpenviduConfig {
|
||||||
|
|
||||||
private void checkOpenviduPublicurl() {
|
private void checkOpenviduPublicurl() {
|
||||||
final String property = "openvidu.domain.or.public.ip";
|
final String property = "openvidu.domain.or.public.ip";
|
||||||
String domain = getConfigValue(property);
|
String domain = getValue(property);
|
||||||
|
|
||||||
if (domain != null && !domain.isEmpty()) {
|
if (domain != null && !domain.isEmpty()) {
|
||||||
this.openviduPublicUrl = "https://" + domain;
|
this.openviduPublicUrl = "https://" + domain;
|
||||||
} else {
|
} else {
|
||||||
final String urlProperty = "openvidu.publicurl";
|
final String urlProperty = "openvidu.publicurl";
|
||||||
String publicurl = getConfigValue(urlProperty);
|
String publicurl = getValue(urlProperty);
|
||||||
if (publicurl == null || publicurl.isEmpty()) {
|
if (publicurl == null || publicurl.isEmpty()) {
|
||||||
addError(property, "Cannot be empty");
|
addError(property, "Cannot be empty");
|
||||||
} else {
|
} else {
|
||||||
|
@ -573,7 +607,7 @@ public class OpenviduConfig {
|
||||||
|
|
||||||
String property = "kms.uris";
|
String property = "kms.uris";
|
||||||
|
|
||||||
return asKmsUris(property, getConfigValue(property));
|
return asKmsUris(property, getValue(property));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,7 +680,7 @@ public class OpenviduConfig {
|
||||||
// -------------------------------------------------------
|
// -------------------------------------------------------
|
||||||
|
|
||||||
protected String asOptionalURL(String property) {
|
protected String asOptionalURL(String property) {
|
||||||
String optionalUrl = getConfigValue(property);
|
String optionalUrl = getValue(property);
|
||||||
try {
|
try {
|
||||||
if (!optionalUrl.isEmpty()) {
|
if (!optionalUrl.isEmpty()) {
|
||||||
checkUrl(optionalUrl);
|
checkUrl(optionalUrl);
|
||||||
|
@ -659,7 +693,7 @@ public class OpenviduConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String asNonEmptyString(String property) {
|
protected String asNonEmptyString(String property) {
|
||||||
String stringValue = getConfigValue(property);
|
String stringValue = getValue(property);
|
||||||
if (stringValue != null && !stringValue.isEmpty()) {
|
if (stringValue != null && !stringValue.isEmpty()) {
|
||||||
return stringValue;
|
return stringValue;
|
||||||
} else {
|
} else {
|
||||||
|
@ -669,11 +703,11 @@ public class OpenviduConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String asOptionalString(String property) {
|
protected String asOptionalString(String property) {
|
||||||
return getConfigValue(property);
|
return getValue(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean asBoolean(String property) {
|
protected boolean asBoolean(String property) {
|
||||||
String value = getConfigValue(property);
|
String value = getValue(property);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
addError(property, "Cannot be empty");
|
addError(property, "Cannot be empty");
|
||||||
return false;
|
return false;
|
||||||
|
@ -689,7 +723,7 @@ public class OpenviduConfig {
|
||||||
|
|
||||||
protected Integer asNonNegativeInteger(String property) {
|
protected Integer asNonNegativeInteger(String property) {
|
||||||
try {
|
try {
|
||||||
Integer integerValue = Integer.parseInt(getConfigValue(property));
|
Integer integerValue = Integer.parseInt(getValue(property));
|
||||||
|
|
||||||
if (integerValue < 0) {
|
if (integerValue < 0) {
|
||||||
addError(property, "Is not a non negative integer");
|
addError(property, "Is not a non negative integer");
|
||||||
|
@ -705,7 +739,7 @@ public class OpenviduConfig {
|
||||||
* This method checks all types of Internet addresses (IPv4, IPv6 and Domains)
|
* This method checks all types of Internet addresses (IPv4, IPv6 and Domains)
|
||||||
*/
|
*/
|
||||||
protected String asOptionalInetAddress(String property) {
|
protected String asOptionalInetAddress(String property) {
|
||||||
String inetAddress = getConfigValue(property);
|
String inetAddress = getValue(property);
|
||||||
if (inetAddress != null && !inetAddress.isEmpty()) {
|
if (inetAddress != null && !inetAddress.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
Inet6Address.getByName(inetAddress).getHostAddress();
|
Inet6Address.getByName(inetAddress).getHostAddress();
|
||||||
|
@ -733,7 +767,7 @@ public class OpenviduConfig {
|
||||||
protected List<String> asJsonStringsArray(String property) {
|
protected List<String> asJsonStringsArray(String property) {
|
||||||
try {
|
try {
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
JsonArray jsonArray = gson.fromJson(getConfigValue(property), JsonArray.class);
|
JsonArray jsonArray = gson.fromJson(getValue(property), JsonArray.class);
|
||||||
List<String> list = JsonUtils.toStringList(jsonArray);
|
List<String> list = JsonUtils.toStringList(jsonArray);
|
||||||
if (list.size() == 1 && list.get(0).isEmpty()) {
|
if (list.size() == 1 && list.get(0).isEmpty()) {
|
||||||
list = new ArrayList<>();
|
list = new ArrayList<>();
|
||||||
|
@ -746,7 +780,7 @@ public class OpenviduConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <E extends Enum<E>> E asEnumValue(String property, Class<E> enumType) {
|
protected <E extends Enum<E>> E asEnumValue(String property, Class<E> enumType) {
|
||||||
String value = this.getConfigValue(property);
|
String value = this.getValue(property);
|
||||||
try {
|
try {
|
||||||
return Enum.valueOf(enumType, value);
|
return Enum.valueOf(enumType, value);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
|
|
|
@ -28,6 +28,7 @@ import java.util.TimerTask;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
@ -447,7 +448,8 @@ public abstract class SessionManager {
|
||||||
long sessionExistsSince = currentMillis - sessionNotActive.getStartTime();
|
long sessionExistsSince = currentMillis - sessionNotActive.getStartTime();
|
||||||
if (sessionExistsSince > (openviduConfig.getSessionGarbageThreshold() * 1000)) {
|
if (sessionExistsSince > (openviduConfig.getSessionGarbageThreshold() * 1000)) {
|
||||||
try {
|
try {
|
||||||
sessionNotActive.closingLock.writeLock().lock();
|
if (sessionNotActive.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
|
||||||
|
try {
|
||||||
if (sessions.containsKey(sessionId)) {
|
if (sessions.containsKey(sessionId)) {
|
||||||
// The session passed to active during lock wait
|
// The session passed to active during lock wait
|
||||||
continue;
|
continue;
|
||||||
|
@ -458,6 +460,16 @@ public abstract class SessionManager {
|
||||||
} finally {
|
} finally {
|
||||||
sessionNotActive.closingLock.writeLock().unlock();
|
sessionNotActive.closingLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.error(
|
||||||
|
"Timeout waiting for Session closing lock to be available for garbage collector to clean session {}",
|
||||||
|
sessionId);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.error(
|
||||||
|
"InterruptedException while waiting for Session closing lock to be available for garbage collector to clean session {}",
|
||||||
|
sessionId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,7 +527,8 @@ public abstract class SessionManager {
|
||||||
// to the session. That is: if the session was in the automatic recording stop
|
// to the session. That is: if the session was in the automatic recording stop
|
||||||
// timeout with INDIVIDUAL recording (no docker participant connected)
|
// timeout with INDIVIDUAL recording (no docker participant connected)
|
||||||
try {
|
try {
|
||||||
session.closingLock.writeLock().lock();
|
if (session.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
|
||||||
|
try {
|
||||||
if (session.isClosed()) {
|
if (session.isClosed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -523,6 +536,12 @@ public abstract class SessionManager {
|
||||||
} finally {
|
} finally {
|
||||||
session.closingLock.writeLock().unlock();
|
session.closingLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.error("Timeout waiting for Session {} closing lock to be available", sessionId);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.error("InterruptedException while waiting for Session {} closing lock to be available", sessionId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -331,12 +331,18 @@ public class KurentoParticipant extends Participant {
|
||||||
} finally {
|
} finally {
|
||||||
pub.closingLock.writeLock().unlock();
|
pub.closingLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.error(
|
||||||
|
"Timeout waiting for PublisherEndpoint closing lock of participant {} to be available for participant {} to call cancelReceveivingMedia",
|
||||||
|
senderName, this.getParticipantPublicId());
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
subscribers.remove(senderName);
|
|
||||||
log.error(
|
log.error(
|
||||||
"Timeout wating for PublisherEndpoint closing lock of participant {} to be available for participant {} to call cancelReceveivingMedia",
|
"InterruptedException while waiting for PublisherEndpoint closing lock of participant {} to be available for participant {} to call cancelReceveivingMedia",
|
||||||
senderName, this.getParticipantPublicId());
|
senderName, this.getParticipantPublicId());
|
||||||
|
} finally {
|
||||||
|
// Always clean map
|
||||||
|
subscribers.remove(senderName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ public class KurentoSessionManager extends SessionManager {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
/* Protected by Session.closingLock.readLock */
|
/* Protected by Session.closingLock.readLock */
|
||||||
public synchronized void joinRoom(Participant participant, String sessionId, Integer transactionId) {
|
public void joinRoom(Participant participant, String sessionId, Integer transactionId) {
|
||||||
Set<Participant> existingParticipants = null;
|
Set<Participant> existingParticipants = null;
|
||||||
boolean lockAcquired = false;
|
boolean lockAcquired = false;
|
||||||
try {
|
try {
|
||||||
|
@ -162,8 +162,7 @@ public class KurentoSessionManager extends SessionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized boolean leaveRoom(Participant participant, Integer transactionId, EndReason reason,
|
public boolean leaveRoom(Participant participant, Integer transactionId, EndReason reason, boolean closeWebSocket) {
|
||||||
boolean closeWebSocket) {
|
|
||||||
log.info("Request [LEAVE_ROOM] for participant {} of session {} with reason {}",
|
log.info("Request [LEAVE_ROOM] for participant {} of session {} with reason {}",
|
||||||
participant.getParticipantPublicId(), participant.getSessionId(),
|
participant.getParticipantPublicId(), participant.getSessionId(),
|
||||||
reason != null ? reason.name() : "NULL");
|
reason != null ? reason.name() : "NULL");
|
||||||
|
@ -235,7 +234,8 @@ public class KurentoSessionManager extends SessionManager {
|
||||||
recordingManager.initAutomaticRecordingStopThread(session);
|
recordingManager.initAutomaticRecordingStopThread(session);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
session.closingLock.writeLock().lock();
|
if (session.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
|
||||||
|
try {
|
||||||
if (session.isClosed()) {
|
if (session.isClosed()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -245,7 +245,16 @@ public class KurentoSessionManager extends SessionManager {
|
||||||
} finally {
|
} finally {
|
||||||
session.closingLock.writeLock().unlock();
|
session.closingLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.error(
|
||||||
|
"Timeout waiting for Session {} closing lock to be available for closing as last participant left",
|
||||||
|
sessionId);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.error(
|
||||||
|
"InterruptedException while waiting for Session {} closing lock to be available for closing as last participant left",
|
||||||
|
sessionId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (remainingParticipants.size() == 1 && openviduConfig.isRecordingModuleEnabled()
|
} else if (remainingParticipants.size() == 1 && openviduConfig.isRecordingModuleEnabled()
|
||||||
&& MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode())
|
&& MediaMode.ROUTED.equals(session.getSessionProperties().mediaMode())
|
||||||
|
|
|
@ -480,23 +480,24 @@ public class RecordingManager {
|
||||||
recordingId, this.openviduConfig.getOpenviduRecordingAutostopTimeout());
|
recordingId, this.openviduConfig.getOpenviduRecordingAutostopTimeout());
|
||||||
|
|
||||||
if (this.automaticRecordingStopThreads.remove(session.getSessionId()) != null) {
|
if (this.automaticRecordingStopThreads.remove(session.getSessionId()) != null) {
|
||||||
|
|
||||||
boolean alreadyUnlocked = false;
|
boolean alreadyUnlocked = false;
|
||||||
try {
|
try {
|
||||||
session.closingLock.writeLock().lock();
|
if (session.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
|
||||||
|
try {
|
||||||
if (session.isClosed()) {
|
if (session.isClosed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.getParticipants().size() == 0 || session.onlyRecorderParticipant()) {
|
if (session.getParticipants().size() == 0 || session.onlyRecorderParticipant()) {
|
||||||
// Close session if there are no participants connected (RECORDER does not
|
// Close session if there are no participants connected (RECORDER does not
|
||||||
// count) and publishing
|
// count) and publishing
|
||||||
log.info("Closing session {} after automatic stop of recording {}", session.getSessionId(),
|
log.info("Closing session {} after automatic stop of recording {}",
|
||||||
recordingId);
|
session.getSessionId(), recordingId);
|
||||||
sessionManager.closeSessionAndEmptyCollections(session, EndReason.automaticStop, true);
|
sessionManager.closeSessionAndEmptyCollections(session, EndReason.automaticStop,
|
||||||
|
true);
|
||||||
} else {
|
} else {
|
||||||
// There are users connected, but no one is publishing
|
// There are users connected, but no one is publishing
|
||||||
session.closingLock.writeLock().unlock(); // We don't need the lock if session's not closing
|
// We don't need the lock if session is not closing
|
||||||
|
session.closingLock.writeLock().unlock();
|
||||||
alreadyUnlocked = true;
|
alreadyUnlocked = true;
|
||||||
log.info(
|
log.info(
|
||||||
"Automatic stopping recording {}. There are users connected to session {}, but no one is publishing",
|
"Automatic stopping recording {}. There are users connected to session {}, but no one is publishing",
|
||||||
|
@ -508,6 +509,17 @@ public class RecordingManager {
|
||||||
session.closingLock.writeLock().unlock();
|
session.closingLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.error(
|
||||||
|
"Timeout waiting for Session {} closing lock to be available for automatic recording stop thred",
|
||||||
|
session.getSessionId());
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.error(
|
||||||
|
"InterruptedException while waiting for Session {} closing lock to be available for automatic recording stop thred",
|
||||||
|
session.getSessionId());
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// This code shouldn't be reachable
|
// This code shouldn't be reachable
|
||||||
log.warn("Recording {} was already automatically stopped by a previous thread", recordingId);
|
log.warn("Recording {} was already automatically stopped by a previous thread", recordingId);
|
||||||
|
@ -523,7 +535,8 @@ public class RecordingManager {
|
||||||
if (future != null) {
|
if (future != null) {
|
||||||
boolean cancelled = future.cancel(false);
|
boolean cancelled = future.cancel(false);
|
||||||
try {
|
try {
|
||||||
session.closingLock.writeLock().lock();
|
if (session.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
|
||||||
|
try {
|
||||||
if (session.isClosed()) {
|
if (session.isClosed()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -536,10 +549,20 @@ public class RecordingManager {
|
||||||
session.getSessionId());
|
session.getSessionId());
|
||||||
sessionManager.closeSessionAndEmptyCollections(session, reason, false);
|
sessionManager.closeSessionAndEmptyCollections(session, reason, false);
|
||||||
}
|
}
|
||||||
return cancelled;
|
|
||||||
} finally {
|
} finally {
|
||||||
session.closingLock.writeLock().unlock();
|
session.closingLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.error(
|
||||||
|
"Timeout waiting for Session {} closing lock to be available for aborting automatic recording stop thred",
|
||||||
|
session.getSessionId());
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.error(
|
||||||
|
"InterruptedException while waiting for Session {} closing lock to be available for aborting automatic recording stop thred",
|
||||||
|
session.getSessionId());
|
||||||
|
}
|
||||||
|
return cancelled;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.net.MalformedURLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
@ -234,16 +235,29 @@ public class SessionRestController {
|
||||||
Session sessionNotActive = this.sessionManager.getSessionNotActive(sessionId);
|
Session sessionNotActive = this.sessionManager.getSessionNotActive(sessionId);
|
||||||
if (sessionNotActive != null) {
|
if (sessionNotActive != null) {
|
||||||
try {
|
try {
|
||||||
sessionNotActive.closingLock.writeLock().lock();
|
if (sessionNotActive.closingLock.writeLock().tryLock(15, TimeUnit.SECONDS)) {
|
||||||
|
try {
|
||||||
if (sessionNotActive.isClosed()) {
|
if (sessionNotActive.isClosed()) {
|
||||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||||
}
|
}
|
||||||
this.sessionManager.closeSessionAndEmptyCollections(sessionNotActive, EndReason.sessionClosedByServer,
|
this.sessionManager.closeSessionAndEmptyCollections(sessionNotActive,
|
||||||
true);
|
EndReason.sessionClosedByServer, true);
|
||||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||||
} finally {
|
} finally {
|
||||||
sessionNotActive.closingLock.writeLock().unlock();
|
sessionNotActive.closingLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
String errorMsg = "Timeout waiting for Session " + sessionId
|
||||||
|
+ " closing lock to be available for closing from DELETE /api/sessions";
|
||||||
|
log.error(errorMsg);
|
||||||
|
return this.generateErrorResponse(errorMsg, "/api/sessions", HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
String errorMsg = "InterruptedException while waiting for Session " + sessionId
|
||||||
|
+ " closing lock to be available for closing from DELETE /api/sessions";
|
||||||
|
log.error(errorMsg);
|
||||||
|
return this.generateErrorResponse(errorMsg, "/api/sessions", HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import static org.openqa.selenium.OutputType.BASE64;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -3259,6 +3260,8 @@ public class OpenViduTestAppE2eTest {
|
||||||
recording.getResolution(), realResolution);
|
recording.getResolution(), realResolution);
|
||||||
|
|
||||||
log.info("Recording map color: {}", colorMap.toString());
|
log.info("Recording map color: {}", colorMap.toString());
|
||||||
|
log.info("Recording frame below");
|
||||||
|
System.out.println(bufferedImageToBase64PngString(image));
|
||||||
isFine = this.checkVideoAverageRgbGreen(colorMap);
|
isFine = this.checkVideoAverageRgbGreen(colorMap);
|
||||||
} catch (IOException | JCodecException e) {
|
} catch (IOException | JCodecException e) {
|
||||||
log.warn("Error getting frame from video recording: {}", e.getMessage());
|
log.warn("Error getting frame from video recording: {}", e.getMessage());
|
||||||
|
@ -3267,6 +3270,22 @@ public class OpenViduTestAppE2eTest {
|
||||||
return isFine;
|
return isFine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String bufferedImageToBase64PngString(BufferedImage image) {
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
String imageString = null;
|
||||||
|
try {
|
||||||
|
ImageIO.write(image, "png", bos);
|
||||||
|
byte[] imageBytes = bos.toByteArray();
|
||||||
|
imageString = "data:image/png;base64," + Base64.getEncoder().encodeToString(imageBytes);
|
||||||
|
bos.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageString;
|
||||||
|
}
|
||||||
|
|
||||||
private void checkIndividualRecording(String recPath, Recording recording, int numberOfVideoFiles,
|
private void checkIndividualRecording(String recPath, Recording recording, int numberOfVideoFiles,
|
||||||
String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {
|
String audioDecoder, String videoDecoder, boolean checkAudio) throws IOException {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue