mirror of https://github.com/OpenVidu/openvidu.git
openvidu server: Experimental parameter to enable GPU in recording containers. Add custom recording image configuration parameter.
parent
a22ceb2d84
commit
ad1dca04cb
|
@ -111,6 +111,8 @@ public class OpenviduConfig {
|
||||||
|
|
||||||
protected Map<String, ?> propertiesSource;
|
protected Map<String, ?> propertiesSource;
|
||||||
|
|
||||||
|
public static final String DEFAULT_RECORDING_IMAGE_REPO = "openvidu/openvidu-recording";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected Environment env;
|
protected Environment env;
|
||||||
|
|
||||||
|
@ -141,6 +143,10 @@ public class OpenviduConfig {
|
||||||
|
|
||||||
private String openviduRecordingVersion;
|
private String openviduRecordingVersion;
|
||||||
|
|
||||||
|
private String openviduRecordingImageRepo;
|
||||||
|
|
||||||
|
private boolean openviduRecordingEnableGPU;
|
||||||
|
|
||||||
private Integer openviduStreamsVideoMaxRecvBandwidth;
|
private Integer openviduStreamsVideoMaxRecvBandwidth;
|
||||||
|
|
||||||
private Integer openviduStreamsVideoMinRecvBandwidth;
|
private Integer openviduStreamsVideoMinRecvBandwidth;
|
||||||
|
@ -292,6 +298,14 @@ public class OpenviduConfig {
|
||||||
return this.openviduRecordingAutostopTimeout;
|
return this.openviduRecordingAutostopTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getOpenviduRecordingImageRepo() {
|
||||||
|
return this.openviduRecordingImageRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOpenviduRecordingGPUEnabled() {
|
||||||
|
return this.openviduRecordingEnableGPU;
|
||||||
|
}
|
||||||
|
|
||||||
public int getVideoMaxRecvBandwidth() {
|
public int getVideoMaxRecvBandwidth() {
|
||||||
return this.openviduStreamsVideoMaxRecvBandwidth;
|
return this.openviduStreamsVideoMaxRecvBandwidth;
|
||||||
}
|
}
|
||||||
|
@ -507,7 +521,8 @@ public class OpenviduConfig {
|
||||||
|
|
||||||
protected List<String> getNonUserProperties() {
|
protected List<String> getNonUserProperties() {
|
||||||
return Arrays.asList("server.port", "SERVER_PORT", "DOTENV_PATH", "COTURN_IP", "COTURN_REDIS_IP",
|
return Arrays.asList("server.port", "SERVER_PORT", "DOTENV_PATH", "COTURN_IP", "COTURN_REDIS_IP",
|
||||||
"COTURN_REDIS_DBNAME", "COTURN_REDIS_PASSWORD", "COTURN_REDIS_CONNECT_TIMEOUT");
|
"COTURN_REDIS_DBNAME", "COTURN_REDIS_PASSWORD", "COTURN_REDIS_CONNECT_TIMEOUT",
|
||||||
|
"COTURN_INTERNAL_RELAY", "OPENVIDU_RECORDING_IMAGE", "OPENVIDU_RECORDING_ENABLE_GPU");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
@ -549,6 +564,8 @@ public class OpenviduConfig {
|
||||||
openviduRecordingComposedBasicauth = asBoolean("OPENVIDU_RECORDING_COMPOSED_BASICAUTH");
|
openviduRecordingComposedBasicauth = asBoolean("OPENVIDU_RECORDING_COMPOSED_BASICAUTH");
|
||||||
openviduRecordingVersion = asNonEmptyString("OPENVIDU_RECORDING_VERSION");
|
openviduRecordingVersion = asNonEmptyString("OPENVIDU_RECORDING_VERSION");
|
||||||
openviduRecordingComposedUrl = asOptionalURL("OPENVIDU_RECORDING_COMPOSED_URL");
|
openviduRecordingComposedUrl = asOptionalURL("OPENVIDU_RECORDING_COMPOSED_URL");
|
||||||
|
openviduRecordingEnableGPU = asBoolean("OPENVIDU_RECORDING_ENABLE_GPU");
|
||||||
|
configureRecordingImage("OPENVIDU_RECORDING_IMAGE");
|
||||||
checkOpenviduRecordingNotification();
|
checkOpenviduRecordingNotification();
|
||||||
|
|
||||||
openviduStreamsVideoMaxRecvBandwidth = asNonNegativeInteger("OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH");
|
openviduStreamsVideoMaxRecvBandwidth = asNonNegativeInteger("OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH");
|
||||||
|
@ -1018,6 +1035,24 @@ public class OpenviduConfig {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void configureRecordingImage(String recordingImageProperty) {
|
||||||
|
String configuredImage = asOptionalString(recordingImageProperty);
|
||||||
|
if (configuredImage == null || configuredImage.isEmpty()) {
|
||||||
|
openviduRecordingImageRepo = DEFAULT_RECORDING_IMAGE_REPO;
|
||||||
|
} else {
|
||||||
|
String[] customImageSplit = configuredImage.split(":");
|
||||||
|
if (customImageSplit.length != 2) {
|
||||||
|
addError(recordingImageProperty, "The docker image configured is not valid. " +
|
||||||
|
"This parameter must have this format: '<image>:<tag>'");
|
||||||
|
} else {
|
||||||
|
String customImageName = customImageSplit[0];
|
||||||
|
String customVersion = customImageSplit[1];
|
||||||
|
openviduRecordingImageRepo = customImageName;
|
||||||
|
openviduRecordingVersion = customVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, String> loadMediaNodePublicIps(String propertyName) {
|
private Map<String, String> loadMediaNodePublicIps(String propertyName) {
|
||||||
String mediaNodesPublicIpsRaw = this.asOptionalString(propertyName);
|
String mediaNodesPublicIpsRaw = this.asOptionalString(propertyName);
|
||||||
final Map<String, String> mediaNodesPublicIps = new HashMap<>();
|
final Map<String, String> mediaNodesPublicIps = new HashMap<>();
|
||||||
|
|
|
@ -219,7 +219,7 @@ public class ComposedQuickStartRecordingService extends ComposedRecordingService
|
||||||
|
|
||||||
String containerId = null;
|
String containerId = null;
|
||||||
try {
|
try {
|
||||||
final String container = RecordingManager.IMAGE_NAME + ":" + openviduConfig.getOpenViduRecordingVersion();
|
final String container = openviduConfig.getOpenviduRecordingImageRepo() + ":" + openviduConfig.getOpenViduRecordingVersion();
|
||||||
final String containerName = "recording_" + session.getSessionId();
|
final String containerName = "recording_" + session.getSessionId();
|
||||||
Volume volume1 = new Volume("/recordings");
|
Volume volume1 = new Volume("/recordings");
|
||||||
List<Volume> volumes = new ArrayList<>();
|
List<Volume> volumes = new ArrayList<>();
|
||||||
|
@ -228,7 +228,8 @@ public class ComposedQuickStartRecordingService extends ComposedRecordingService
|
||||||
List<Bind> binds = new ArrayList<>();
|
List<Bind> binds = new ArrayList<>();
|
||||||
binds.add(bind1);
|
binds.add(bind1);
|
||||||
containerId = dockerManager.runContainer(properties.mediaNode(), container, containerName, null, volumes,
|
containerId = dockerManager.runContainer(properties.mediaNode(), container, containerName, null, volumes,
|
||||||
binds, "host", envs, null, properties.shmSize(), false, null);
|
binds, "host", envs, null, properties.shmSize(), false, null,
|
||||||
|
openviduConfig.isOpenviduRecordingGPUEnabled());
|
||||||
containers.put(containerId, containerName);
|
containers.put(containerId, containerName);
|
||||||
this.sessionsContainers.put(session.getSessionId(), containerId);
|
this.sessionsContainers.put(session.getSessionId(), containerId);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -164,7 +164,7 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
|
|
||||||
String containerId;
|
String containerId;
|
||||||
try {
|
try {
|
||||||
final String container = RecordingManager.IMAGE_NAME + ":" + openviduConfig.getOpenViduRecordingVersion();
|
final String container = openviduConfig.getOpenviduRecordingImageRepo() + ":" + openviduConfig.getOpenViduRecordingVersion();
|
||||||
final String containerName = "recording_" + recording.getId();
|
final String containerName = "recording_" + recording.getId();
|
||||||
Volume volume1 = new Volume("/recordings");
|
Volume volume1 = new Volume("/recordings");
|
||||||
List<Volume> volumes = new ArrayList<>();
|
List<Volume> volumes = new ArrayList<>();
|
||||||
|
@ -173,7 +173,8 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
List<Bind> binds = new ArrayList<>();
|
List<Bind> binds = new ArrayList<>();
|
||||||
binds.add(bind1);
|
binds.add(bind1);
|
||||||
containerId = dockerManager.runContainer(properties.mediaNode(), container, containerName, null, volumes,
|
containerId = dockerManager.runContainer(properties.mediaNode(), container, containerName, null, volumes,
|
||||||
binds, "host", envs, null, properties.shmSize(), false, null);
|
binds, "host", envs, null, properties.shmSize(), false, null,
|
||||||
|
openviduConfig.isOpenviduRecordingGPUEnabled());
|
||||||
containers.put(containerId, containerName);
|
containers.put(containerId, containerName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
this.cleanRecordingMaps(recording);
|
this.cleanRecordingMaps(recording);
|
||||||
|
@ -605,4 +606,4 @@ public class ComposedRecordingService extends RecordingService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,8 +128,6 @@ public class RecordingManager {
|
||||||
private ScheduledExecutorService automaticRecordingStopExecutor = Executors
|
private ScheduledExecutorService automaticRecordingStopExecutor = Executors
|
||||||
.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
|
.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
|
||||||
|
|
||||||
public static final String IMAGE_NAME = "openvidu/openvidu-recording";
|
|
||||||
|
|
||||||
private static final List<EndReason> LAST_PARTICIPANT_LEFT_REASONS = Arrays
|
private static final List<EndReason> LAST_PARTICIPANT_LEFT_REASONS = Arrays
|
||||||
.asList(new EndReason[] { EndReason.disconnect, EndReason.forceDisconnectByUser,
|
.asList(new EndReason[] { EndReason.disconnect, EndReason.forceDisconnectByUser,
|
||||||
EndReason.forceDisconnectByServer, EndReason.networkDisconnect });
|
EndReason.forceDisconnectByServer, EndReason.networkDisconnect });
|
||||||
|
@ -187,7 +185,7 @@ public class RecordingManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean any stranded openvidu/openvidu-recording container on startup
|
// Clean any stranded openvidu/openvidu-recording container on startup
|
||||||
dockMng.cleanStrandedContainers(RecordingManager.IMAGE_NAME);
|
dockMng.cleanStrandedContainers(openviduConfig.getOpenviduRecordingImageRepo());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkRecordingRequirements(String openviduRecordingPath, String openviduRecordingCustomLayout)
|
public void checkRecordingRequirements(String openviduRecordingPath, String openviduRecordingCustomLayout)
|
||||||
|
@ -223,7 +221,7 @@ public class RecordingManager {
|
||||||
log.info("Recording module required: Downloading openvidu/openvidu-recording:"
|
log.info("Recording module required: Downloading openvidu/openvidu-recording:"
|
||||||
+ openviduConfig.getOpenViduRecordingVersion() + " Docker image (350MB aprox)");
|
+ openviduConfig.getOpenViduRecordingVersion() + " Docker image (350MB aprox)");
|
||||||
|
|
||||||
if (dockMng.dockerImageExistsLocally(IMAGE_NAME + ":" + openviduConfig.getOpenViduRecordingVersion())) {
|
if (dockMng.dockerImageExistsLocally(openviduConfig.getOpenviduRecordingImageRepo() + ":" + openviduConfig.getOpenViduRecordingVersion())) {
|
||||||
log.info("Docker image already exists locally");
|
log.info("Docker image already exists locally");
|
||||||
} else {
|
} else {
|
||||||
Thread t = new Thread(() -> {
|
Thread t = new Thread(() -> {
|
||||||
|
@ -241,9 +239,9 @@ public class RecordingManager {
|
||||||
});
|
});
|
||||||
t.start();
|
t.start();
|
||||||
try {
|
try {
|
||||||
dockMng.downloadDockerImage(IMAGE_NAME + ":" + openviduConfig.getOpenViduRecordingVersion(), 600);
|
dockMng.downloadDockerImage(openviduConfig.getOpenviduRecordingImageRepo() + ":" + openviduConfig.getOpenViduRecordingVersion(), 600);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error downloading docker image {}:{}", IMAGE_NAME,
|
log.error("Error downloading docker image {}:{}", openviduConfig.getOpenviduRecordingImageRepo(),
|
||||||
openviduConfig.getOpenViduRecordingVersion());
|
openviduConfig.getOpenViduRecordingVersion());
|
||||||
}
|
}
|
||||||
t.interrupt();
|
t.interrupt();
|
||||||
|
|
|
@ -13,7 +13,7 @@ public interface DockerManager {
|
||||||
|
|
||||||
public String runContainer(String mediaNodeId, String image, String containerName, String user,
|
public String runContainer(String mediaNodeId, String image, String containerName, String user,
|
||||||
List<Volume> volumes, List<Bind> binds, String networkMode, List<String> envs, List<String> command,
|
List<Volume> volumes, List<Bind> binds, String networkMode, List<String> envs, List<String> command,
|
||||||
Long shmSize, boolean privileged, Map<String, String> labels) throws Exception;
|
Long shmSize, boolean privileged, Map<String, String> labels, boolean enableGPU) throws Exception;
|
||||||
|
|
||||||
public void removeContainer(String mediaNodeId, String containerId, boolean force);
|
public void removeContainer(String mediaNodeId, String containerId, boolean force);
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,8 @@ import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.ws.rs.ProcessingException;
|
import javax.ws.rs.ProcessingException;
|
||||||
|
|
||||||
|
import com.github.dockerjava.api.model.*;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -39,10 +41,6 @@ import com.github.dockerjava.api.exception.ConflictException;
|
||||||
import com.github.dockerjava.api.exception.DockerClientException;
|
import com.github.dockerjava.api.exception.DockerClientException;
|
||||||
import com.github.dockerjava.api.exception.InternalServerErrorException;
|
import com.github.dockerjava.api.exception.InternalServerErrorException;
|
||||||
import com.github.dockerjava.api.exception.NotFoundException;
|
import com.github.dockerjava.api.exception.NotFoundException;
|
||||||
import com.github.dockerjava.api.model.Bind;
|
|
||||||
import com.github.dockerjava.api.model.Container;
|
|
||||||
import com.github.dockerjava.api.model.HostConfig;
|
|
||||||
import com.github.dockerjava.api.model.Volume;
|
|
||||||
import com.github.dockerjava.core.DefaultDockerClientConfig;
|
import com.github.dockerjava.core.DefaultDockerClientConfig;
|
||||||
import com.github.dockerjava.core.DockerClientBuilder;
|
import com.github.dockerjava.core.DockerClientBuilder;
|
||||||
import com.github.dockerjava.core.DockerClientConfig;
|
import com.github.dockerjava.core.DockerClientConfig;
|
||||||
|
@ -118,7 +116,7 @@ public class LocalDockerManager implements DockerManager {
|
||||||
@Override
|
@Override
|
||||||
public String runContainer(String mediaNodeId, String image, String containerName, String user,
|
public String runContainer(String mediaNodeId, String image, String containerName, String user,
|
||||||
List<Volume> volumes, List<Bind> binds, String networkMode, List<String> envs, List<String> command,
|
List<Volume> volumes, List<Bind> binds, String networkMode, List<String> envs, List<String> command,
|
||||||
Long shmSize, boolean privileged, Map<String, String> labels) throws Exception {
|
Long shmSize, boolean privileged, Map<String, String> labels, boolean enableGPU) throws Exception {
|
||||||
|
|
||||||
CreateContainerCmd cmd = dockerClient.createContainerCmd(image).withEnv(envs);
|
CreateContainerCmd cmd = dockerClient.createContainerCmd(image).withEnv(envs);
|
||||||
if (containerName != null) {
|
if (containerName != null) {
|
||||||
|
@ -133,6 +131,15 @@ public class LocalDockerManager implements DockerManager {
|
||||||
if (shmSize != null) {
|
if (shmSize != null) {
|
||||||
hostConfig.withShmSize(shmSize);
|
hostConfig.withShmSize(shmSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableGPU) {
|
||||||
|
DeviceRequest deviceRequest = new DeviceRequest()
|
||||||
|
.withCapabilities(ImmutableList.of(ImmutableList.of("gpu")))
|
||||||
|
.withCount(-1)
|
||||||
|
.withOptions(null);
|
||||||
|
hostConfig.withDeviceRequests(ImmutableList.of(deviceRequest));
|
||||||
|
}
|
||||||
|
|
||||||
if (volumes != null) {
|
if (volumes != null) {
|
||||||
cmd.withVolumes(volumes);
|
cmd.withVolumes(volumes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,8 @@ OPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/custom-layout
|
||||||
OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT=120
|
OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT=120
|
||||||
OPENVIDU_RECORDING_COMPOSED_URL=
|
OPENVIDU_RECORDING_COMPOSED_URL=
|
||||||
OPENVIDU_RECORDING_COMPOSED_BASICAUTH=true
|
OPENVIDU_RECORDING_COMPOSED_BASICAUTH=true
|
||||||
|
OPENVIDU_RECORDING_IMAGE=
|
||||||
|
OPENVIDU_RECORDING_ENABLE_GPU=false
|
||||||
|
|
||||||
OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH=1000
|
OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH=1000
|
||||||
OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH=300
|
OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH=300
|
||||||
|
|
Loading…
Reference in New Issue