]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/helpers/ffmpeg-utils.ts
Add audio-only option to transcoders and player
[github/Chocobozzz/PeerTube.git] / server / helpers / ffmpeg-utils.ts
index 8041e7b3b494168edbe80e8e0e9e5827168c2feb..2d9ce2bfa0313aa90e7fe6318597407624b22803 100644 (file)
@@ -14,6 +14,7 @@ function computeResolutionsToTranscode (videoFileHeight: number) {
 
   // Put in the order we want to proceed jobs
   const resolutions = [
+    VideoResolution.H_NOVIDEO,
     VideoResolution.H_480P,
     VideoResolution.H_360P,
     VideoResolution.H_720P,
@@ -34,10 +35,15 @@ function computeResolutionsToTranscode (videoFileHeight: number) {
 async function getVideoFileSize (path: string) {
   const videoStream = await getVideoStreamFromFile(path)
 
-  return {
-    width: videoStream.width,
-    height: videoStream.height
-  }
+  return videoStream == null 
+    ? {
+        width: 0,
+        height: 0
+      }
+    : {
+        width: videoStream.width,
+        height: videoStream.height
+      }
 }
 
 async function getVideoFileResolution (path: string) {
@@ -52,6 +58,10 @@ async function getVideoFileResolution (path: string) {
 async function getVideoFileFPS (path: string) {
   const videoStream = await getVideoStreamFromFile(path)
 
+  if (videoStream == null) {
+      return 0
+  }
+
   for (const key of [ 'avg_frame_rate', 'r_frame_rate' ]) {
     const valuesText: string = videoStream[key]
     if (!valuesText) continue
@@ -118,7 +128,7 @@ async function generateImageFromVideoFile (fromPath: string, folder: string, ima
   }
 }
 
-type TranscodeOptionsType = 'hls' | 'quick-transcode' | 'video' | 'merge-audio'
+type TranscodeOptionsType = 'hls' | 'quick-transcode' | 'video' | 'merge-audio' | 'split-audio'
 
 interface BaseTranscodeOptions {
   type: TranscodeOptionsType
@@ -130,6 +140,7 @@ interface BaseTranscodeOptions {
 
 interface HLSTranscodeOptions extends BaseTranscodeOptions {
   type: 'hls'
+  copyCodecs: boolean
   hlsPlaylist: {
     videoFilename: string
   }
@@ -148,7 +159,11 @@ interface MergeAudioTranscodeOptions extends BaseTranscodeOptions {
   audioPath: string
 }
 
-type TranscodeOptions = HLSTranscodeOptions | VideoTranscodeOptions | MergeAudioTranscodeOptions | QuickTranscodeOptions
+interface SplitAudioTranscodeOptions extends BaseTranscodeOptions {
+  type: 'split-audio'
+}
+
+type TranscodeOptions = HLSTranscodeOptions | VideoTranscodeOptions | MergeAudioTranscodeOptions | SplitAudioTranscodeOptions | QuickTranscodeOptions
 
 function transcode (options: TranscodeOptions) {
   return new Promise<void>(async (res, rej) => {
@@ -162,6 +177,8 @@ function transcode (options: TranscodeOptions) {
         command = await buildHLSCommand(command, options)
       } else if (options.type === 'merge-audio') {
         command = await buildAudioMergeCommand(command, options)
+      } else if (options.type === 'split-audio') {
+        command = await buildAudioSplitCommand(command, options)
       } else {
         command = await buildx264Command(command, options)
       }
@@ -197,7 +214,9 @@ async function canDoQuickTranscode (path: string): Promise<boolean> {
   const resolution = await getVideoFileResolution(path)
 
   // check video params
+  if (videoStream == null) return false
   if (videoStream[ 'codec_name' ] !== 'h264') return false
+  if (videoStream[ 'pix_fmt' ] !== 'yuv420p') return false
   if (fps < VIDEO_TRANSCODING_FPS.MIN || fps > VIDEO_TRANSCODING_FPS.MAX) return false
   if (bitRate > getMaxBitrate(resolution.videoFileResolution, fps, VIDEO_TRANSCODING_FPS)) return false
 
@@ -231,7 +250,7 @@ export {
 
 // ---------------------------------------------------------------------------
 
-async function buildx264Command (command: ffmpeg.FfmpegCommand, options: VideoTranscodeOptions) {
+async function buildx264Command (command: ffmpeg.FfmpegCommand, options: TranscodeOptions) {
   let fps = await getVideoFileFPS(options.inputPath)
   // On small/medium resolutions, limit FPS
   if (
@@ -274,6 +293,12 @@ async function buildAudioMergeCommand (command: ffmpeg.FfmpegCommand, options: M
   return command
 }
 
+async function buildAudioSplitCommand (command: ffmpeg.FfmpegCommand, options: SplitAudioTranscodeOptions) {
+  command = await presetAudioSplit(command)
+
+  return command
+}
+
 async function buildQuickTranscodeCommand (command: ffmpeg.FfmpegCommand) {
   command = await presetCopy(command)
 
@@ -286,7 +311,8 @@ async function buildQuickTranscodeCommand (command: ffmpeg.FfmpegCommand) {
 async function buildHLSCommand (command: ffmpeg.FfmpegCommand, options: HLSTranscodeOptions) {
   const videoPath = getHLSVideoPath(options)
 
-  command = await presetCopy(command)
+  if (options.copyCodecs) command = await presetCopy(command)
+  else command = await buildx264Command(command, options)
 
   command = command.outputOption('-hls_time 4')
                    .outputOption('-hls_list_size 0')
@@ -324,7 +350,7 @@ function getVideoStreamFromFile (path: string) {
       if (err) return rej(err)
 
       const videoStream = metadata.streams.find(s => s.codec_type === 'video')
-      if (!videoStream) return rej(new Error('Cannot find video stream of ' + path))
+      //if (!videoStream) return rej(new Error('Cannot find video stream of ' + path))
 
       return res(videoStream)
     })
@@ -387,14 +413,15 @@ namespace audio {
   export namespace bitrate {
     const baseKbitrate = 384
 
-    const toBits = (kbits: number): number => { return kbits * 8000 }
+    const toBits = (kbits: number) => kbits * 8000
 
     export const aac = (bitrate: number): number => {
       switch (true) {
-      case bitrate > toBits(baseKbitrate):
-        return baseKbitrate
-      default:
-        return -1 // we interpret it as a signal to copy the audio stream as is
+        case bitrate > toBits(baseKbitrate):
+          return baseKbitrate
+
+        default:
+          return -1 // we interpret it as a signal to copy the audio stream as is
       }
     }
 
@@ -405,12 +432,14 @@ namespace audio {
       made here are not made to be accurate, especially with good mp3 encoders.
       */
       switch (true) {
-      case bitrate <= toBits(192):
-        return 128
-      case bitrate <= toBits(384):
-        return 256
-      default:
-        return baseKbitrate
+        case bitrate <= toBits(192):
+          return 128
+
+        case bitrate <= toBits(384):
+          return 256
+
+        default:
+          return baseKbitrate
       }
     }
   }
@@ -476,3 +505,11 @@ async function presetCopy (command: ffmpeg.FfmpegCommand): Promise<ffmpeg.Ffmpeg
     .videoCodec('copy')
     .audioCodec('copy')
 }
+
+
+async function presetAudioSplit (command: ffmpeg.FfmpegCommand): Promise<ffmpeg.FfmpegCommand> {
+  return command
+    .format('mp4')
+    .audioCodec('copy')
+    .noVideo()
+}