aboutsummaryrefslogtreecommitdiffhomepage
path: root/shared/ffmpeg/ffmpeg-images.ts
diff options
context:
space:
mode:
Diffstat (limited to 'shared/ffmpeg/ffmpeg-images.ts')
-rw-r--r--shared/ffmpeg/ffmpeg-images.ts61
1 files changed, 47 insertions, 14 deletions
diff --git a/shared/ffmpeg/ffmpeg-images.ts b/shared/ffmpeg/ffmpeg-images.ts
index 2db63bd8b..618fac7d1 100644
--- a/shared/ffmpeg/ffmpeg-images.ts
+++ b/shared/ffmpeg/ffmpeg-images.ts
@@ -1,4 +1,5 @@
1import { FFmpegCommandWrapper, FFmpegCommandWrapperOptions } from './ffmpeg-command-wrapper' 1import { FFmpegCommandWrapper, FFmpegCommandWrapperOptions } from './ffmpeg-command-wrapper'
2import { getVideoStreamDuration } from './ffprobe'
2 3
3export class FFmpegImage { 4export class FFmpegImage {
4 private readonly commandWrapper: FFmpegCommandWrapper 5 private readonly commandWrapper: FFmpegCommandWrapper
@@ -36,24 +37,56 @@ export class FFmpegImage {
36 37
37 async generateThumbnailFromVideo (options: { 38 async generateThumbnailFromVideo (options: {
38 fromPath: string 39 fromPath: string
39 folder: string 40 output: string
40 imageName: string
41 }) { 41 }) {
42 const { fromPath, folder, imageName } = options 42 const { fromPath, output } = options
43 43
44 const pendingImageName = 'pending-' + imageName 44 let duration = await getVideoStreamDuration(fromPath)
45 if (isNaN(duration)) duration = 0
45 46
46 const thumbnailOptions = { 47 this.commandWrapper.buildCommand(fromPath)
47 filename: pendingImageName, 48 .seekInput(duration / 2)
48 count: 1, 49 .videoFilter('thumbnail=500')
49 folder 50 .outputOption('-frames:v 1')
51 .output(output)
52
53 return this.commandWrapper.runCommand()
54 }
55
56 async generateStoryboardFromVideo (options: {
57 path: string
58 destination: string
59
60 sprites: {
61 size: {
62 width: number
63 height: number
64 }
65
66 count: {
67 width: number
68 height: number
69 }
70
71 duration: number
50 } 72 }
73 }) {
74 const { path, destination, sprites } = options
75
76 const command = this.commandWrapper.buildCommand(path)
51 77
52 return new Promise<string>((res, rej) => { 78 const filter = [
53 this.commandWrapper.buildCommand(fromPath) 79 `setpts=N/round(FRAME_RATE)/TB`,
54 .on('error', rej) 80 `select='not(mod(t,${options.sprites.duration}))'`,
55 .on('end', () => res(imageName)) 81 `scale=${sprites.size.width}:${sprites.size.height}`,
56 .thumbnail(thumbnailOptions) 82 `tile=layout=${sprites.count.width}x${sprites.count.height}`
57 }) 83 ].join(',')
84
85 command.outputOption('-filter_complex', filter)
86 command.outputOption('-frames:v', '1')
87 command.outputOption('-q:v', '2')
88 command.output(destination)
89
90 return this.commandWrapper.runCommand()
58 } 91 }
59} 92}