gpu-detector.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. /**
  2. * @fileoverview GPU Hardware Acceleration Detection
  3. * @author GrabZilla Development Team
  4. * @version 2.1.0
  5. */
  6. const { execSync } = require('child_process')
  7. const os = require('os')
  8. const path = require('path')
  9. /**
  10. * GPU Detector
  11. * Detects available hardware acceleration for video encoding
  12. */
  13. class GPUDetector {
  14. constructor() {
  15. this.platform = os.platform()
  16. this.arch = os.arch()
  17. this.capabilities = null
  18. }
  19. /**
  20. * Get path to ffmpeg binary
  21. * @returns {string} Path to ffmpeg
  22. * @private
  23. */
  24. getFfmpegPath() {
  25. const ext = process.platform === 'win32' ? '.exe' : ''
  26. return path.join(__dirname, '../../binaries', `ffmpeg${ext}`)
  27. }
  28. /**
  29. * Detect available GPU hardware acceleration
  30. * @returns {Promise<Object>} GPU capabilities
  31. */
  32. async detect() {
  33. // Return cached result if available
  34. if (this.capabilities) {
  35. return this.capabilities
  36. }
  37. const capabilities = {
  38. hasGPU: false,
  39. type: null,
  40. encoders: [],
  41. decoders: [],
  42. supported: false,
  43. platform: this.platform,
  44. arch: this.arch
  45. }
  46. try {
  47. const ffmpegPath = this.getFfmpegPath()
  48. // Get available encoders
  49. const encodersOutput = execSync(`"${ffmpegPath}" -hide_banner -encoders 2>&1`, {
  50. encoding: 'utf8',
  51. timeout: 10000,
  52. maxBuffer: 1024 * 1024 // 1MB buffer
  53. })
  54. // Get available hardware accelerations
  55. const hwaccelsOutput = execSync(`"${ffmpegPath}" -hide_banner -hwaccels 2>&1`, {
  56. encoding: 'utf8',
  57. timeout: 10000,
  58. maxBuffer: 1024 * 1024
  59. })
  60. // Detect platform-specific hardware acceleration
  61. if (this.platform === 'darwin') {
  62. // macOS - VideoToolbox
  63. if (encodersOutput.includes('h264_videotoolbox')) {
  64. capabilities.type = 'videotoolbox'
  65. capabilities.hasGPU = true
  66. capabilities.encoders = this.parseEncoders(encodersOutput, ['videotoolbox'])
  67. capabilities.decoders = ['h264', 'hevc', 'mpeg4']
  68. capabilities.description = 'Apple VideoToolbox (Hardware Accelerated)'
  69. }
  70. } else if (this.platform === 'win32') {
  71. // Windows - Check NVENC, AMF, QSV in priority order
  72. // NVIDIA NVENC
  73. if (encodersOutput.includes('h264_nvenc') && hwaccelsOutput.includes('cuda')) {
  74. capabilities.type = 'nvenc'
  75. capabilities.hasGPU = true
  76. capabilities.encoders = this.parseEncoders(encodersOutput, ['nvenc'])
  77. capabilities.decoders = ['h264', 'hevc', 'mpeg2', 'mpeg4', 'vc1', 'vp8', 'vp9']
  78. capabilities.description = 'NVIDIA NVENC (Hardware Accelerated)'
  79. }
  80. // AMD AMF
  81. else if (encodersOutput.includes('h264_amf')) {
  82. capabilities.type = 'amf'
  83. capabilities.hasGPU = true
  84. capabilities.encoders = this.parseEncoders(encodersOutput, ['amf'])
  85. capabilities.decoders = ['h264', 'hevc', 'mpeg2', 'mpeg4']
  86. capabilities.description = 'AMD AMF (Hardware Accelerated)'
  87. }
  88. // Intel QSV
  89. else if (encodersOutput.includes('h264_qsv') && hwaccelsOutput.includes('qsv')) {
  90. capabilities.type = 'qsv'
  91. capabilities.hasGPU = true
  92. capabilities.encoders = this.parseEncoders(encodersOutput, ['qsv'])
  93. capabilities.decoders = ['h264', 'hevc', 'mpeg2', 'mpeg4', 'vp8', 'vp9']
  94. capabilities.description = 'Intel Quick Sync (Hardware Accelerated)'
  95. }
  96. } else {
  97. // Linux - Check for VAAPI, NVENC
  98. if (encodersOutput.includes('h264_vaapi') && hwaccelsOutput.includes('vaapi')) {
  99. capabilities.type = 'vaapi'
  100. capabilities.hasGPU = true
  101. capabilities.encoders = this.parseEncoders(encodersOutput, ['vaapi'])
  102. capabilities.decoders = ['h264', 'hevc', 'mpeg2', 'mpeg4', 'vp8', 'vp9']
  103. capabilities.description = 'VA-API (Hardware Accelerated)'
  104. } else if (encodersOutput.includes('h264_nvenc') && hwaccelsOutput.includes('cuda')) {
  105. capabilities.type = 'nvenc'
  106. capabilities.hasGPU = true
  107. capabilities.encoders = this.parseEncoders(encodersOutput, ['nvenc'])
  108. capabilities.decoders = ['h264', 'hevc', 'mpeg2', 'mpeg4', 'vc1', 'vp8', 'vp9']
  109. capabilities.description = 'NVIDIA NVENC (Hardware Accelerated)'
  110. }
  111. }
  112. capabilities.supported = capabilities.hasGPU
  113. // Log detection results
  114. if (capabilities.hasGPU) {
  115. console.log(`🎮 GPU Acceleration: ${capabilities.description}`)
  116. console.log(` Encoders: ${capabilities.encoders.join(', ')}`)
  117. console.log(` Platform: ${this.platform} (${this.arch})`)
  118. } else {
  119. console.log(`⚠️ No GPU acceleration available - using software encoding`)
  120. }
  121. } catch (error) {
  122. console.warn('GPU detection failed:', error.message)
  123. capabilities.error = error.message
  124. }
  125. // Cache the result
  126. this.capabilities = capabilities
  127. return capabilities
  128. }
  129. /**
  130. * Parse encoder list for specific hardware type
  131. * @param {string} encodersOutput - Output from ffmpeg -encoders
  132. * @param {Array<string>} keywords - Keywords to filter (e.g., ['nvenc', 'videotoolbox'])
  133. * @returns {Array<string>} List of available encoders
  134. * @private
  135. */
  136. parseEncoders(encodersOutput, keywords) {
  137. const encoders = []
  138. const lines = encodersOutput.split('\n')
  139. for (const line of lines) {
  140. // Skip header lines
  141. if (line.trim().startsWith('--') || line.trim().length === 0) continue
  142. // Check if line contains any of the keywords
  143. const matchesKeyword = keywords.some(keyword =>
  144. line.toLowerCase().includes(keyword.toLowerCase())
  145. )
  146. if (matchesKeyword) {
  147. // Extract encoder name (format: " V..... h264_videotoolbox ...")
  148. const match = line.match(/^\s*[VA\.]+\s+(\S+)\s/)
  149. if (match) {
  150. encoders.push(match[1])
  151. }
  152. }
  153. }
  154. return encoders
  155. }
  156. /**
  157. * Get recommended encoder for H.264
  158. * @returns {string} Encoder name
  159. */
  160. getH264Encoder() {
  161. if (!this.capabilities || !this.capabilities.hasGPU) {
  162. return 'libx264' // Software fallback
  163. }
  164. const h264Encoders = this.capabilities.encoders.filter(e =>
  165. e.includes('h264') || e.includes('264')
  166. )
  167. return h264Encoders.length > 0 ? h264Encoders[0] : 'libx264'
  168. }
  169. /**
  170. * Get recommended encoder for HEVC/H.265
  171. * @returns {string} Encoder name
  172. */
  173. getHEVCEncoder() {
  174. if (!this.capabilities || !this.capabilities.hasGPU) {
  175. return 'libx265' // Software fallback
  176. }
  177. const hevcEncoders = this.capabilities.encoders.filter(e =>
  178. e.includes('hevc') || e.includes('265')
  179. )
  180. return hevcEncoders.length > 0 ? hevcEncoders[0] : 'libx265'
  181. }
  182. /**
  183. * Check if GPU is available
  184. * @returns {boolean} True if GPU hardware acceleration is available
  185. */
  186. isAvailable() {
  187. return this.capabilities && this.capabilities.hasGPU
  188. }
  189. /**
  190. * Get GPU type
  191. * @returns {string|null} GPU type (videotoolbox, nvenc, amf, qsv, vaapi) or null
  192. */
  193. getType() {
  194. return this.capabilities ? this.capabilities.type : null
  195. }
  196. /**
  197. * Reset cached capabilities (for testing)
  198. */
  199. reset() {
  200. this.capabilities = null
  201. }
  202. }
  203. // Export singleton instance
  204. module.exports = new GPUDetector()