]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - shared/extra-utils/videos/live.ts
Introduce live command
[github/Chocobozzz/PeerTube.git] / shared / extra-utils / videos / live.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
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'
9
10 function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') {
11 const fixture = buildAbsoluteFixturePath(fixtureName)
12
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')
21
22 const rtmpUrl = rtmpBaseUrl + '/' + streamKey
23 command.output(rtmpUrl)
24
25 command.on('error', err => {
26 if (err?.message?.includes('Exiting normally')) return
27
28 if (process.env.DEBUG) console.error(err)
29 })
30
31 if (process.env.DEBUG) {
32 command.on('stderr', data => console.log(data))
33 }
34
35 command.run()
36
37 return command
38 }
39
40 function waitFfmpegUntilError (command: ffmpeg.FfmpegCommand, successAfterMS = 10000) {
41 return new Promise<void>((res, rej) => {
42 command.on('error', err => {
43 return rej(err)
44 })
45
46 setTimeout(() => {
47 res()
48 }, successAfterMS)
49 })
50 }
51
52 async function testFfmpegStreamError (command: ffmpeg.FfmpegCommand, shouldHaveError: boolean) {
53 let error: Error
54
55 try {
56 await waitFfmpegUntilError(command, 35000)
57 } catch (err) {
58 error = err
59 }
60
61 await stopFfmpeg(command)
62
63 if (shouldHaveError && !error) throw new Error('Ffmpeg did not have an error')
64 if (!shouldHaveError && error) throw error
65 }
66
67 async function stopFfmpeg (command: ffmpeg.FfmpegCommand) {
68 command.kill('SIGINT')
69
70 await wait(500)
71 }
72
73 async function waitUntilLivePublishedOnAllServers (servers: ServerInfo[], videoId: string) {
74 for (const server of servers) {
75 await server.liveCommand.waitUntilLivePublished({ videoId })
76 }
77 }
78
79 async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resolutions: number[] = []) {
80 const basePath = buildServerDirectory(server, 'streaming-playlists')
81 const hlsPath = join(basePath, 'hls', videoUUID)
82
83 if (resolutions.length === 0) {
84 const result = await pathExists(hlsPath)
85 expect(result).to.be.false
86
87 return
88 }
89
90 const files = await readdir(hlsPath)
91
92 // fragmented file and playlist per resolution + master playlist + segments sha256 json file
93 expect(files).to.have.lengthOf(resolutions.length * 2 + 2)
94
95 for (const resolution of resolutions) {
96 expect(files).to.contain(`${videoUUID}-${resolution}-fragmented.mp4`)
97 expect(files).to.contain(`${resolution}.m3u8`)
98 }
99
100 expect(files).to.contain('master.m3u8')
101 expect(files).to.contain('segments-sha256.json')
102 }
103
104 export {
105 sendRTMPStream,
106 waitFfmpegUntilError,
107 testFfmpegStreamError,
108 stopFfmpeg,
109 waitUntilLivePublishedOnAllServers,
110 checkLiveCleanup
111 }