aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2023-01-27 09:04:02 +0100
committerChocobozzz <me@florianbigard.com>2023-01-27 09:04:02 +0100
commitafb371d940531a5a8aafb5895b369b9cdecd555b (patch)
tree0456b1fcc83b57a24c8e10b6a9886e1841ceb937
parent06a9fdf4331db011d1d77aa09911b17d76d69b85 (diff)
downloadPeerTube-afb371d940531a5a8aafb5895b369b9cdecd555b.tar.gz
PeerTube-afb371d940531a5a8aafb5895b369b9cdecd555b.tar.zst
PeerTube-afb371d940531a5a8aafb5895b369b9cdecd555b.zip
Refactor playlist creation for lives
-rw-r--r--server/lib/live/live-manager.ts34
-rw-r--r--server/lib/live/shared/muxing-session.ts71
-rw-r--r--server/tests/api/server/proxy.ts2
3 files changed, 54 insertions, 53 deletions
diff --git a/server/lib/live/live-manager.ts b/server/lib/live/live-manager.ts
index fa4e1df07..1d5b8bf14 100644
--- a/server/lib/live/live-manager.ts
+++ b/server/lib/live/live-manager.ts
@@ -13,18 +13,18 @@ import {
13} from '@server/helpers/ffmpeg' 13} from '@server/helpers/ffmpeg'
14import { logger, loggerTagsFactory } from '@server/helpers/logger' 14import { logger, loggerTagsFactory } from '@server/helpers/logger'
15import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' 15import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config'
16import { P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE } from '@server/initializers/constants' 16import { VIDEO_LIVE } from '@server/initializers/constants'
17import { UserModel } from '@server/models/user/user' 17import { UserModel } from '@server/models/user/user'
18import { VideoModel } from '@server/models/video/video' 18import { VideoModel } from '@server/models/video/video'
19import { VideoLiveModel } from '@server/models/video/video-live' 19import { VideoLiveModel } from '@server/models/video/video-live'
20import { VideoLiveSessionModel } from '@server/models/video/video-live-session' 20import { VideoLiveSessionModel } from '@server/models/video/video-live-session'
21import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' 21import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
22import { MStreamingPlaylistVideo, MVideo, MVideoLiveSession, MVideoLiveVideo } from '@server/types/models' 22import { MVideo, MVideoLiveSession, MVideoLiveVideo } from '@server/types/models'
23import { pick, wait } from '@shared/core-utils' 23import { pick, wait } from '@shared/core-utils'
24import { LiveVideoError, VideoState, VideoStorage, VideoStreamingPlaylistType } from '@shared/models' 24import { LiveVideoError, VideoState } from '@shared/models'
25import { federateVideoIfNeeded } from '../activitypub/videos' 25import { federateVideoIfNeeded } from '../activitypub/videos'
26import { JobQueue } from '../job-queue' 26import { JobQueue } from '../job-queue'
27import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getLiveReplayBaseDirectory } from '../paths' 27import { getLiveReplayBaseDirectory } from '../paths'
28import { PeerTubeSocket } from '../peertube-socket' 28import { PeerTubeSocket } from '../peertube-socket'
29import { Hooks } from '../plugins/hooks' 29import { Hooks } from '../plugins/hooks'
30import { LiveQuotaStore } from './live-quota-store' 30import { LiveQuotaStore } from './live-quota-store'
@@ -255,13 +255,10 @@ class LiveManager {
255 { allResolutions, ...lTags(sessionId, video.uuid) } 255 { allResolutions, ...lTags(sessionId, video.uuid) }
256 ) 256 )
257 257
258 const streamingPlaylist = await this.createLivePlaylist(video, allResolutions)
259
260 return this.runMuxingSession({ 258 return this.runMuxingSession({
261 sessionId, 259 sessionId,
262 videoLive, 260 videoLive,
263 261
264 streamingPlaylist,
265 inputUrl, 262 inputUrl,
266 fps, 263 fps,
267 bitrate, 264 bitrate,
@@ -275,7 +272,6 @@ class LiveManager {
275 sessionId: string 272 sessionId: string
276 videoLive: MVideoLiveVideo 273 videoLive: MVideoLiveVideo
277 274
278 streamingPlaylist: MStreamingPlaylistVideo
279 inputUrl: string 275 inputUrl: string
280 fps: number 276 fps: number
281 bitrate: number 277 bitrate: number
@@ -298,7 +294,7 @@ class LiveManager {
298 videoLive, 294 videoLive,
299 user, 295 user,
300 296
301 ...pick(options, [ 'streamingPlaylist', 'inputUrl', 'bitrate', 'ratio', 'fps', 'allResolutions', 'hasAudio' ]) 297 ...pick(options, [ 'inputUrl', 'bitrate', 'ratio', 'fps', 'allResolutions', 'hasAudio' ])
302 }) 298 })
303 299
304 muxingSession.on('live-ready', () => this.publishAndFederateLive(videoLive, localLTags)) 300 muxingSession.on('live-ready', () => this.publishAndFederateLive(videoLive, localLTags))
@@ -474,26 +470,6 @@ class LiveManager {
474 return resolutionsEnabled 470 return resolutionsEnabled
475 } 471 }
476 472
477 private async createLivePlaylist (video: MVideo, allResolutions: number[]): Promise<MStreamingPlaylistVideo> {
478 const playlist = await VideoStreamingPlaylistModel.loadOrGenerate(video)
479
480 playlist.playlistFilename = generateHLSMasterPlaylistFilename(true)
481 playlist.segmentsSha256Filename = generateHlsSha256SegmentsFilename(true)
482
483 playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION
484 playlist.type = VideoStreamingPlaylistType.HLS
485
486 playlist.storage = CONFIG.OBJECT_STORAGE.ENABLED
487 ? VideoStorage.OBJECT_STORAGE
488 : VideoStorage.FILE_SYSTEM
489
490 if (playlist.storage === VideoStorage.FILE_SYSTEM) {
491 playlist.assignP2PMediaLoaderInfoHashes(video, allResolutions)
492 }
493
494 return playlist.save()
495 }
496
497 private saveStartingSession (videoLive: MVideoLiveVideo) { 473 private saveStartingSession (videoLive: MVideoLiveVideo) {
498 const liveSession = new VideoLiveSessionModel({ 474 const liveSession = new VideoLiveSessionModel({
499 startDate: new Date(), 475 startDate: new Date(),
diff --git a/server/lib/live/shared/muxing-session.ts b/server/lib/live/shared/muxing-session.ts
index 25ecf1c64..2727fc4a7 100644
--- a/server/lib/live/shared/muxing-session.ts
+++ b/server/lib/live/shared/muxing-session.ts
@@ -1,4 +1,3 @@
1
2import { mapSeries } from 'bluebird' 1import { mapSeries } from 'bluebird'
3import { FSWatcher, watch } from 'chokidar' 2import { FSWatcher, watch } from 'chokidar'
4import { FfmpegCommand } from 'fluent-ffmpeg' 3import { FfmpegCommand } from 'fluent-ffmpeg'
@@ -9,12 +8,18 @@ import { EventEmitter } from 'stream'
9import { getLiveMuxingCommand, getLiveTranscodingCommand } from '@server/helpers/ffmpeg' 8import { getLiveMuxingCommand, getLiveTranscodingCommand } from '@server/helpers/ffmpeg'
10import { logger, loggerTagsFactory, LoggerTagsFn } from '@server/helpers/logger' 9import { logger, loggerTagsFactory, LoggerTagsFn } from '@server/helpers/logger'
11import { CONFIG } from '@server/initializers/config' 10import { CONFIG } from '@server/initializers/config'
12import { MEMOIZE_TTL, VIDEO_LIVE } from '@server/initializers/constants' 11import { MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE } from '@server/initializers/constants'
13import { removeHLSFileObjectStorageByPath, storeHLSFileFromFilename, storeHLSFileFromPath } from '@server/lib/object-storage' 12import { removeHLSFileObjectStorageByPath, storeHLSFileFromFilename, storeHLSFileFromPath } from '@server/lib/object-storage'
14import { VideoFileModel } from '@server/models/video/video-file' 13import { VideoFileModel } from '@server/models/video/video-file'
14import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
15import { MStreamingPlaylistVideo, MUserId, MVideoLiveVideo } from '@server/types/models' 15import { MStreamingPlaylistVideo, MUserId, MVideoLiveVideo } from '@server/types/models'
16import { VideoStorage } from '@shared/models' 16import { VideoStorage, VideoStreamingPlaylistType } from '@shared/models'
17import { getLiveDirectory, getLiveReplayBaseDirectory } from '../../paths' 17import {
18 generateHLSMasterPlaylistFilename,
19 generateHlsSha256SegmentsFilename,
20 getLiveDirectory,
21 getLiveReplayBaseDirectory
22} from '../../paths'
18import { VideoTranscodingProfilesManager } from '../../transcoding/default-transcoding-profiles' 23import { VideoTranscodingProfilesManager } from '../../transcoding/default-transcoding-profiles'
19import { isAbleToUploadVideo } from '../../user' 24import { isAbleToUploadVideo } from '../../user'
20import { LiveQuotaStore } from '../live-quota-store' 25import { LiveQuotaStore } from '../live-quota-store'
@@ -53,7 +58,6 @@ class MuxingSession extends EventEmitter {
53 private readonly user: MUserId 58 private readonly user: MUserId
54 private readonly sessionId: string 59 private readonly sessionId: string
55 private readonly videoLive: MVideoLiveVideo 60 private readonly videoLive: MVideoLiveVideo
56 private readonly streamingPlaylist: MStreamingPlaylistVideo
57 private readonly inputUrl: string 61 private readonly inputUrl: string
58 private readonly fps: number 62 private readonly fps: number
59 private readonly allResolutions: number[] 63 private readonly allResolutions: number[]
@@ -70,12 +74,13 @@ class MuxingSession extends EventEmitter {
70 private readonly outDirectory: string 74 private readonly outDirectory: string
71 private readonly replayDirectory: string 75 private readonly replayDirectory: string
72 76
73 private readonly liveSegmentShaStore: LiveSegmentShaStore
74
75 private readonly lTags: LoggerTagsFn 77 private readonly lTags: LoggerTagsFn
76 78
77 private segmentsToProcessPerPlaylist: { [playlistId: string]: string[] } = {} 79 private segmentsToProcessPerPlaylist: { [playlistId: string]: string[] } = {}
78 80
81 private streamingPlaylist: MStreamingPlaylistVideo
82 private liveSegmentShaStore: LiveSegmentShaStore
83
79 private tsWatcher: FSWatcher 84 private tsWatcher: FSWatcher
80 private masterWatcher: FSWatcher 85 private masterWatcher: FSWatcher
81 private m3u8Watcher: FSWatcher 86 private m3u8Watcher: FSWatcher
@@ -98,7 +103,6 @@ class MuxingSession extends EventEmitter {
98 user: MUserId 103 user: MUserId
99 sessionId: string 104 sessionId: string
100 videoLive: MVideoLiveVideo 105 videoLive: MVideoLiveVideo
101 streamingPlaylist: MStreamingPlaylistVideo
102 inputUrl: string 106 inputUrl: string
103 fps: number 107 fps: number
104 bitrate: number 108 bitrate: number
@@ -112,7 +116,6 @@ class MuxingSession extends EventEmitter {
112 this.user = options.user 116 this.user = options.user
113 this.sessionId = options.sessionId 117 this.sessionId = options.sessionId
114 this.videoLive = options.videoLive 118 this.videoLive = options.videoLive
115 this.streamingPlaylist = options.streamingPlaylist
116 this.inputUrl = options.inputUrl 119 this.inputUrl = options.inputUrl
117 this.fps = options.fps 120 this.fps = options.fps
118 121
@@ -131,17 +134,13 @@ class MuxingSession extends EventEmitter {
131 this.outDirectory = getLiveDirectory(this.videoLive.Video) 134 this.outDirectory = getLiveDirectory(this.videoLive.Video)
132 this.replayDirectory = join(getLiveReplayBaseDirectory(this.videoLive.Video), new Date().toISOString()) 135 this.replayDirectory = join(getLiveReplayBaseDirectory(this.videoLive.Video), new Date().toISOString())
133 136
134 this.liveSegmentShaStore = new LiveSegmentShaStore({
135 videoUUID: this.videoLive.Video.uuid,
136 sha256Path: join(this.outDirectory, this.streamingPlaylist.segmentsSha256Filename),
137 streamingPlaylist: this.streamingPlaylist,
138 sendToObjectStorage: CONFIG.OBJECT_STORAGE.ENABLED
139 })
140
141 this.lTags = loggerTagsFactory('live', this.sessionId, this.videoUUID) 137 this.lTags = loggerTagsFactory('live', this.sessionId, this.videoUUID)
142 } 138 }
143 139
144 async runMuxing () { 140 async runMuxing () {
141 this.streamingPlaylist = await this.createLivePlaylist()
142
143 this.createLiveShaStore()
145 this.createFiles() 144 this.createFiles()
146 145
147 await this.prepareDirectories() 146 await this.prepareDirectories()
@@ -257,17 +256,18 @@ class MuxingSession extends EventEmitter {
257 this.masterWatcher = watch(this.outDirectory + '/' + this.streamingPlaylist.playlistFilename) 256 this.masterWatcher = watch(this.outDirectory + '/' + this.streamingPlaylist.playlistFilename)
258 257
259 this.masterWatcher.on('add', async () => { 258 this.masterWatcher.on('add', async () => {
260 if (this.streamingPlaylist.storage === VideoStorage.OBJECT_STORAGE) { 259 try {
261 try { 260 if (this.streamingPlaylist.storage === VideoStorage.OBJECT_STORAGE) {
262 const url = await storeHLSFileFromFilename(this.streamingPlaylist, this.streamingPlaylist.playlistFilename) 261 const url = await storeHLSFileFromFilename(this.streamingPlaylist, this.streamingPlaylist.playlistFilename)
263 262
264 this.streamingPlaylist.playlistUrl = url 263 this.streamingPlaylist.playlistUrl = url
265 this.streamingPlaylist.assignP2PMediaLoaderInfoHashes(this.videoLive.Video, this.allResolutions)
266
267 await this.streamingPlaylist.save()
268 } catch (err) {
269 logger.error('Cannot upload live master file to object storage.', { err, ...this.lTags() })
270 } 264 }
265
266 this.streamingPlaylist.assignP2PMediaLoaderInfoHashes(this.videoLive.Video, this.allResolutions)
267
268 await this.streamingPlaylist.save()
269 } catch (err) {
270 logger.error('Cannot update streaming playlist.', { err, ...this.lTags() })
271 } 271 }
272 272
273 this.masterPlaylistCreated = true 273 this.masterPlaylistCreated = true
@@ -478,6 +478,31 @@ class MuxingSession extends EventEmitter {
478 logger.error('Cannot copy segment %s to replay directory.', segmentPath, { err, ...this.lTags() }) 478 logger.error('Cannot copy segment %s to replay directory.', segmentPath, { err, ...this.lTags() })
479 } 479 }
480 } 480 }
481
482 private async createLivePlaylist (): Promise<MStreamingPlaylistVideo> {
483 const playlist = await VideoStreamingPlaylistModel.loadOrGenerate(this.videoLive.Video)
484
485 playlist.playlistFilename = generateHLSMasterPlaylistFilename(true)
486 playlist.segmentsSha256Filename = generateHlsSha256SegmentsFilename(true)
487
488 playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION
489 playlist.type = VideoStreamingPlaylistType.HLS
490
491 playlist.storage = CONFIG.OBJECT_STORAGE.ENABLED
492 ? VideoStorage.OBJECT_STORAGE
493 : VideoStorage.FILE_SYSTEM
494
495 return playlist.save()
496 }
497
498 private createLiveShaStore () {
499 this.liveSegmentShaStore = new LiveSegmentShaStore({
500 videoUUID: this.videoLive.Video.uuid,
501 sha256Path: join(this.outDirectory, this.streamingPlaylist.segmentsSha256Filename),
502 streamingPlaylist: this.streamingPlaylist,
503 sendToObjectStorage: CONFIG.OBJECT_STORAGE.ENABLED
504 })
505 }
481} 506}
482 507
483// --------------------------------------------------------------------------- 508// ---------------------------------------------------------------------------
diff --git a/server/tests/api/server/proxy.ts b/server/tests/api/server/proxy.ts
index de8872d74..4bf89410e 100644
--- a/server/tests/api/server/proxy.ts
+++ b/server/tests/api/server/proxy.ts
@@ -95,7 +95,7 @@ describe('Test proxy', function () {
95 } 95 }
96 96
97 it('Should succeed import with the appropriate proxy config', async function () { 97 it('Should succeed import with the appropriate proxy config', async function () {
98 this.timeout(120000) 98 this.timeout(240000)
99 99
100 await servers[0].kill() 100 await servers[0].kill()
101 await servers[0].run({}, { env: goodEnv }) 101 await servers[0].run({}, { env: goodEnv })