diff --git a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java index 60477f4a..c219b9a9 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java +++ b/openvidu-server/src/main/java/io/openvidu/server/config/OpenviduConfig.java @@ -111,6 +111,8 @@ public class OpenviduConfig { protected Map propertiesSource; + public static final String DEFAULT_RECORDING_IMAGE_REPO = "openvidu/openvidu-recording"; + @Autowired protected Environment env; @@ -141,6 +143,10 @@ public class OpenviduConfig { private String openviduRecordingVersion; + private String openviduRecordingImageRepo; + + private boolean openviduRecordingEnableGPU; + private Integer openviduStreamsVideoMaxRecvBandwidth; private Integer openviduStreamsVideoMinRecvBandwidth; @@ -292,6 +298,14 @@ public class OpenviduConfig { return this.openviduRecordingAutostopTimeout; } + public String getOpenviduRecordingImageRepo() { + return this.openviduRecordingImageRepo; + } + + public boolean isOpenviduRecordingGPUEnabled() { + return this.openviduRecordingEnableGPU; + } + public int getVideoMaxRecvBandwidth() { return this.openviduStreamsVideoMaxRecvBandwidth; } @@ -507,7 +521,8 @@ public class OpenviduConfig { protected List getNonUserProperties() { 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 @@ -549,6 +564,8 @@ public class OpenviduConfig { openviduRecordingComposedBasicauth = asBoolean("OPENVIDU_RECORDING_COMPOSED_BASICAUTH"); openviduRecordingVersion = asNonEmptyString("OPENVIDU_RECORDING_VERSION"); openviduRecordingComposedUrl = asOptionalURL("OPENVIDU_RECORDING_COMPOSED_URL"); + openviduRecordingEnableGPU = asBoolean("OPENVIDU_RECORDING_ENABLE_GPU"); + configureRecordingImage("OPENVIDU_RECORDING_IMAGE"); checkOpenviduRecordingNotification(); openviduStreamsVideoMaxRecvBandwidth = asNonNegativeInteger("OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH"); @@ -1018,6 +1035,24 @@ public class OpenviduConfig { 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: ':'"); + } else { + String customImageName = customImageSplit[0]; + String customVersion = customImageSplit[1]; + openviduRecordingImageRepo = customImageName; + openviduRecordingVersion = customVersion; + } + } + } + private Map loadMediaNodePublicIps(String propertyName) { String mediaNodesPublicIpsRaw = this.asOptionalString(propertyName); final Map mediaNodesPublicIps = new HashMap<>(); diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java index a78b14e4..530e5817 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedQuickStartRecordingService.java @@ -219,7 +219,7 @@ public class ComposedQuickStartRecordingService extends ComposedRecordingService String containerId = null; try { - final String container = RecordingManager.IMAGE_NAME + ":" + openviduConfig.getOpenViduRecordingVersion(); + final String container = openviduConfig.getOpenviduRecordingImageRepo() + ":" + openviduConfig.getOpenViduRecordingVersion(); final String containerName = "recording_" + session.getSessionId(); Volume volume1 = new Volume("/recordings"); List volumes = new ArrayList<>(); @@ -228,7 +228,8 @@ public class ComposedQuickStartRecordingService extends ComposedRecordingService List binds = new ArrayList<>(); binds.add(bind1); 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); this.sessionsContainers.put(session.getSessionId(), containerId); } catch (Exception e) { diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java index 03dee0cf..be62c550 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/ComposedRecordingService.java @@ -164,7 +164,7 @@ public class ComposedRecordingService extends RecordingService { String containerId; try { - final String container = RecordingManager.IMAGE_NAME + ":" + openviduConfig.getOpenViduRecordingVersion(); + final String container = openviduConfig.getOpenviduRecordingImageRepo() + ":" + openviduConfig.getOpenViduRecordingVersion(); final String containerName = "recording_" + recording.getId(); Volume volume1 = new Volume("/recordings"); List volumes = new ArrayList<>(); @@ -173,7 +173,8 @@ public class ComposedRecordingService extends RecordingService { List binds = new ArrayList<>(); binds.add(bind1); 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); } catch (Exception e) { this.cleanRecordingMaps(recording); @@ -605,4 +606,4 @@ public class ComposedRecordingService extends RecordingService { } } -} \ No newline at end of file +} diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java index 94484203..3000d820 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/recording/service/RecordingManager.java @@ -128,8 +128,6 @@ public class RecordingManager { private ScheduledExecutorService automaticRecordingStopExecutor = Executors .newScheduledThreadPool(Runtime.getRuntime().availableProcessors()); - public static final String IMAGE_NAME = "openvidu/openvidu-recording"; - private static final List LAST_PARTICIPANT_LEFT_REASONS = Arrays .asList(new EndReason[] { EndReason.disconnect, EndReason.forceDisconnectByUser, EndReason.forceDisconnectByServer, EndReason.networkDisconnect }); @@ -187,7 +185,7 @@ public class RecordingManager { } // 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) @@ -223,7 +221,7 @@ public class RecordingManager { log.info("Recording module required: Downloading openvidu/openvidu-recording:" + 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"); } else { Thread t = new Thread(() -> { @@ -241,9 +239,9 @@ public class RecordingManager { }); t.start(); try { - dockMng.downloadDockerImage(IMAGE_NAME + ":" + openviduConfig.getOpenViduRecordingVersion(), 600); + dockMng.downloadDockerImage(openviduConfig.getOpenviduRecordingImageRepo() + ":" + openviduConfig.getOpenViduRecordingVersion(), 600); } catch (Exception e) { - log.error("Error downloading docker image {}:{}", IMAGE_NAME, + log.error("Error downloading docker image {}:{}", openviduConfig.getOpenviduRecordingImageRepo(), openviduConfig.getOpenViduRecordingVersion()); } t.interrupt(); diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/DockerManager.java b/openvidu-server/src/main/java/io/openvidu/server/utils/DockerManager.java index 74857a43..80e600bd 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/utils/DockerManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/utils/DockerManager.java @@ -13,7 +13,7 @@ public interface DockerManager { public String runContainer(String mediaNodeId, String image, String containerName, String user, List volumes, List binds, String networkMode, List envs, List command, - Long shmSize, boolean privileged, Map labels) throws Exception; + Long shmSize, boolean privileged, Map labels, boolean enableGPU) throws Exception; public void removeContainer(String mediaNodeId, String containerId, boolean force); diff --git a/openvidu-server/src/main/java/io/openvidu/server/utils/LocalDockerManager.java b/openvidu-server/src/main/java/io/openvidu/server/utils/LocalDockerManager.java index 0b697b51..2d03208d 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/utils/LocalDockerManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/utils/LocalDockerManager.java @@ -26,6 +26,8 @@ import java.util.concurrent.TimeUnit; import javax.ws.rs.ProcessingException; +import com.github.dockerjava.api.model.*; +import com.google.common.collect.ImmutableList; import org.slf4j.Logger; 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.InternalServerErrorException; 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.DockerClientBuilder; import com.github.dockerjava.core.DockerClientConfig; @@ -118,7 +116,7 @@ public class LocalDockerManager implements DockerManager { @Override public String runContainer(String mediaNodeId, String image, String containerName, String user, List volumes, List binds, String networkMode, List envs, List command, - Long shmSize, boolean privileged, Map labels) throws Exception { + Long shmSize, boolean privileged, Map labels, boolean enableGPU) throws Exception { CreateContainerCmd cmd = dockerClient.createContainerCmd(image).withEnv(envs); if (containerName != null) { @@ -133,6 +131,15 @@ public class LocalDockerManager implements DockerManager { if (shmSize != null) { 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) { cmd.withVolumes(volumes); } diff --git a/openvidu-server/src/main/resources/application.properties b/openvidu-server/src/main/resources/application.properties index b0156062..603d0dd8 100644 --- a/openvidu-server/src/main/resources/application.properties +++ b/openvidu-server/src/main/resources/application.properties @@ -37,6 +37,8 @@ OPENVIDU_RECORDING_CUSTOM_LAYOUT=/opt/openvidu/custom-layout OPENVIDU_RECORDING_AUTOSTOP_TIMEOUT=120 OPENVIDU_RECORDING_COMPOSED_URL= OPENVIDU_RECORDING_COMPOSED_BASICAUTH=true +OPENVIDU_RECORDING_IMAGE= +OPENVIDU_RECORDING_ENABLE_GPU=false OPENVIDU_STREAMS_VIDEO_MAX_RECV_BANDWIDTH=1000 OPENVIDU_STREAMS_VIDEO_MIN_RECV_BANDWIDTH=300