Edit Video using FFmpeg
Yêu cầu
bài toán:
- merge nhiều ảnh tĩnh thành 1 video
- merge 1 video A vào 1 video B
- merge 1 file mp3 tạo thành background music vào 1 video A
- chỉnh âm lượng của background music từ nhỏ tới lớn dần
- vẽ 1 dòng text lên 1 video
- hiển thị dòng text trên video với hiệu ứng: chạy từ trái qua phải, chạy zoom out, zoom in
sử dụng:
https://github.com/kkroening/ffmpeg-python
https://github.com/binoculars/aws-lambda-ffmpeg
https://github.com/cmxl/FFmpeg.NET
https://github.com/hokiedsp/node-kiss-ffmpeg
https://github.com/fluent-ffmpeg/node-fluent-ffmpeg
Giải pháp
- Mỗi yêu cầu trả lời những câu hỏi sau
- Có làm được không?
- Làm như thế nào?
- Có hạn chế gì không?
- Giải pháp nào khác cho những hạn chế ở trên?
- Sau khi trả lời những câu hỏi trên, chốt stack sẽ sử dụng?
- Document lại những gì đã tìm hiểu
- Triển demo xác nhận lại những thứ ở trên
Demo
- Thiết lập môi trường
- Ví dụ đơn giản cho từng case ở trên
- Ví dụ phức tạp, mix nhiều case (mix cố định không cần tùy chỉnh)
Demo
Package : https://www.npmjs.com/package/fluent-ffmpeg - Thiết lập môi trường (nodejs) - Ví dụ đơn giản cho từng case ở trên - Ví dụ phức tạp, mix nhiều case (mix cố định không cần tùy chỉnh)
Thực hiện
- Chú ý : Resize image hoặc video theo cùng 1 kích sỡ trước
+ image
const outputOptions = {
filter: "scale",
options: { w: width, h: height, force_original_aspect_ratio: "decrease" },
};
+ video
Ffmpeg(videoPath)
.outputFormat("mp4")
.videoCodec("libx264")
.audioCodec("aac")
.size(`${width}x${height}`)
.fps(30)
.save(outputVideoPath)
+ video có backgroud là transparent:
``
.outputOptions([-vf scale=${width}:${height}`,
"-c:v prores_ks",
"-pix_fmt yuva444p10le",
])
```
-
merge nhiều ảnh tĩnh thành 1 video
- Để toàn bộ ảnh trong 1 folder (lưu ý tên phải đồng bộ vidu: image_1.jpg)
- Convert tên file => image_%d.jpg
- Option cơ bản để tạo 1 video
fps: 25, inputFPS: 1 / 5, transition: true, transitionDuration: 1, videoCodec: "libx264", outputOptions: ["-pix_fmt yuv420p"], complexFilter:["scale=1280:720"]or - Chuyển image thành video => merge các video đó lại
const video = Ffmpeg({ source: imagePath }); video .inputOptions("-loop 1") .outputOptions("-c:v libx264") .outputOptions("-t 6") .outputOptions("-pix_fmt yuv420p") .videoFilters("scale=720:720") .save(outputVideoPath) -
merge 1 video A vào 1 video B (có thể n video thành 1 video)
- option cơ bản
-y -filter_complex [0:v:0][0:a:0][1:v:0][1:a:0]concat=n=2:v=1:a=1[v][a] -acodec aac -vcodec libx264 -map [v] map [a]With fluent-ffmpeg`` const filterComplex = [ { filter: "concat", options: { n: videoPaths.length, v: 1, a: 1, }, inputs: videoPaths.flatMap((_, index) => [${index}:v:0,${index}:a:0`]), outputs: ["v", "a"], }, ];
mergedVideo .complexFilter(filterComplex) .outputOptions(["-map", "[v]", "-map", "[a]"]) .videoCodec("libx264") .audioCodec("aac")
orconst mergedVideo = Ffmpeg(); videoPaths.forEach((path) => mergedVideo.input(path));mergedVideo .outputFormat("mp4") .mergeToFile(outputVideoPath)
- merge 1 file mp3 tạo thành background music vào 1 video A - chỉnh âm lượng của background music từ nhỏ tới lớn dần - Option cơ bản `volumeFactor:volume (min:0 -> max:1)`.audioFilters(volume=${volumeFactor}) .outputOptions("-c:v", "copy", "-c:a", "aac", "-strict", "experimental") ``` - option cơ bản
-
vẽ 1 dòng text lên 1 video
- Option cơ bản (vị trí , màu , kích cỡ , font ,...)
default: x=0, y=0 Position: -top-left : "x=${x}:y=${x}" -top-center : "x=(w-text_w)/2:y=${y}" -top-right : "x=w-tw-${x}:y=${y}" -center : "x=(w-text_w)/2:y=(h-text_h)/2" -bottom-left : "x=${x}:y=h-th-${y}" -bottom-right : "x=(w-text_w)/2:y=h-th-${y}" -bottom-center : "x=w-tw-${x}:y=h-th-${y}" .outputOptions([ "-vf", `drawtext=text='${text}':${positionText}:fontsize=${fontSize}:fontcolor=${color}`, ])or.videoFilters({ filter: 'drawtext', options: { fontfile: '/path/to/your/font.ttf', text: text, fontcolor: 'white', fontsize: '24', x: '(w-text_w)/2', y: '(h-text_h)/2', } }) - Multiple lines break
Trường hợp cho nhập nhiều dòng
Chỉ định nhiều 'drawtext'ffmpeg -I file_source_path.extension -vf "drawtext=fontfile=path/to/font/:fontsize=30:fontcolor=white:x=(w-text_w)/2:y=(h-text_h-text_h)/2:text='custom-text',drawtext=fontfile=/path/to/font:fontsize=30:fontcolor=white:x= (w-text_w)/2:y=(h+text_h)/2:text='custom-text'" file_destination_path.extension
- Option cơ bản (vị trí , màu , kích cỡ , font ,...)
-
hiển thị dòng text trên video với hiệu ứng: chạy từ trái qua phải, chạy zoom out, zoom in
- option tương tự vẽ 1 dòng text
- chạy từ trái qua phải :
x='(mod(${speed}*n,w+tw)-tw)':y=h/2với speed là tốc độ của hiệu ứng - chạy zoom out, zoom in,
- thay đổi fontsize text:
fontsize: '24+2*t', t là thơi gian - tao ảnh với text ở trung tâm và background là transparent -> affect giống như option của ảnh trên video
- thay đổi fontsize text:
- chạy từ trái qua phải :
- option tương tự vẽ 1 dòng text
-
sử lý ảnh
-
blur:
ffmpeg -ss 0 -i Image-25-overlayImage.png -y -filter_complex [0:v]avgblur=10[bg];[0:v]crop=370:450:x:y[fg];[bg][fg]overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2 -acodec copy -vframes 25 -movflags faststart Image-29-blur.png -
mix hiệu ứng
-
fade in , out :
[ '[1]format=yuva420p,fade=in:st=8:d=1:alpha=1[in],[in]fade=out:st=13:d=1:alpha=1[fadeOut]', "[0:v][fadeOut]overlay=x='if(lte(t,8), (W-overlay_w)/2 + 20, max((W-overlay_w)/2 + 20 - (t-9) * 20, 0 - overlay_w))':y='(main_h-overlay_h)/2':enable='between(t,8,14)':shortest = 1" ] -
fade in, out + text :
[
'[1]format=yuva420p,fade=in:st=28:d=1:alpha=1[in],[in]fade=out:st=33:d=1:alpha=1[fadeOut]',
"[0:v][fadeOut]overlay=x='(main_w-overlay_w)/2':y='(main_h-overlay_h)/2':shortest=1[overlay]",
"[overlay]drawtext=fontfile=D:\\01_SGT_WORK\\FFMPEG\\Ffmpeg\\font\\mincho.otf:text='text in bottom center':fontsize=30:fontcolor=white:x=(w-text_w)/2:y=h-th-20:enable='between(t, 29, 33 )'"
]
+
- khó khăn đang gặp:
- Khi mix các Animation để tạo video, bị conflict với các option trước đó
- Giải pháp hiện tại , chia nhỏ các animation ra rồi gộp lại, nhưng vẫn chưa ok , phải test nhiều chỉnh option phù hợp
- Chưa tìm ra cách giải quyết cho các hiệu ứng smooth , với đúng yêu cầu đề bài