1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import { expect } from 'chai'
4 import * as ffmpeg from 'fluent-ffmpeg'
5 import { pathExists, readdir } from 'fs-extra'
6 import { join } from 'path'
7 import { buildAbsoluteFixturePath, wait } from '../miscs'
8 import { PeerTubeServer } from '../server/server'
10 function sendRTMPStream (options: {
13 fixtureName?: string // default video_short.mp4
14 copyCodecs?: boolean // default false
16 const { rtmpBaseUrl, streamKey, fixtureName = 'video_short.mp4', copyCodecs = false } = options
18 const fixture = buildAbsoluteFixturePath(fixtureName)
20 const command = ffmpeg(fixture)
21 command.inputOption('-stream_loop -1')
22 command.inputOption('-re')
25 command.outputOption('-c copy')
27 command.outputOption('-c:v libx264')
28 command.outputOption('-g 50')
29 command.outputOption('-keyint_min 2')
30 command.outputOption('-r 60')
33 command.outputOption('-f flv')
35 const rtmpUrl = rtmpBaseUrl + '/' + streamKey
36 command.output(rtmpUrl)
38 command.on('error', err => {
39 if (err?.message?.includes('Exiting normally')) return
41 if (process.env.DEBUG) console.error(err)
44 if (process.env.DEBUG) {
45 command.on('stderr', data => console.log(data))
53 function waitFfmpegUntilError (command: ffmpeg.FfmpegCommand, successAfterMS = 10000) {
54 return new Promise<void>((res, rej) => {
55 command.on('error', err => {
65 async function testFfmpegStreamError (command: ffmpeg.FfmpegCommand, shouldHaveError: boolean) {
69 await waitFfmpegUntilError(command, 35000)
74 await stopFfmpeg(command)
76 if (shouldHaveError && !error) throw new Error('Ffmpeg did not have an error')
77 if (!shouldHaveError && error) throw error
80 async function stopFfmpeg (command: ffmpeg.FfmpegCommand) {
81 command.kill('SIGINT')
86 async function waitUntilLivePublishedOnAllServers (servers: PeerTubeServer[], videoId: string) {
87 for (const server of servers) {
88 await server.live.waitUntilPublished({ videoId })
92 async function checkLiveCleanupAfterSave (server: PeerTubeServer, videoUUID: string, resolutions: number[] = []) {
93 const basePath = server.servers.buildDirectory('streaming-playlists')
94 const hlsPath = join(basePath, 'hls', videoUUID)
96 if (resolutions.length === 0) {
97 const result = await pathExists(hlsPath)
98 expect(result).to.be.false
103 const files = await readdir(hlsPath)
105 // fragmented file and playlist per resolution + master playlist + segments sha256 json file
106 expect(files).to.have.lengthOf(resolutions.length * 2 + 2)
108 for (const resolution of resolutions) {
109 const fragmentedFile = files.find(f => f.endsWith(`-${resolution}-fragmented.mp4`))
110 expect(fragmentedFile).to.exist
112 const playlistFile = files.find(f => f.endsWith(`${resolution}.m3u8`))
113 expect(playlistFile).to.exist
116 const masterPlaylistFile = files.find(f => f.endsWith('-master.m3u8'))
117 expect(masterPlaylistFile).to.exist
119 const shaFile = files.find(f => f.endsWith('-segments-sha256.json'))
120 expect(shaFile).to.exist
125 waitFfmpegUntilError,
126 testFfmpegStreamError,
128 waitUntilLivePublishedOnAllServers,
129 checkLiveCleanupAfterSave