default-transcoding-profiles.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import { logger } from '@server/helpers/logger.js'
  2. import { FFmpegCommandWrapper, getDefaultAvailableEncoders } from '@peertube/peertube-ffmpeg'
  3. import { AvailableEncoders, EncoderOptionsBuilder } from '@peertube/peertube-models'
  4. // ---------------------------------------------------------------------------
  5. // Profile manager to get and change default profiles
  6. // ---------------------------------------------------------------------------
  7. class VideoTranscodingProfilesManager {
  8. private static instance: VideoTranscodingProfilesManager
  9. // 1 === less priority
  10. private readonly encodersPriorities = {
  11. vod: this.buildDefaultEncodersPriorities(),
  12. live: this.buildDefaultEncodersPriorities()
  13. }
  14. private readonly availableEncoders = getDefaultAvailableEncoders()
  15. private availableProfiles = {
  16. vod: [] as string[],
  17. live: [] as string[]
  18. }
  19. private constructor () {
  20. this.buildAvailableProfiles()
  21. }
  22. getAvailableEncoders (): AvailableEncoders {
  23. return {
  24. available: this.availableEncoders,
  25. encodersToTry: {
  26. vod: {
  27. video: this.getEncodersByPriority('vod', 'video'),
  28. audio: this.getEncodersByPriority('vod', 'audio')
  29. },
  30. live: {
  31. video: this.getEncodersByPriority('live', 'video'),
  32. audio: this.getEncodersByPriority('live', 'audio')
  33. }
  34. }
  35. }
  36. }
  37. getAvailableProfiles (type: 'vod' | 'live') {
  38. return this.availableProfiles[type]
  39. }
  40. addProfile (options: {
  41. type: 'vod' | 'live'
  42. encoder: string
  43. profile: string
  44. builder: EncoderOptionsBuilder
  45. }) {
  46. const { type, encoder, profile, builder } = options
  47. const encoders = this.availableEncoders[type]
  48. if (!encoders[encoder]) encoders[encoder] = {}
  49. encoders[encoder][profile] = builder
  50. this.buildAvailableProfiles()
  51. }
  52. removeProfile (options: {
  53. type: 'vod' | 'live'
  54. encoder: string
  55. profile: string
  56. }) {
  57. const { type, encoder, profile } = options
  58. delete this.availableEncoders[type][encoder][profile]
  59. this.buildAvailableProfiles()
  60. }
  61. addEncoderPriority (type: 'vod' | 'live', streamType: 'audio' | 'video', encoder: string, priority: number) {
  62. this.encodersPriorities[type][streamType].push({ name: encoder, priority, isDefault: false })
  63. FFmpegCommandWrapper.resetSupportedEncoders()
  64. }
  65. removeEncoderPriority (type: 'vod' | 'live', streamType: 'audio' | 'video', encoder: string, priority: number) {
  66. this.encodersPriorities[type][streamType] = this.encodersPriorities[type][streamType]
  67. .filter(o => {
  68. // Don't remove default encoders
  69. if (o.isDefault) return true
  70. // Don't include this encoder anymore
  71. if (o.name === encoder && o.priority === priority) return false
  72. return true
  73. })
  74. FFmpegCommandWrapper.resetSupportedEncoders()
  75. }
  76. private getEncodersByPriority (type: 'vod' | 'live', streamType: 'audio' | 'video') {
  77. return this.encodersPriorities[type][streamType]
  78. .sort((e1, e2) => {
  79. if (e1.priority > e2.priority) return -1
  80. else if (e1.priority === e2.priority) return 0
  81. return 1
  82. })
  83. .map(e => e.name)
  84. }
  85. private buildAvailableProfiles () {
  86. for (const type of [ 'vod', 'live' ]) {
  87. const result = new Set()
  88. const encoders = this.availableEncoders[type]
  89. for (const encoderName of Object.keys(encoders)) {
  90. for (const profile of Object.keys(encoders[encoderName])) {
  91. result.add(profile)
  92. }
  93. }
  94. this.availableProfiles[type] = Array.from(result)
  95. }
  96. logger.debug('Available transcoding profiles built.', { availableProfiles: this.availableProfiles })
  97. }
  98. private buildDefaultEncodersPriorities () {
  99. return {
  100. video: [
  101. { name: 'libx264', priority: 100, isDefault: true }
  102. ],
  103. // Try the first one, if not available try the second one etc
  104. audio: [
  105. // we favor VBR, if a good AAC encoder is available
  106. { name: 'libfdk_aac', priority: 200, isDefault: true },
  107. { name: 'aac', priority: 100, isDefault: true }
  108. ]
  109. }
  110. }
  111. static get Instance () {
  112. return this.instance || (this.instance = new this())
  113. }
  114. }
  115. // ---------------------------------------------------------------------------
  116. export {
  117. VideoTranscodingProfilesManager
  118. }