JS利用ffmpeg和sharp玩轉(zhuǎn)音視頻和圖片
前端時(shí)間剛從 windows 上切換過去 mac 進(jìn)行開發(fā),我想找一個(gè)能錄制 gif 的軟件,但是發(fā)現(xiàn)并沒有比較好的軟件,有的都是收費(fèi)的。
在 mac 上自帶的錄屏倒是挺不錯(cuò)的,所以我就想有沒有什么辦法可以將這些視頻的格式轉(zhuǎn)換為 gif 格式的。
可能是因?yàn)椴⒉皇呛軇傂璋?,并沒有去找解決方案,直到我今天刷到了一篇文章,我就大概看了一下。
使用 ffmpeg 將視頻轉(zhuǎn) GIF 格式
在開始之前我們大概來了解一下什么是 ffmpeg。
ffmpeg 是一個(gè)非常流行的開源軟件套件,用于處理音頻和視頻數(shù)據(jù)。它提供了多種工具集,如 ffmpeg、ffplay 和 ffprobe,這些工具可以用于:
- 轉(zhuǎn)碼:可以將視頻和音頻從一種格式轉(zhuǎn)換為另一種格式。
- 播放:使用
ffplay可以播放多種格式的音頻和視頻文件。 - 提取信息:使用
ffprobe可以獲取關(guān)于音頻和視頻文件的詳細(xì)信息。 - 流處理:可以捕獲和編碼實(shí)時(shí)音頻/視頻流。
- 過濾:可以應(yīng)用各種過濾器來處理音頻和視頻數(shù)據(jù),例如調(diào)整亮度、裁剪、調(diào)整速度等。
ffmpeg 支持多種音頻、視頻、字幕和其他相關(guān)媒體數(shù)據(jù)的格式,它的功能非常豐富且靈活,可以滿足許多媒體處理的需求。由于其強(qiáng)大的功能和開源的特性,ffmpeg 在多個(gè)平臺(tái)和應(yīng)用程序中得到了廣泛的應(yīng)用。
大概了解了之后,我們來看看如何在 node 中實(shí)現(xiàn)視頻轉(zhuǎn) webp 的,首先安裝相關(guān)的依賴包:
pnpm add @ffmpeg-installer/ffmpeg fluent-ffmpeg
然后編寫一下代碼:
const ffmpegPath = require("@ffmpeg-installer/ffmpeg").path;
const ffmpeg = require("fluent-ffmpeg");
const video2Gif = async (videoPath, gifPath, width, fps) => {
ffmpeg.setFfmpegPath(ffmpegPath); // 設(shè)置二進(jìn)制客戶端路徑
ffmpeg(videoPath) // 讀入路徑
.outputOptions("-pix_fmt", "rgb24") // 設(shè)置像素格式為rgb24
.output(gifPath) // 輸出路徑
.withFPS(fps) // 設(shè)置輸出GIF的幀率
.size(`${width}x?`) // 設(shè)置輸出GIF的寬度,高度等比縮放
.noAudio() // 禁用音頻輸出
.setStartTime("00:00:10") // 開始時(shí)間
.setDuration(15) // 持續(xù)時(shí)間
.videoFilters({
filter: "crop",
options: {
out_w: `iw/1.5`, // 裁剪到原始寬度
out_h: "ih", // 高度保持不變
x: "(iw-out_w)/2", // 從中心開始裁剪
y: 0, // y坐標(biāo)保持在頂部
},
})
.on("end", () => {
console.log("轉(zhuǎn)換完成!");
})
.on("error", (err) => console.error(err))
.run();
};
video2Gif("./i.mp4", "./index.gif", 600, 80);
這段代碼利用 ffmpeg 將一個(gè)視頻文件的特定 15 秒段落(從 10 秒開始)轉(zhuǎn)換為 GIF 格式。在轉(zhuǎn)換過程中,視頻的寬度被裁剪到其原始的 2/3,高度保持不變,同時(shí)移除了音頻部分。最后,生成的 GIF 保存到指定的路徑。
這個(gè)時(shí)候我們使用 node 運(yùn)行該文件,內(nèi)容變輸出了。查看文件大小的時(shí)候,我們發(fā)現(xiàn)文件太大了,雖然我們可以在上面調(diào)整參數(shù),但是我偏不哈哈哈哈哈,我就要倔

使用 sharp 壓縮 gif
要想對(duì)圖片之類的進(jìn)行壓縮,我們可以選擇 sharp 來進(jìn)行操作,這里我就不過多說了,直接上代碼:
const sharp = require("sharp");
const fs = require("fs");
async function compressGif(filePath, outputPath) {
const data = await sharp(filePath, {
animated: true,
limitInputPixels: false,
})
.gif({
colours: 60,
})
.toBuffer();
// 寫入Buffer到文件
fs.writeFileSync(outputPath, data);
}
compressGif("./index.gif", "./php.gif");
上面這段代碼使用 sharp 庫(kù)對(duì) GIF 文件進(jìn)行壓縮,將其顏色范圍減少到 60 fps,從而減小文件大小。壓縮后的數(shù)據(jù)被保存為一個(gè) Buffer,然后寫入到新的 GIF 文件路徑。
這個(gè)時(shí)候,gif 被我們壓縮到了 20mb 了,也終于能在掘金上面上傳了。

