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, buildServerDirectory, wait } from '../miscs/miscs'
8 import { ServerInfo } from '../server/servers'
10 function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') {
11 const fixture = buildAbsoluteFixturePath(fixtureName)
13 const command = ffmpeg(fixture)
14 command.inputOption('-stream_loop -1')
15 command.inputOption('-re')
16 command.outputOption('-c:v libx264')
17 command.outputOption('-g 50')
18 command.outputOption('-keyint_min 2')
19 command.outputOption('-r 60')
20 command.outputOption('-f flv')
22 const rtmpUrl = rtmpBaseUrl + '/' + streamKey
23 command.output(rtmpUrl)
25 command.on('error', err => {
26 if (err?.message?.includes('Exiting normally')) return
28 if (process.env.DEBUG) console.error(err)
31 if (process.env.DEBUG) {
32 command.on('stderr', data => console.log(data))
40 function waitFfmpegUntilError (command: ffmpeg.FfmpegCommand, successAfterMS = 10000) {
41 return new Promise<void>((res, rej) => {
42 command.on('error', err => {
52 async function testFfmpegStreamError (command: ffmpeg.FfmpegCommand, shouldHaveError: boolean) {
56 await waitFfmpegUntilError(command, 35000)
61 await stopFfmpeg(command)
63 if (shouldHaveError && !error) throw new Error('Ffmpeg did not have an error')
64 if (!shouldHaveError && error) throw error
67 async function stopFfmpeg (command: ffmpeg.FfmpegCommand) {
68 command.kill('SIGINT')
73 async function waitUntilLivePublishedOnAllServers (servers: ServerInfo[], videoId: string) {
74 for (const server of servers) {
75 await server.liveCommand.waitUntilPublished({ videoId })
79 async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resolutions: number[] = []) {
80 const basePath = buildServerDirectory(server, 'streaming-playlists')
81 const hlsPath = join(basePath, 'hls', videoUUID)
83 if (resolutions.length === 0) {
84 const result = await pathExists(hlsPath)
85 expect(result).to.be.false
90 const files = await readdir(hlsPath)
92 // fragmented file and playlist per resolution + master playlist + segments sha256 json file
93 expect(files).to.have.lengthOf(resolutions.length * 2 + 2)
95 for (const resolution of resolutions) {
96 expect(files).to.contain(`${videoUUID}-${resolution}-fragmented.mp4`)
97 expect(files).to.contain(`${resolution}.m3u8`)
100 expect(files).to.contain('master.m3u8')
101 expect(files).to.contain('segments-sha256.json')
106 waitFfmpegUntilError,
107 testFfmpegStreamError,
109 waitUntilLivePublishedOnAllServers,