plugin-transcoding.ts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
  2. import 'mocha'
  3. import { expect } from 'chai'
  4. import { getAudioStream, getVideoStreamFPS, getVideoStream } from '@server/helpers/ffmpeg'
  5. import { VideoPrivacy } from '@shared/models'
  6. import {
  7. cleanupTests,
  8. createSingleServer,
  9. PeerTubeServer,
  10. PluginsCommand,
  11. setAccessTokensToServers,
  12. setDefaultVideoChannel,
  13. testFfmpegStreamError,
  14. waitJobs
  15. } from '@shared/server-commands'
  16. async function createLiveWrapper (server: PeerTubeServer) {
  17. const liveAttributes = {
  18. name: 'live video',
  19. channelId: server.store.channel.id,
  20. privacy: VideoPrivacy.PUBLIC
  21. }
  22. const { uuid } = await server.live.create({ fields: liveAttributes })
  23. return uuid
  24. }
  25. function updateConf (server: PeerTubeServer, vodProfile: string, liveProfile: string) {
  26. return server.config.updateCustomSubConfig({
  27. newConfig: {
  28. transcoding: {
  29. enabled: true,
  30. profile: vodProfile,
  31. hls: {
  32. enabled: true
  33. },
  34. webtorrent: {
  35. enabled: true
  36. },
  37. resolutions: {
  38. '240p': true,
  39. '360p': false,
  40. '480p': false,
  41. '720p': true
  42. }
  43. },
  44. live: {
  45. transcoding: {
  46. profile: liveProfile,
  47. enabled: true,
  48. resolutions: {
  49. '240p': true,
  50. '360p': false,
  51. '480p': false,
  52. '720p': true
  53. }
  54. }
  55. }
  56. }
  57. })
  58. }
  59. describe('Test transcoding plugins', function () {
  60. let server: PeerTubeServer
  61. before(async function () {
  62. this.timeout(60000)
  63. server = await createSingleServer(1)
  64. await setAccessTokensToServers([ server ])
  65. await setDefaultVideoChannel([ server ])
  66. await updateConf(server, 'default', 'default')
  67. })
  68. describe('When using a plugin adding profiles to existing encoders', function () {
  69. async function checkVideoFPS (uuid: string, type: 'above' | 'below', fps: number) {
  70. const video = await server.videos.get({ id: uuid })
  71. const files = video.files.concat(...video.streamingPlaylists.map(p => p.files))
  72. for (const file of files) {
  73. if (type === 'above') {
  74. expect(file.fps).to.be.above(fps)
  75. } else {
  76. expect(file.fps).to.be.below(fps)
  77. }
  78. }
  79. }
  80. async function checkLiveFPS (uuid: string, type: 'above' | 'below', fps: number) {
  81. const playlistUrl = `${server.url}/static/streaming-playlists/hls/${uuid}/0.m3u8`
  82. const videoFPS = await getVideoStreamFPS(playlistUrl)
  83. if (type === 'above') {
  84. expect(videoFPS).to.be.above(fps)
  85. } else {
  86. expect(videoFPS).to.be.below(fps)
  87. }
  88. }
  89. before(async function () {
  90. await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-transcoding-one') })
  91. })
  92. it('Should have the appropriate available profiles', async function () {
  93. const config = await server.config.getConfig()
  94. expect(config.transcoding.availableProfiles).to.have.members([ 'default', 'low-vod', 'input-options-vod', 'bad-scale-vod' ])
  95. expect(config.live.transcoding.availableProfiles).to.have.members([ 'default', 'high-live', 'input-options-live', 'bad-scale-live' ])
  96. })
  97. describe('VOD', function () {
  98. it('Should not use the plugin profile if not chosen by the admin', async function () {
  99. this.timeout(240000)
  100. const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
  101. await waitJobs([ server ])
  102. await checkVideoFPS(videoUUID, 'above', 20)
  103. })
  104. it('Should use the vod profile', async function () {
  105. this.timeout(240000)
  106. await updateConf(server, 'low-vod', 'default')
  107. const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
  108. await waitJobs([ server ])
  109. await checkVideoFPS(videoUUID, 'below', 12)
  110. })
  111. it('Should apply input options in vod profile', async function () {
  112. this.timeout(240000)
  113. await updateConf(server, 'input-options-vod', 'default')
  114. const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
  115. await waitJobs([ server ])
  116. await checkVideoFPS(videoUUID, 'below', 6)
  117. })
  118. it('Should apply the scale filter in vod profile', async function () {
  119. this.timeout(240000)
  120. await updateConf(server, 'bad-scale-vod', 'default')
  121. const videoUUID = (await server.videos.quickUpload({ name: 'video' })).uuid
  122. await waitJobs([ server ])
  123. // Transcoding failed
  124. const video = await server.videos.get({ id: videoUUID })
  125. expect(video.files).to.have.lengthOf(1)
  126. expect(video.streamingPlaylists).to.have.lengthOf(0)
  127. })
  128. })
  129. describe('Live', function () {
  130. it('Should not use the plugin profile if not chosen by the admin', async function () {
  131. this.timeout(240000)
  132. const liveVideoId = await createLiveWrapper(server)
  133. await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_very_short_240p.mp4' })
  134. await server.live.waitUntilPublished({ videoId: liveVideoId })
  135. await waitJobs([ server ])
  136. await checkLiveFPS(liveVideoId, 'above', 20)
  137. })
  138. it('Should use the live profile', async function () {
  139. this.timeout(240000)
  140. await updateConf(server, 'low-vod', 'high-live')
  141. const liveVideoId = await createLiveWrapper(server)
  142. await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_very_short_240p.mp4' })
  143. await server.live.waitUntilPublished({ videoId: liveVideoId })
  144. await waitJobs([ server ])
  145. await checkLiveFPS(liveVideoId, 'above', 45)
  146. })
  147. it('Should apply the input options on live profile', async function () {
  148. this.timeout(240000)
  149. await updateConf(server, 'low-vod', 'input-options-live')
  150. const liveVideoId = await createLiveWrapper(server)
  151. await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_very_short_240p.mp4' })
  152. await server.live.waitUntilPublished({ videoId: liveVideoId })
  153. await waitJobs([ server ])
  154. await checkLiveFPS(liveVideoId, 'above', 45)
  155. })
  156. it('Should apply the scale filter name on live profile', async function () {
  157. this.timeout(240000)
  158. await updateConf(server, 'low-vod', 'bad-scale-live')
  159. const liveVideoId = await createLiveWrapper(server)
  160. const command = await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_very_short_240p.mp4' })
  161. await testFfmpegStreamError(command, true)
  162. })
  163. it('Should default to the default profile if the specified profile does not exist', async function () {
  164. this.timeout(240000)
  165. await server.plugins.uninstall({ npmName: 'peertube-plugin-test-transcoding-one' })
  166. const config = await server.config.getConfig()
  167. expect(config.transcoding.availableProfiles).to.deep.equal([ 'default' ])
  168. expect(config.live.transcoding.availableProfiles).to.deep.equal([ 'default' ])
  169. const videoUUID = (await server.videos.quickUpload({ name: 'video', fixture: 'video_very_short_240p.mp4' })).uuid
  170. await waitJobs([ server ])
  171. await checkVideoFPS(videoUUID, 'above', 20)
  172. })
  173. })
  174. })
  175. describe('When using a plugin adding new encoders', function () {
  176. before(async function () {
  177. await server.plugins.install({ path: PluginsCommand.getPluginTestPath('-transcoding-two') })
  178. await updateConf(server, 'test-vod-profile', 'test-live-profile')
  179. })
  180. it('Should use the new vod encoders', async function () {
  181. this.timeout(240000)
  182. const videoUUID = (await server.videos.quickUpload({ name: 'video', fixture: 'video_very_short_240p.mp4' })).uuid
  183. await waitJobs([ server ])
  184. const video = await server.videos.get({ id: videoUUID })
  185. const path = server.servers.buildWebTorrentFilePath(video.files[0].fileUrl)
  186. const audioProbe = await getAudioStream(path)
  187. expect(audioProbe.audioStream.codec_name).to.equal('opus')
  188. const videoProbe = await getVideoStream(path)
  189. expect(videoProbe.codec_name).to.equal('vp9')
  190. })
  191. it('Should use the new live encoders', async function () {
  192. this.timeout(240000)
  193. const liveVideoId = await createLiveWrapper(server)
  194. await server.live.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
  195. await server.live.waitUntilPublished({ videoId: liveVideoId })
  196. await waitJobs([ server ])
  197. const playlistUrl = `${server.url}/static/streaming-playlists/hls/${liveVideoId}/0.m3u8`
  198. const audioProbe = await getAudioStream(playlistUrl)
  199. expect(audioProbe.audioStream.codec_name).to.equal('opus')
  200. const videoProbe = await getVideoStream(playlistUrl)
  201. expect(videoProbe.codec_name).to.equal('h264')
  202. })
  203. })
  204. after(async function () {
  205. await cleanupTests([ server ])
  206. })
  207. })