將任何格式的圖片轉(zhuǎn)換為 webp 格式并添加水印
WebP 是 Google 開發(fā)的圖片格式,它提供比 JPEG 和 PNG 更高效的壓縮,從而達(dá)到更小的文件大小而不犧牲圖像質(zhì)量。同時(shí),它支持透明度和動(dòng)畫,且得到了大多數(shù)現(xiàn)代瀏覽器的支持,使其成為網(wǎng)站和應(yīng)用中優(yōu)化圖像性能的理想選擇。
在下面,我們就來使用 sharp 來實(shí)現(xiàn)這種轉(zhuǎn)換,如下代碼所示:
const sharp = require("sharp");
const fs = require("fs");
const path = require("path");
const sourceDirectory = "./image";
const outputDirectory = "./webp_images";
if (!fs.existsSync(outputDirectory)) {
fs.mkdirSync(outputDirectory);
}
fs.readdir(sourceDirectory, (err, files) => {
if (err) {
console.error("Error reading the directory:", err);
return;
}
files.forEach((file) => {
const extname = path.extname(file).toLowerCase();
const filename = path.basename(file, extname);
const allowedFormats = [
".jpg",
".jpeg",
".png",
".tif",
".tiff",
".bmp",
".gif",
];
if (allowedFormats.includes(extname)) {
const inputFile = path.join(sourceDirectory, file);
const outputFile = path.join(outputDirectory, `${filename}.webp`);
const watermarkSvg = `
<svg width="200" height="80" xmlns="http://www.w3.org/2000/svg">
<text x="10" y="40" font-family="Arial" font-size="30" fill="pink" stroke="black" stroke-width="2">Moment</text>
</svg>
`;
sharp(inputFile)
.webp()
.composite([
{
input: Buffer.from(watermarkSvg),
gravity: "southeast",
},
])
.toFile(outputFile, (err, info) => {
if (err) {
console.error(`Failed to convert and watermark ${file}:`, err);
} else {
console.log(`Converted and watermarked ${file} to WebP format.`);
}
});
}
});
});
在上面這段代碼從一個(gè)指定的文件夾中讀取圖片,將其轉(zhuǎn)換為 WebP 格式并添加一個(gè) SVG 水印在右下角,然后保存轉(zhuǎn)換后的圖片到另一個(gè)文件夾中。
最終輸出結(jié)果如下圖所示:

水印也被加上去了。
總結(jié)
通過巧妙地利用這兩者,我們可以做到平時(shí)很多我們平時(shí)想做的事情,發(fā)揮你的想象,去做一些有意義有想法的事情吧。
到此這篇關(guān)于JS利用ffmpeg和sharp玩轉(zhuǎn)音視頻和圖片的文章就介紹到這了,更多相關(guān)JS ffmpeg sharp內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Taro實(shí)現(xiàn)小程序商城的購(gòu)物車功能模塊的實(shí)例代碼
這篇文章主要介紹了使用Taro實(shí)現(xiàn)的小程序商城的購(gòu)物車功能模塊,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
JavaScript中使用參數(shù)個(gè)數(shù)實(shí)現(xiàn)重載功能
這篇文章主要介紹了JavaScript中使用參數(shù)個(gè)數(shù)實(shí)現(xiàn)重載功能,需要的朋友可以參考下2017-09-09
JS返回上一頁實(shí)例代碼通過圖片和按鈕分別實(shí)現(xiàn)
返回上一頁的方法有很多,本代碼通過圖片和按鈕分別實(shí)現(xiàn),感興趣的朋友可以參考下,希望對(duì)大家有所幫助2013-08-08
解決使用layui對(duì)select append元素?zé)o效或者未及時(shí)更新的問題
今天小編就為大家分享一篇解決使用layui對(duì)select append元素?zé)o效或者未及時(shí)更新的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-09-09
js實(shí)現(xiàn)從數(shù)組里隨機(jī)獲取元素
這篇文章主要介紹了js實(shí)現(xiàn)從數(shù)組里隨機(jī)獲取元素的方法,以及個(gè)人封裝的js代碼分享,十分的實(shí)用,這里推薦給小伙伴們2015-01-01
Radio 單選JS動(dòng)態(tài)添加的選項(xiàng)onchange事件無效的解決方法
radio 單選JS動(dòng)態(tài)添加的選項(xiàng),onchange事件無效。使用delegate()函數(shù)可以解決該問題,具體解決方案大家通過本文詳細(xì)了解下2016-12-12
微信小程序中的店鋪評(píng)分組件及vue中用svg實(shí)現(xiàn)的評(píng)分顯示組件
這篇文章主要介紹了微信小程序之店鋪評(píng)分組件及vue中用svg實(shí)現(xiàn)的評(píng)分顯示組件,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11
JS獲取月的最后一天與JS得到一個(gè)月份最大天數(shù)的實(shí)例代碼
本篇文章主要是對(duì)JS獲取月的最后一天與JS得到一個(gè)月份最大天數(shù)的實(shí)例代碼進(jìn)行了介紹,需要的朋友可以過來參考下,希望對(duì)大家有所幫助2013-12-12

