diff --git a/config/default.yaml b/config/default.yaml index d4e773d46..7787dc1d6 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -222,6 +222,12 @@ object_storage: # Useful when you want to use a CDN/external proxy base_url: '' # Example: 'https://mirror.example.com' + # PeerTube makes many small requests to the object storage provider to upload/delete/update live chunks + # which can be a problem depending on your object storage provider + # You can also choose to disable this feature to reduce live streams latency + # Live stream replays are not affected by this setting, so they are uploaded in object storage as regular VOD videos + store_live_streams: true + web_videos: bucket_name: 'web-videos' prefix: '' diff --git a/config/production.yaml.example b/config/production.yaml.example index 5e9b62af6..05577c202 100644 --- a/config/production.yaml.example +++ b/config/production.yaml.example @@ -220,6 +220,12 @@ object_storage: # Useful when you want to use a CDN/external proxy base_url: '' # Example: 'https://mirror.example.com' + # PeerTube makes many small requests to the object storage provider to upload/delete/update live chunks + # which can be a problem depending on your object storage provider + # You can also choose to disable this feature to reduce live streams latency + # Live stream replays are not affected by this setting, so they are uploaded in object storage as regular VOD videos + store_live_streams: true + web_videos: bucket_name: 'web-videos' prefix: '' diff --git a/packages/server-commands/src/server/object-storage-command.ts b/packages/server-commands/src/server/object-storage-command.ts index ff8d5d75c..ed9b3a394 100644 --- a/packages/server-commands/src/server/object-storage-command.ts +++ b/packages/server-commands/src/server/object-storage-command.ts @@ -29,7 +29,11 @@ export class ObjectStorageCommand { return 'us-east-1' } - getDefaultMockConfig () { + getDefaultMockConfig (options: { + storeLiveStreams?: boolean // default true + } = {}) { + const { storeLiveStreams = true } = options + return { object_storage: { enabled: true, @@ -39,7 +43,9 @@ export class ObjectStorageCommand { credentials: ObjectStorageCommand.getMockCredentialsConfig(), streaming_playlists: { - bucket_name: this.getMockStreamingPlaylistsBucketName() + bucket_name: this.getMockStreamingPlaylistsBucketName(), + + store_live_streams: storeLiveStreams }, web_videos: { diff --git a/packages/tests/src/api/object-storage/live.ts b/packages/tests/src/api/object-storage/live.ts index c8c214af5..71aa62357 100644 --- a/packages/tests/src/api/object-storage/live.ts +++ b/packages/tests/src/api/object-storage/live.ts @@ -305,6 +305,49 @@ describe('Object storage for lives', function () { }) }) + describe('With live stream to object storage disabled', function () { + let videoUUID: string + + before(async function () { + await servers[0].kill() + await servers[0].run(objectStorage.getDefaultMockConfig({ storeLiveStreams: false })) + await servers[0].config.enableLive({ transcoding: false }) + + videoUUID = await createLive(servers[0], false) + }) + + it('Should create a live and keep it on file system', async function () { + this.timeout(220000) + + const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUID }) + await waitUntilLivePublishedOnAllServers(servers, videoUUID) + + await testLiveVideoResolutions({ + originServer: servers[0], + sqlCommand: sqlCommandServer1, + servers, + liveVideoId: videoUUID, + resolutions: [ 720 ], + transcoded: false, + objectStorage: undefined + }) + + // Should not have files on object storage + await checkFilesCleanup({ server: servers[0], videoUUID, resolutions: [ 720 ], objectStorage }) + + await stopFfmpeg(ffmpegCommand) + }) + + it('Should have saved the replay on object storage', async function () { + this.timeout(220000) + + await waitUntilLiveReplacedByReplayOnAllServers(servers, videoUUID) + await waitJobs(servers) + + await checkFilesExist({ servers, videoUUID, numberOfFiles: 1, objectStorage }) + }) + }) + after(async function () { await sqlCommandServer1.cleanup() await objectStorage.cleanupMock() diff --git a/server/core/initializers/config.ts b/server/core/initializers/config.ts index 8410afc95..4da75126d 100644 --- a/server/core/initializers/config.ts +++ b/server/core/initializers/config.ts @@ -152,7 +152,8 @@ const CONFIG = { STREAMING_PLAYLISTS: { BUCKET_NAME: config.get('object_storage.streaming_playlists.bucket_name'), PREFIX: config.get('object_storage.streaming_playlists.prefix'), - BASE_URL: config.get('object_storage.streaming_playlists.base_url') + BASE_URL: config.get('object_storage.streaming_playlists.base_url'), + STORE_LIVE_STREAMS: config.get('object_storage.streaming_playlists.store_live_streams') }, USER_EXPORTS: { BUCKET_NAME: config.get('object_storage.user_exports.bucket_name'), diff --git a/server/core/lib/live/shared/muxing-session.ts b/server/core/lib/live/shared/muxing-session.ts index 99ee0584a..c1678c27e 100644 --- a/server/core/lib/live/shared/muxing-session.ts +++ b/server/core/lib/live/shared/muxing-session.ts @@ -463,7 +463,7 @@ class MuxingSession extends EventEmitter { playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION playlist.type = VideoStreamingPlaylistType.HLS - playlist.storage = CONFIG.OBJECT_STORAGE.ENABLED + playlist.storage = CONFIG.OBJECT_STORAGE.ENABLED && CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.STORE_LIVE_STREAMS ? FileStorage.OBJECT_STORAGE : FileStorage.FILE_SYSTEM @@ -475,7 +475,7 @@ class MuxingSession extends EventEmitter { videoUUID: this.videoLive.Video.uuid, sha256Path: join(this.outDirectory, this.streamingPlaylist.segmentsSha256Filename), streamingPlaylist: this.streamingPlaylist, - sendToObjectStorage: CONFIG.OBJECT_STORAGE.ENABLED + sendToObjectStorage: this.streamingPlaylist.storage === FileStorage.OBJECT_STORAGE }) }