aboutsummaryrefslogtreecommitdiffhomepage
path: root/shared/server-commands/videos/live.ts
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-12-17 09:29:23 +0100
committerChocobozzz <me@florianbigard.com>2021-12-17 09:29:23 +0100
commitbf54587a3e2ad9c2c186828f2a5682b91ee2cc00 (patch)
tree54b40aaf01bae210632473285c3c7571d51e4f89 /shared/server-commands/videos/live.ts
parent6b5f72beda96d8b7e4d6329c4001827334de27dd (diff)
downloadPeerTube-bf54587a3e2ad9c2c186828f2a5682b91ee2cc00.tar.gz
PeerTube-bf54587a3e2ad9c2c186828f2a5682b91ee2cc00.tar.zst
PeerTube-bf54587a3e2ad9c2c186828f2a5682b91ee2cc00.zip
shared/ typescript types dir server-commands
Diffstat (limited to 'shared/server-commands/videos/live.ts')
-rw-r--r--shared/server-commands/videos/live.ts137
1 files changed, 137 insertions, 0 deletions
diff --git a/shared/server-commands/videos/live.ts b/shared/server-commands/videos/live.ts
new file mode 100644
index 000000000..d3665bc90
--- /dev/null
+++ b/shared/server-commands/videos/live.ts
@@ -0,0 +1,137 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import ffmpeg, { FfmpegCommand } from 'fluent-ffmpeg'
5import { pathExists, readdir } from 'fs-extra'
6import { join } from 'path'
7import { buildAbsoluteFixturePath, wait } from '../miscs'
8import { PeerTubeServer } from '../server/server'
9
10function sendRTMPStream (options: {
11 rtmpBaseUrl: string
12 streamKey: string
13 fixtureName?: string // default video_short.mp4
14 copyCodecs?: boolean // default false
15}) {
16 const { rtmpBaseUrl, streamKey, fixtureName = 'video_short.mp4', copyCodecs = false } = options
17
18 const fixture = buildAbsoluteFixturePath(fixtureName)
19
20 const command = ffmpeg(fixture)
21 command.inputOption('-stream_loop -1')
22 command.inputOption('-re')
23
24 if (copyCodecs) {
25 command.outputOption('-c copy')
26 } else {
27 command.outputOption('-c:v libx264')
28 command.outputOption('-g 50')
29 command.outputOption('-keyint_min 2')
30 command.outputOption('-r 60')
31 }
32
33 command.outputOption('-f flv')
34
35 const rtmpUrl = rtmpBaseUrl + '/' + streamKey
36 command.output(rtmpUrl)
37
38 command.on('error', err => {
39 if (err?.message?.includes('Exiting normally')) return
40
41 if (process.env.DEBUG) console.error(err)
42 })
43
44 if (process.env.DEBUG) {
45 command.on('stderr', data => console.log(data))
46 }
47
48 command.run()
49
50 return command
51}
52
53function waitFfmpegUntilError (command: FfmpegCommand, successAfterMS = 10000) {
54 return new Promise<void>((res, rej) => {
55 command.on('error', err => {
56 return rej(err)
57 })
58
59 setTimeout(() => {
60 res()
61 }, successAfterMS)
62 })
63}
64
65async function testFfmpegStreamError (command: FfmpegCommand, shouldHaveError: boolean) {
66 let error: Error
67
68 try {
69 await waitFfmpegUntilError(command, 35000)
70 } catch (err) {
71 error = err
72 }
73
74 await stopFfmpeg(command)
75
76 if (shouldHaveError && !error) throw new Error('Ffmpeg did not have an error')
77 if (!shouldHaveError && error) throw error
78}
79
80async function stopFfmpeg (command: FfmpegCommand) {
81 command.kill('SIGINT')
82
83 await wait(500)
84}
85
86async function waitUntilLivePublishedOnAllServers (servers: PeerTubeServer[], videoId: string) {
87 for (const server of servers) {
88 await server.live.waitUntilPublished({ videoId })
89 }
90}
91
92async function waitUntilLiveSavedOnAllServers (servers: PeerTubeServer[], videoId: string) {
93 for (const server of servers) {
94 await server.live.waitUntilSaved({ videoId })
95 }
96}
97
98async function checkLiveCleanupAfterSave (server: PeerTubeServer, videoUUID: string, resolutions: number[] = []) {
99 const basePath = server.servers.buildDirectory('streaming-playlists')
100 const hlsPath = join(basePath, 'hls', videoUUID)
101
102 if (resolutions.length === 0) {
103 const result = await pathExists(hlsPath)
104 expect(result).to.be.false
105
106 return
107 }
108
109 const files = await readdir(hlsPath)
110
111 // fragmented file and playlist per resolution + master playlist + segments sha256 json file
112 expect(files).to.have.lengthOf(resolutions.length * 2 + 2)
113
114 for (const resolution of resolutions) {
115 const fragmentedFile = files.find(f => f.endsWith(`-${resolution}-fragmented.mp4`))
116 expect(fragmentedFile).to.exist
117
118 const playlistFile = files.find(f => f.endsWith(`${resolution}.m3u8`))
119 expect(playlistFile).to.exist
120 }
121
122 const masterPlaylistFile = files.find(f => f.endsWith('-master.m3u8'))
123 expect(masterPlaylistFile).to.exist
124
125 const shaFile = files.find(f => f.endsWith('-segments-sha256.json'))
126 expect(shaFile).to.exist
127}
128
129export {
130 sendRTMPStream,
131 waitFfmpegUntilError,
132 testFfmpegStreamError,
133 stopFfmpeg,
134 waitUntilLivePublishedOnAllServers,
135 waitUntilLiveSavedOnAllServers,
136 checkLiveCleanupAfterSave
137}