plugin-transcoding.ts 9.0 KB

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