1
- import { FFMPEGCodecAudio , FFMPEGCodecVideo } from '../shared'
1
+ import {
2
+ FFMPEG_TIME_PATTERN ,
3
+ FFmpegCodecAudio ,
4
+ FFmpegCodecSubtitle ,
5
+ FFmpegCodecVideo ,
6
+ FFmpegStrictOption ,
7
+ } from '../shared'
2
8
import { IOPath } from '../shared/type'
3
9
import { exec } from './process'
4
10
@@ -15,67 +21,225 @@ export async function addAudioToVideoWithFFMPEG({
15
21
inputVideoPath,
16
22
inputAudioPath,
17
23
outputPath,
24
+ audioCodec = 'aac' ,
18
25
fit,
19
26
} : {
20
27
inputVideoPath : string
21
28
inputAudioPath : string
22
29
outputPath : string
23
30
fit ?: boolean
31
+ audioCodec ?: FFmpegCodecAudio
24
32
} ) {
25
33
return await exec (
26
- `ffmpeg -y -loglevel warning -hide_banner -nostats -i "${ inputVideoPath } " -i "${ inputAudioPath } " ${
34
+ `ffmpeg -y -loglevel error -hide_banner -nostats -i "${ inputVideoPath } " -i "${ inputAudioPath } " ${
27
35
fit ? '-shortest ' : ''
28
- } -c:v copy -c:a aac "${ outputPath } "`,
36
+ } -c:v copy -c:a ${ audioCodec } "${ outputPath } "`,
29
37
)
30
38
}
31
39
32
- export async function convertVideoToAudioWithFFMPEG ( {
40
+ export async function convertVideoToAudioWithFFmpeg ( {
33
41
inputPath,
34
42
outputPath,
35
43
} : IOPath ) {
36
44
// output.mp3
37
45
return await exec ( `ffmpeg -i "${ inputPath } " -vn ${ outputPath } ` )
38
46
}
39
47
40
- export async function compressMP4WithFFMPEG ( {
48
+ export async function compressMP4WithFFmpeg ( {
41
49
inputPath,
42
50
outputPath,
43
51
audioCodec = 'aac' ,
44
52
videoCodec = 'h264' ,
45
53
} : IOPath & {
46
- audioCodec ?: FFMPEGCodecAudio
47
- videoCodec ?: FFMPEGCodecVideo
54
+ audioCodec ?: FFmpegCodecAudio
55
+ videoCodec ?: FFmpegCodecVideo
48
56
} ) {
49
- return await exec (
50
- `ffmpeg -y -loglevel warning -hide_banner -nostats -i "${ inputPath } " -strict -2 -vcodec ${ videoCodec } -acodec ${ audioCodec } "${ outputPath } "` ,
51
- )
57
+ return await convertVideoWithFFmpeg ( {
58
+ inputPath,
59
+ outputPath,
60
+ audioCodec,
61
+ videoCodec,
62
+ } )
63
+ }
64
+
65
+ // ffmpeg -filters
66
+ // https://ffmpeg.org/ffmpeg-filters.html
67
+ // https://opensource.com/article/17/6/ffmpeg-convert-media-file-formats
68
+ // -ss 00:01:00 (HOURS:MM:SS.MILLISECONDS)
69
+ // You can also use the -an and -sn flags in the same manner to strip out audio and subtitle streams.
70
+ // https://img.ly/blog/ultimate-guide-to-ffmpeg/#example-material
71
+ // https://ottverse.com/change-resolution-resize-scale-video-using-ffmpeg/
72
+ // https://creatomate.com/blog/how-to-change-the-resolution-of-a-video-using-ffmpeg
73
+ // https://creatomate.com/blog/use-nodejs-to-generate-instagram-youtube-or-tiktok-videos
74
+ // -q:a
75
+ // wav to mp3
76
+ // ffmpeg -i audio.wav -acodec libmp3lame audio.mp3
77
+ // ogg to mp3
78
+ // ffmpeg -i audio.ogg -acodec libmp3lame audio.mp3
79
+ // ac3 to mp3
80
+ // ffmpeg -i audio.ac3 -acodec libmp3lame audio.mp3
81
+ // aac to mp3
82
+ // ffmpeg -i audio.aac -acodec libmp3lame audio.mp3
83
+ export async function convertVideoWithFFmpeg ( {
84
+ inputPath,
85
+ outputPath,
86
+ audioCodec,
87
+ videoCodec,
88
+ audioBitRate,
89
+ videoBitRate,
90
+ frameRate,
91
+ startTime,
92
+ endTime,
93
+ strict = 'strict' ,
94
+ scaleWidth,
95
+ scaleHeight,
96
+ audioChannels,
97
+ audioSamplingFrequency,
98
+ subtitleCodec,
99
+ duration,
100
+ rotate,
101
+ } : IOPath & {
102
+ audioCodec ?: FFmpegCodecAudio
103
+ videoCodec ?: FFmpegCodecVideo
104
+ audioBitRate ?: number
105
+ videoBitRate ?: number
106
+ frameRate ?: number
107
+ startTime ?: string
108
+ endTime ?: string
109
+ strict ?: FFmpegStrictOption
110
+ scaleWidth ?: number
111
+ scaleHeight ?: number
112
+ audioChannels ?: number
113
+ audioSamplingFrequency ?: number
114
+ subtitleCodec ?: FFmpegCodecSubtitle
115
+ duration ?: string
116
+ rotate ?: number
117
+ } ) {
118
+ if ( startTime && ! startTime . match ( FFMPEG_TIME_PATTERN ) ) {
119
+ throw new Error ( 'Start time value is incorrectly formatted.' )
120
+ }
121
+ if ( endTime && ! endTime . match ( FFMPEG_TIME_PATTERN ) ) {
122
+ throw new Error ( 'End time value is incorrectly formatted.' )
123
+ }
124
+ if ( duration && ! duration . match ( FFMPEG_TIME_PATTERN ) ) {
125
+ throw new Error ( 'Duration value is incorrectly formatted.' )
126
+ }
127
+
128
+ const command = [ 'ffmpeg -y -loglevel error -hide_banner -nostats' ]
129
+ command . push ( `-i "${ inputPath } "` )
130
+
131
+ if ( startTime ) {
132
+ command . push ( `-ss ${ startTime } ` )
133
+ }
134
+
135
+ if ( endTime ) {
136
+ command . push ( `-to ${ endTime } ` )
137
+ }
138
+
139
+ if ( duration ) {
140
+ command . push ( `-t ${ duration } ` )
141
+ }
142
+
143
+ if ( strict ) {
144
+ command . push ( `-strict ${ strict } ` )
145
+ }
146
+
147
+ if ( audioChannels ) {
148
+ command . push ( `-ac ${ audioChannels } ` )
149
+ }
150
+
151
+ if ( audioSamplingFrequency ) {
152
+ command . push ( `-ar ${ audioSamplingFrequency } ` )
153
+ }
154
+
155
+ if ( scaleWidth ) {
156
+ if ( scaleHeight ) {
157
+ command . push ( `-filter:v "scale=${ scaleWidth } :${ scaleHeight } "` )
158
+ } else {
159
+ command . push ( `-filter:v "scale=${ scaleWidth } :-1"` )
160
+ }
161
+ } else if ( scaleHeight ) {
162
+ command . push ( `-filter:v "scale=-1:${ scaleHeight } "` )
163
+ } else if ( rotate ) {
164
+ command . push ( `-filter:v "rotate=${ rotate } ` )
165
+ }
166
+
167
+ if ( audioBitRate ) {
168
+ command . push ( `-b:a ${ audioBitRate } ` )
169
+ }
170
+
171
+ if ( videoBitRate ) {
172
+ command . push ( `-b:v ${ videoBitRate } ` )
173
+ }
174
+
175
+ if ( frameRate ) {
176
+ command . push ( `-r ${ frameRate } ` )
177
+ }
178
+
179
+ if ( videoCodec ) {
180
+ command . push ( `-c:v ${ videoCodec } ` )
181
+ }
182
+
183
+ if ( audioCodec ) {
184
+ command . push ( `-c:a ${ audioCodec } ` )
185
+ }
186
+
187
+ if ( subtitleCodec ) {
188
+ command . push ( `-s:a ${ subtitleCodec } ` )
189
+ }
190
+
191
+ command . push ( `"${ outputPath } "` )
192
+
193
+ return await exec ( command . join ( ' ' ) )
52
194
}
53
195
54
- export type ConvertMP4ToGifWithFFMPEG = IOPath & {
196
+ export async function compressVideoWithFFmpeg ( ) { }
197
+
198
+ export type ConvertMP4ToGifWithFFmpeg = IOPath & {
55
199
fps ?: number
56
200
width : number
57
- startTimeInSeconds ?: number
58
- duration ?: number
201
+ startTime ?: string
202
+ endTime ?: string
203
+ duration ?: string
59
204
}
60
205
61
- export async function convertMP4ToGifWithFFMPEG ( {
206
+ export async function convertMP4ToGifWithFFmpeg ( {
62
207
inputPath,
63
208
outputPath,
64
209
fps = 10 ,
65
210
width,
66
- startTimeInSeconds,
211
+ startTime,
212
+ endTime,
67
213
duration,
68
- } : ConvertMP4ToGifWithFFMPEG ) {
214
+ } : ConvertMP4ToGifWithFFmpeg ) {
69
215
fps = fps || 10
216
+
70
217
let cmd = [
71
- `ffmpeg -y -loglevel warning -hide_banner -nostats -i "${ inputPath } "` ,
218
+ `ffmpeg -y -loglevel error -hide_banner -nostats -i "${ inputPath } "` ,
72
219
]
73
- if ( startTimeInSeconds ) {
74
- cmd . push ( `-ss ${ startTimeInSeconds } ` )
220
+
221
+ if ( startTime && ! startTime . match ( FFMPEG_TIME_PATTERN ) ) {
222
+ throw new Error ( 'Start time value is incorrectly formatted.' )
223
+ }
224
+ if ( endTime && ! endTime . match ( FFMPEG_TIME_PATTERN ) ) {
225
+ throw new Error ( 'End time value is incorrectly formatted.' )
226
+ }
227
+ if ( duration && ! duration . match ( FFMPEG_TIME_PATTERN ) ) {
228
+ throw new Error ( 'Duration value is incorrectly formatted.' )
75
229
}
230
+
231
+ if ( startTime ) {
232
+ cmd . push ( `-ss ${ startTime } ` )
233
+ }
234
+
235
+ if ( endTime ) {
236
+ cmd . push ( `-to ${ endTime } ` )
237
+ }
238
+
76
239
if ( duration ) {
77
240
cmd . push ( `-t ${ duration } ` )
78
241
}
242
+
79
243
cmd . push (
80
244
`-vf "fps=${ fps } ,scale=${ width } :-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 "${ outputPath } "` ,
81
245
)
0 commit comments