openvidu server: Experimental parameter to enable GPU in recording containers. Add custom recording image configuration parameter.

pull/648/head
cruizba 2021-07-23 17:03:23 +02:00
parent a22ceb2d84
commit ad1dca04cb
7 changed files with 62 additions and 18 deletions

View File

@ -111,6 +111,8 @@ public class OpenviduConfig {
protected Map<String, ?> 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<String> 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: '<image>:<tag>'");
} else {
String customImageName = customImageSplit[0];
String customVersion = customImageSplit[1];
openviduRecordingImageRepo = customImageName;
openviduRecordingVersion = customVersion;
}
}
}
private Map<String, String> loadMediaNodePublicIps(String propertyName) {
String mediaNodesPublicIpsRaw = this.asOptionalString(propertyName);
final Map<String, String> mediaNodesPublicIps = new HashMap<>();

View File

@ -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<Volume> volumes = new ArrayList<>();
@ -228,7 +228,8 @@ public class ComposedQuickStartRecordingService extends ComposedRecordingService
List<Bind> 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) {

View File

@ -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<Volume> volumes = new ArrayList<>();
@ -173,7 +173,8 @@ public class ComposedRecordingService extends RecordingService {
List<Bind> 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 {
}
}
}
}

View File

@ -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<EndReason> 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();

View File

@ -13,7 +13,7 @@ public interface DockerManager {
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,
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);

View File

@ -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<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);
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);
}

View File

@ -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