video-transcoding-profiles.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import { logger } from '@server/helpers/logger'
  2. import { getTargetBitrate, VideoResolution } from '../../shared/models/videos'
  3. import { AvailableEncoders, buildStreamSuffix, EncoderOptionsBuilder } from '../helpers/ffmpeg-utils'
  4. import {
  5. canDoQuickAudioTranscode,
  6. ffprobePromise,
  7. getAudioStream,
  8. getMaxAudioBitrate,
  9. getVideoFileBitrate,
  10. getVideoStreamFromFile
  11. } from '../helpers/ffprobe-utils'
  12. import { VIDEO_TRANSCODING_FPS } from '../initializers/constants'
  13. /**
  14. *
  15. * Available encoders and profiles for the transcoding jobs
  16. * These functions are used by ffmpeg-utils that will get the encoders and options depending on the chosen profile
  17. *
  18. */
  19. // Resources:
  20. // * https://slhck.info/video/2017/03/01/rate-control.html
  21. // * https://trac.ffmpeg.org/wiki/Limiting%20the%20output%20bitrate
  22. const defaultX264VODOptionsBuilder: EncoderOptionsBuilder = async ({ input, resolution, fps }) => {
  23. const targetBitrate = await buildTargetBitrate({ input, resolution, fps })
  24. if (!targetBitrate) return { outputOptions: [ ] }
  25. return {
  26. outputOptions: [
  27. `-r ${fps}`,
  28. `-maxrate ${targetBitrate}`,
  29. `-bufsize ${targetBitrate * 2}`
  30. ]
  31. }
  32. }
  33. const defaultX264LiveOptionsBuilder: EncoderOptionsBuilder = async ({ resolution, fps, streamNum }) => {
  34. const targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS)
  35. return {
  36. outputOptions: [
  37. `${buildStreamSuffix('-r:v', streamNum)} ${fps}`,
  38. `${buildStreamSuffix('-b:v', streamNum)} ${targetBitrate}`,
  39. `-maxrate ${targetBitrate}`,
  40. `-bufsize ${targetBitrate * 2}`
  41. ]
  42. }
  43. }
  44. const defaultAACOptionsBuilder: EncoderOptionsBuilder = async ({ input, streamNum }) => {
  45. const probe = await ffprobePromise(input)
  46. if (await canDoQuickAudioTranscode(input, probe)) {
  47. logger.debug('Copy audio stream %s by AAC encoder.', input)
  48. return { copy: true, outputOptions: [] }
  49. }
  50. const parsedAudio = await getAudioStream(input, probe)
  51. // We try to reduce the ceiling bitrate by making rough matches of bitrates
  52. // Of course this is far from perfect, but it might save some space in the end
  53. const audioCodecName = parsedAudio.audioStream['codec_name']
  54. const bitrate = getMaxAudioBitrate(audioCodecName, parsedAudio.bitrate)
  55. logger.debug('Calculating audio bitrate of %s by AAC encoder.', input, { bitrate: parsedAudio.bitrate, audioCodecName })
  56. if (bitrate !== undefined && bitrate !== -1) {
  57. return { outputOptions: [ buildStreamSuffix('-b:a', streamNum), bitrate + 'k' ] }
  58. }
  59. return { outputOptions: [ ] }
  60. }
  61. const defaultLibFDKAACVODOptionsBuilder: EncoderOptionsBuilder = ({ streamNum }) => {
  62. return { outputOptions: [ buildStreamSuffix('-q:a', streamNum), '5' ] }
  63. }
  64. const availableEncoders: AvailableEncoders = {
  65. vod: {
  66. libx264: {
  67. default: defaultX264VODOptionsBuilder
  68. },
  69. aac: {
  70. default: defaultAACOptionsBuilder
  71. },
  72. libfdk_aac: {
  73. default: defaultLibFDKAACVODOptionsBuilder
  74. }
  75. },
  76. live: {
  77. libx264: {
  78. default: defaultX264LiveOptionsBuilder
  79. },
  80. aac: {
  81. default: defaultAACOptionsBuilder
  82. }
  83. }
  84. }
  85. // ---------------------------------------------------------------------------
  86. export {
  87. availableEncoders
  88. }
  89. // ---------------------------------------------------------------------------
  90. async function buildTargetBitrate (options: {
  91. input: string
  92. resolution: VideoResolution
  93. fps: number
  94. }) {
  95. const { input, resolution, fps } = options
  96. const probe = await ffprobePromise(input)
  97. const videoStream = await getVideoStreamFromFile(input, probe)
  98. if (!videoStream) return undefined
  99. const targetBitrate = getTargetBitrate(resolution, fps, VIDEO_TRANSCODING_FPS)
  100. // Don't transcode to an higher bitrate than the original file
  101. const fileBitrate = await getVideoFileBitrate(input, probe)
  102. return Math.min(targetBitrate, fileBitrate)
  103. }