diff --git a/openvidu-server/pom.xml b/openvidu-server/pom.xml
index bd2fbf0d..1f42077d 100644
--- a/openvidu-server/pom.xml
+++ b/openvidu-server/pom.xml
@@ -327,6 +327,18 @@
openvidu-java-client
${version.openvidu.java.client}
+
+ com.google.cloud
+ libraries-bom
+ 16.1.0
+ pom
+
+
+ com.google.cloud
+ google-cloud-storage
+ 1.113.4
+
+
diff --git a/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java b/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java
index cede2eb9..d0fa5e5d 100644
--- a/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java
+++ b/openvidu-server/src/main/java/io/openvidu/server/OpenViduServer.java
@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
+import io.openvidu.server.recording.*;
import org.bouncycastle.util.Arrays;
import org.kurento.jsonrpc.internal.server.config.JsonRpcConfiguration;
import org.kurento.jsonrpc.server.JsonRpcConfigurer;
@@ -61,10 +62,6 @@ import io.openvidu.server.kurento.kms.DummyLoadManager;
import io.openvidu.server.kurento.kms.FixedOneKmsManager;
import io.openvidu.server.kurento.kms.KmsManager;
import io.openvidu.server.kurento.kms.LoadManager;
-import io.openvidu.server.recording.DummyRecordingDownloader;
-import io.openvidu.server.recording.DummyRecordingUploader;
-import io.openvidu.server.recording.RecordingDownloader;
-import io.openvidu.server.recording.RecordingUploader;
import io.openvidu.server.recording.service.RecordingManager;
import io.openvidu.server.recording.service.RecordingManagerUtils;
import io.openvidu.server.recording.service.RecordingManagerUtilsLocalStorage;
@@ -203,7 +200,11 @@ public class OpenViduServer implements JsonRpcConfigurer {
@Bean
@ConditionalOnMissingBean
- public RecordingUploader recordingUpload() {
+ @DependsOn("openviduConfig")
+ public RecordingUploader recordingUpload(OpenviduConfig openviduConfig) {
+ if (openviduConfig.isGcpRecordingStorageEnabled()) {
+ return new GoogleCloudStorageRecordingUploader();
+ }
return new DummyRecordingUploader();
}
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 199350af..cf011735 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
@@ -190,6 +190,10 @@ public class OpenviduConfig {
private String dotenvPath;
+ private boolean gcpRecordingStorageEnabled;
+
+ private String gcpStorageBucketName;
+
// Derived properties
public static String finalUrl;
@@ -338,6 +342,14 @@ public class OpenviduConfig {
return dotenvPath;
}
+ public boolean isGcpRecordingStorageEnabled() {
+ return gcpRecordingStorageEnabled;
+ }
+
+ public String getGcpStorageBucketName() {
+ return gcpStorageBucketName;
+ }
+
// Derived properties methods
public String getSpringProfile() {
@@ -533,6 +545,10 @@ public class OpenviduConfig {
openviduForcedCodec = asEnumValue("OPENVIDU_STREAMS_FORCED_VIDEO_CODEC", VideoCodec.class);
openviduAllowTranscoding = asBoolean("OPENVIDU_STREAMS_ALLOW_TRANSCODING");
+ gcpRecordingStorageEnabled = asBoolean("OPENVIDU_GCP_RECORDING_STORAGE_ENABLED");
+ gcpStorageBucketName = gcpRecordingStorageEnabled ? asNonEmptyString("OPENVIDU_GCP_STORAGE_BUCKET_NAME")
+ : null;
+
kmsUrisList = checkKmsUris();
checkCoturnIp();
diff --git a/openvidu-server/src/main/java/io/openvidu/server/recording/GoogleCloudStorageRecordingUploader.java b/openvidu-server/src/main/java/io/openvidu/server/recording/GoogleCloudStorageRecordingUploader.java
new file mode 100644
index 00000000..4222e060
--- /dev/null
+++ b/openvidu-server/src/main/java/io/openvidu/server/recording/GoogleCloudStorageRecordingUploader.java
@@ -0,0 +1,68 @@
+package io.openvidu.server.recording;
+
+import com.google.cloud.storage.BlobId;
+import com.google.cloud.storage.BlobInfo;
+import com.google.cloud.storage.Storage;
+import com.google.cloud.storage.StorageException;
+import io.openvidu.server.config.OpenviduConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+public class GoogleCloudStorageRecordingUploader implements RecordingUploader {
+
+ private static final Logger log = LoggerFactory.getLogger(GoogleCloudStorageRecordingUploader.class);
+
+ @Autowired
+ private OpenviduConfig config;
+
+ @Autowired
+ private Storage gcpStorage;
+
+ private final Set recordingsBeingCurrentlyUploaded = ConcurrentHashMap.newKeySet();
+
+ @Override
+ public void uploadRecording(Recording recording, Runnable successCallback, Runnable errorCallback) {
+ recordingsBeingCurrentlyUploaded.add(recording.getId());
+
+ String bucketName = config.getGcpStorageBucketName();
+ BlobInfo blobInfo = BlobInfo.newBuilder(BlobId.of(bucketName, recording.getName())).build();
+ String filePath = getComposedRecordingLocalFilePath(recording);
+
+ try {
+ gcpStorage.create(blobInfo, Files.readAllBytes(Paths.get(filePath)));
+ } catch (StorageException | IOException e) {
+ log.error(e.getMessage(), e);
+ errorCallback.run();
+ } finally {
+ recordingsBeingCurrentlyUploaded.remove(recording.getId());
+ }
+ successCallback.run();
+ }
+
+ private String getComposedRecordingLocalFilePath(Recording recording) {
+ // Audio-only recordings are in WEBM file format
+ String fileExt = recording.hasVideo() ? ".mp4" : ".webm";
+ return config.getOpenViduRecordingPath() + "/" + recording.getId() + "/" + recording.getName() + fileExt;
+ }
+
+ // Prevent uploading recordings from being retrieved from REST API with "ready"
+ // status. This will force their status back to "stopped" on GET until upload
+ // process has finished
+ @Override
+ public void storeAsUploadingRecording(String recordingId) {
+ recordingsBeingCurrentlyUploaded.add(recordingId);
+ }
+
+ @Override
+ public boolean isBeingUploaded(String recordingId) {
+ return recordingsBeingCurrentlyUploaded.contains(recordingId);
+ }
+}