Refactor playlist creation for lives

pull/5573/head
Chocobozzz 2023-01-27 09:04:02 +01:00
parent 06a9fdf433
commit afb371d940
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
3 changed files with 54 additions and 53 deletions

View File

@ -13,18 +13,18 @@ import {
} from '@server/helpers/ffmpeg'
import { logger, loggerTagsFactory } from '@server/helpers/logger'
import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config'
import { P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE } from '@server/initializers/constants'
import { VIDEO_LIVE } from '@server/initializers/constants'
import { UserModel } from '@server/models/user/user'
import { VideoModel } from '@server/models/video/video'
import { VideoLiveModel } from '@server/models/video/video-live'
import { VideoLiveSessionModel } from '@server/models/video/video-live-session'
import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
import { MStreamingPlaylistVideo, MVideo, MVideoLiveSession, MVideoLiveVideo } from '@server/types/models'
import { MVideo, MVideoLiveSession, MVideoLiveVideo } from '@server/types/models'
import { pick, wait } from '@shared/core-utils'
import { LiveVideoError, VideoState, VideoStorage, VideoStreamingPlaylistType } from '@shared/models'
import { LiveVideoError, VideoState } from '@shared/models'
import { federateVideoIfNeeded } from '../activitypub/videos'
import { JobQueue } from '../job-queue'
import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getLiveReplayBaseDirectory } from '../paths'
import { getLiveReplayBaseDirectory } from '../paths'
import { PeerTubeSocket } from '../peertube-socket'
import { Hooks } from '../plugins/hooks'
import { LiveQuotaStore } from './live-quota-store'
@ -255,13 +255,10 @@ class LiveManager {
{ allResolutions, ...lTags(sessionId, video.uuid) }
)
const streamingPlaylist = await this.createLivePlaylist(video, allResolutions)
return this.runMuxingSession({
sessionId,
videoLive,
streamingPlaylist,
inputUrl,
fps,
bitrate,
@ -275,7 +272,6 @@ class LiveManager {
sessionId: string
videoLive: MVideoLiveVideo
streamingPlaylist: MStreamingPlaylistVideo
inputUrl: string
fps: number
bitrate: number
@ -298,7 +294,7 @@ class LiveManager {
videoLive,
user,
...pick(options, [ 'streamingPlaylist', 'inputUrl', 'bitrate', 'ratio', 'fps', 'allResolutions', 'hasAudio' ])
...pick(options, [ 'inputUrl', 'bitrate', 'ratio', 'fps', 'allResolutions', 'hasAudio' ])
})
muxingSession.on('live-ready', () => this.publishAndFederateLive(videoLive, localLTags))
@ -474,26 +470,6 @@ class LiveManager {
return resolutionsEnabled
}
private async createLivePlaylist (video: MVideo, allResolutions: number[]): Promise<MStreamingPlaylistVideo> {
const playlist = await VideoStreamingPlaylistModel.loadOrGenerate(video)
playlist.playlistFilename = generateHLSMasterPlaylistFilename(true)
playlist.segmentsSha256Filename = generateHlsSha256SegmentsFilename(true)
playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION
playlist.type = VideoStreamingPlaylistType.HLS
playlist.storage = CONFIG.OBJECT_STORAGE.ENABLED
? VideoStorage.OBJECT_STORAGE
: VideoStorage.FILE_SYSTEM
if (playlist.storage === VideoStorage.FILE_SYSTEM) {
playlist.assignP2PMediaLoaderInfoHashes(video, allResolutions)
}
return playlist.save()
}
private saveStartingSession (videoLive: MVideoLiveVideo) {
const liveSession = new VideoLiveSessionModel({
startDate: new Date(),

View File

@ -1,4 +1,3 @@
import { mapSeries } from 'bluebird'
import { FSWatcher, watch } from 'chokidar'
import { FfmpegCommand } from 'fluent-ffmpeg'
@ -9,12 +8,18 @@ import { EventEmitter } from 'stream'
import { getLiveMuxingCommand, getLiveTranscodingCommand } from '@server/helpers/ffmpeg'
import { logger, loggerTagsFactory, LoggerTagsFn } from '@server/helpers/logger'
import { CONFIG } from '@server/initializers/config'
import { MEMOIZE_TTL, VIDEO_LIVE } from '@server/initializers/constants'
import { MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE } from '@server/initializers/constants'
import { removeHLSFileObjectStorageByPath, storeHLSFileFromFilename, storeHLSFileFromPath } from '@server/lib/object-storage'
import { VideoFileModel } from '@server/models/video/video-file'
import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
import { MStreamingPlaylistVideo, MUserId, MVideoLiveVideo } from '@server/types/models'
import { VideoStorage } from '@shared/models'
import { getLiveDirectory, getLiveReplayBaseDirectory } from '../../paths'
import { VideoStorage, VideoStreamingPlaylistType } from '@shared/models'
import {
generateHLSMasterPlaylistFilename,
generateHlsSha256SegmentsFilename,
getLiveDirectory,
getLiveReplayBaseDirectory
} from '../../paths'
import { VideoTranscodingProfilesManager } from '../../transcoding/default-transcoding-profiles'
import { isAbleToUploadVideo } from '../../user'
import { LiveQuotaStore } from '../live-quota-store'
@ -53,7 +58,6 @@ class MuxingSession extends EventEmitter {
private readonly user: MUserId
private readonly sessionId: string
private readonly videoLive: MVideoLiveVideo
private readonly streamingPlaylist: MStreamingPlaylistVideo
private readonly inputUrl: string
private readonly fps: number
private readonly allResolutions: number[]
@ -70,12 +74,13 @@ class MuxingSession extends EventEmitter {
private readonly outDirectory: string
private readonly replayDirectory: string
private readonly liveSegmentShaStore: LiveSegmentShaStore
private readonly lTags: LoggerTagsFn
private segmentsToProcessPerPlaylist: { [playlistId: string]: string[] } = {}
private streamingPlaylist: MStreamingPlaylistVideo
private liveSegmentShaStore: LiveSegmentShaStore
private tsWatcher: FSWatcher
private masterWatcher: FSWatcher
private m3u8Watcher: FSWatcher
@ -98,7 +103,6 @@ class MuxingSession extends EventEmitter {
user: MUserId
sessionId: string
videoLive: MVideoLiveVideo
streamingPlaylist: MStreamingPlaylistVideo
inputUrl: string
fps: number
bitrate: number
@ -112,7 +116,6 @@ class MuxingSession extends EventEmitter {
this.user = options.user
this.sessionId = options.sessionId
this.videoLive = options.videoLive
this.streamingPlaylist = options.streamingPlaylist
this.inputUrl = options.inputUrl
this.fps = options.fps
@ -131,17 +134,13 @@ class MuxingSession extends EventEmitter {
this.outDirectory = getLiveDirectory(this.videoLive.Video)
this.replayDirectory = join(getLiveReplayBaseDirectory(this.videoLive.Video), new Date().toISOString())
this.liveSegmentShaStore = new LiveSegmentShaStore({
videoUUID: this.videoLive.Video.uuid,
sha256Path: join(this.outDirectory, this.streamingPlaylist.segmentsSha256Filename),
streamingPlaylist: this.streamingPlaylist,
sendToObjectStorage: CONFIG.OBJECT_STORAGE.ENABLED
})
this.lTags = loggerTagsFactory('live', this.sessionId, this.videoUUID)
}
async runMuxing () {
this.streamingPlaylist = await this.createLivePlaylist()
this.createLiveShaStore()
this.createFiles()
await this.prepareDirectories()
@ -257,17 +256,18 @@ class MuxingSession extends EventEmitter {
this.masterWatcher = watch(this.outDirectory + '/' + this.streamingPlaylist.playlistFilename)
this.masterWatcher.on('add', async () => {
if (this.streamingPlaylist.storage === VideoStorage.OBJECT_STORAGE) {
try {
try {
if (this.streamingPlaylist.storage === VideoStorage.OBJECT_STORAGE) {
const url = await storeHLSFileFromFilename(this.streamingPlaylist, this.streamingPlaylist.playlistFilename)
this.streamingPlaylist.playlistUrl = url
this.streamingPlaylist.assignP2PMediaLoaderInfoHashes(this.videoLive.Video, this.allResolutions)
await this.streamingPlaylist.save()
} catch (err) {
logger.error('Cannot upload live master file to object storage.', { err, ...this.lTags() })
}
this.streamingPlaylist.assignP2PMediaLoaderInfoHashes(this.videoLive.Video, this.allResolutions)
await this.streamingPlaylist.save()
} catch (err) {
logger.error('Cannot update streaming playlist.', { err, ...this.lTags() })
}
this.masterPlaylistCreated = true
@ -478,6 +478,31 @@ class MuxingSession extends EventEmitter {
logger.error('Cannot copy segment %s to replay directory.', segmentPath, { err, ...this.lTags() })
}
}
private async createLivePlaylist (): Promise<MStreamingPlaylistVideo> {
const playlist = await VideoStreamingPlaylistModel.loadOrGenerate(this.videoLive.Video)
playlist.playlistFilename = generateHLSMasterPlaylistFilename(true)
playlist.segmentsSha256Filename = generateHlsSha256SegmentsFilename(true)
playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION
playlist.type = VideoStreamingPlaylistType.HLS
playlist.storage = CONFIG.OBJECT_STORAGE.ENABLED
? VideoStorage.OBJECT_STORAGE
: VideoStorage.FILE_SYSTEM
return playlist.save()
}
private createLiveShaStore () {
this.liveSegmentShaStore = new LiveSegmentShaStore({
videoUUID: this.videoLive.Video.uuid,
sha256Path: join(this.outDirectory, this.streamingPlaylist.segmentsSha256Filename),
streamingPlaylist: this.streamingPlaylist,
sendToObjectStorage: CONFIG.OBJECT_STORAGE.ENABLED
})
}
}
// ---------------------------------------------------------------------------

View File

@ -95,7 +95,7 @@ describe('Test proxy', function () {
}
it('Should succeed import with the appropriate proxy config', async function () {
this.timeout(120000)
this.timeout(240000)
await servers[0].kill()
await servers[0].run({}, { env: goodEnv })