fluent-ffmpeg
鉴于ffmpeg
的原生命令行比较复杂, fluent-ffmpeg
提供了一个有好的API用于和FFmpeg
进行交互,主要就是简化了一些命令行调用,让NodeJs
相关应用可以更容易的处理音视频,以下是一些基本用法:
1.视频转码转格式
ffmpeg('input.mp4')
.output('output.mp4')
.videoBitrate('1000k') // 设置视频码流
.fps(30) // 设置帧率
.on('end', () => {
console.log('转换完成');
})
.on('error', (err) => {
console.error('发生错误:', err);
})
.run();
2.调整码流和帧率
ffmpeg('input.mp4')
.output('output.mp4')
.videoBitrate('1000k') // 设置视频码流
.fps(30) // 设置帧率
.on('end', () => {
console.log('转换完成');
})
.on('error', (err) => {
console.error('发生错误:', err);
})
.run();
3.提取音频
ffmpeg('input.mp4')
.output('output.mp3')
.audioCodec('libmp3lame') // 设置音频编码
.on('end', () => {
console.log('音频提取完成');
})
.on('error', (err) => {
console.error('发生错误:', err);
})
.run();
4.合并视频
ffmpeg()
.input('video1.mp4')
.input('video2.mp4')
.output('output.mp4')
.on('end', () => {
console.log('合并完成');
})
.on('error', (err) => {
console.error('发生错误:', err);
})
.run();
5.事件处理
on('end')
:在转换完成时触发。on('error')
:在转换出错时触发,可以获取错误信息。
ffmpeg-static
ffmpeg-static
提供了 FFmpeg
的静态可执行文件,可以轻松地在跨平台应用中使用。下面是如何在一个项目中集成 ffmpeg-static
:
const ffmpeg = require('fluent-ffmpeg');
const ffmpegPath = require('ffmpeg-static');
// 设置 FFmpeg 路径
ffmpeg.setFfmpegPath(ffmpegPath);
const inputFile = 'input.mp4'; // 输入文件
const outputFile = 'output.mp4'; // 输出文件
ffmpeg(inputFile)
.output(outputFile)
.on('end', () => {
console.log('转换完成!');
})
.on('error', (err) => {
console.error('错误: ' + err.message);
})
.run();
优点:
- 多平台应用:开发一个需要
FFmpeg
的跨平台应用,使用ffmpeg-static
可以避免在每个平台上手动配置FFmpeg。
- 快速原型开发:快速测试和原型开发时,可以立即获得
FFmpeg
的功能而无需复杂的设置。
不足:
- 包大小:由于
ffmpeg-static
包含了多个平台的FFmpeg
可执行文件,包的大小可能会比不使用该库要大。 - 功能限制:虽然
ffmpeg-static
提供了FFmpeg
的可执行文件,但它并不提供FFmpeg
的所有功能或完整的文档。你仍然需要参考FFmpeg
的官方文档以了解可用的命令和选项。
举个栗子
主进程中使用 fluent-ffmpeg
处理视频转换,并通过 IPC
发送进度和结果。
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const ffmpeg = require('fluent-ffmpeg');
const path = require('path');
const ffmpegPath = require('ffmpeg-static');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
enableRemoteModule: false,
},
});
win.loadFile('index.html');
}
// 打开文件对话框并返回选择的文件路径
ipcMain.handle('dialog:openFile', async () => {
const result = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Videos', extensions: ['mp4', 'avi', 'mov'] }],
});
return result.filePaths[0];
});
// 处理视频转换
ipcMain.handle('convert-video', async (event, inputFile, outputFile) => {
return new Promise((resolve, reject) => {
ffmpeg(inputFile)
.setFfmpegPath(ffmpegPath) // 设置 FFmpeg 路径
.output(outputFile)
.on('end', () => {
resolve('转换成功!');
})
.on('error', (err) => {
reject(`转换失败: ${err.message}`);
})
.run();
});
});
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
用户界面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Video Converter</title>
</head>
<body>
<h1>视频转换</h1>
<button id="selectFile">选择视频文件</button>
<input type="text" id="outputFile" placeholder="输出文件名(包括格式)" />
<button id="convert">转换视频</button>
<script src="renderer.js"></script>
</body>
</html>
渲染进程IPC
const { ipcRenderer } = require('electron');
document.getElementById('selectFile').addEventListener('click', async () => {
const filePath = await ipcRenderer.invoke('dialog:openFile');
document.getElementById('outputFile').value = filePath.replace(/\.[^/.]+$/, '.mp4'); // 默认输出为 mp4
});
document.getElementById('convert').addEventListener('click', async () => {
const inputFile = document.getElementById('outputFile').value;
const outputFile = document.getElementById('outputFile').value.replace(/\.[^/.]+$/, '.mp4');
try {
const message = await ipcRenderer.invoke('convert-video', inputFile, outputFile);
alert(message);
} catch (error) {
alert('错误: ' + error);
}
});
具体
fluent-ffmpeg
里有没有调用新进程这个,有时间或者有业务需要看看源码就知道了,因为总感觉需要child_process
开个进程心里才稳当。