Nodejs脚本系列——视频文件转码

最近使用nodejs些了很多实用的脚本,可以很方便的做一些自动化操作。

1
#!/usr/bin/env node

只要在nodejs文件的首行加上这一句,该脚本就可以想普通的shell脚本一样直接执行而不需要使用node xxx.js了,当然还是需要安装node的


今天介绍一个使用ffmpeg批量转换视频文件格式的脚本,该脚本对ffmpeg命令做了简单封装,支持单个文件以及整个文件夹全部转换,并输出实时进度,目前实现了大部分格式向mp4格式转换的命令。

贴上GitHub地址 ffmpeg2mp4 使用方法里面也有,这里就不多做介绍了。

脚本很简单,就是对原生的 ffmpeg 命令做了下封装,让其依次进行每个文件的转换,中间输出进度百分比,完成后输出转换结果。

贴上完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/usr/bin/env node

/**
* 使用ffmpeg转换文件格式的自动化脚本,
* 对ffmpeg命令做了简单封装,
* 支持单个文件以及整个文件夹全部转换,
* 输出实时进度。
*
* Created by lonord on 2016/10/9.
*/

const fs = require('fs');
const path = require('path');
const spawn = require('child_process').spawn;
const async = require('async');

const ffmpegPath = '/usr/local/bin/ffmpeg';

function main() {
if (process.argv.length < 3) {
console.log('缺少参数!');
console.log('用法: ffmpeg2mp4 输入文件/目录 [输出路径]');
return;
}
let source = process.argv[2];
let targetDir;
let sourceStat = fs.statSync(source);
let files = [];
if (sourceStat.isDirectory()) {
targetDir = source;
let names = fs.readdirSync(source);
names.forEach((name) => {
let p = path.join(source, name);
let fileStat = fs.statSync(p);
if (fileStat.isFile()) {
files.push(p);
}
});
}
else if (sourceStat.isFile()) {
targetDir = path.dirname(source);
files.push(source);
}
else {
throw new Error(`输入类型错误 ${source}`);
}
if (process.argv.length >= 4) {
targetDir = process.argv[3];
}
let index = 0;
let successCount = 0;
let failCount = 0;
async.whilst(
function () { return index < files.length; },
function (callback) {
let file = files[index];
let fileName = path.basename(file);
console.log(`开始转换 ${fileName} (${index + 1}/${files.length})...`);
transCodeFile(file, targetDir, (percent) => {
process.stdout.write(`\r进度: ${percent}%`);
}, (success) => {
if (success) {
process.stdout.write('\r转换成功!\n');
successCount++;
}
else {
failCount++;
process.stdout.write('\r转换失败!\n');
}
callback();
});
index++;
},
function () {
console.log(`全部转换结束 成功:${successCount} 失败:${failCount}`);
console.log(`输出目录:${targetDir}`);
}
);
}

/**
* 转换文件格式函数
* @param file 输入文件绝对路径
* @param targetDir 输出文件目录绝对路径
* @param progressCallback 报告进度回调函数
* @param completeCallback 完成回调函数
*/
function transCodeFile(file, targetDir, progressCallback, completeCallback) {
let outPath = path.join(targetDir, path.basename(file));
outPath = outPath.substr(0, outPath.length - path.extname(outPath).length) + '.mp4';
const ffmpeg = spawn(ffmpegPath, ['-i', file, '-y', outPath]);
let totalTime = 0;
ffmpeg.stderr.on('data', (data) => {
let dataStr = data + '';
let durIdx = dataStr.indexOf('Duration:');
if (durIdx > 0) {
let durStr = dataStr.substr(durIdx + 10, 11);
totalTime = parseTime(durStr);
}
let timeIdx = dataStr.indexOf(' time=');
if (timeIdx > 0) {
let timeStr = dataStr.substr(timeIdx + 6, 11);
let time = parseTime(timeStr);
if (time <= totalTime) {
let per = Math.floor(time * 100 / totalTime);
progressCallback(per);
}
}
});
ffmpeg.on('exit', (code) => {
if (code == 0) {
completeCallback(true);
}
else {
completeCallback(false);
}
});

/**
* 将时间由 01:23:45.67 的形式转换为整数
* @param durStr
* @returns {number}
*/
function parseTime(durStr) {
let time = parseInt(durStr.substr(0, 2)) * 3600 * 100;
time += parseInt(durStr.substr(0, 2)) * 3600 * 100;
time += parseInt(durStr.substr(3, 2)) * 60 * 100;
time += parseInt(durStr.substr(6, 2)) * 100;
time += parseInt(durStr.substr(9, 2));
return time;
}
}

try {
main();
}
catch(e) {
console.log(e.message);
}

首先使用 process.argv 对输入参数进行判断

然后使用 fs 模块判断输入输出路径,获取到待转换的文件

transCodeFile() 这个函数就是核心,通过 child_process 模块下的 spawn 方法调起系统中的ffmpeg进程

ffmpeg的命令参数如下

1
ffmpeg -i input -y output

加上参数 -y 是覆盖已经存在重名文件,ffmpeg会根据 output 的后缀来判断目标文件格式

还有一点就是进度的输出,ffmpeg在转换的时候会输出特定的信息,我使用的方法就是对ffmpeg输出的信息不断检索,开始转换前获取到视频文件的总时间,类似 01:23:45.67 这种格式,分别是 时:分:秒.十毫秒,在转换途中不断获取已经转换的时间,计算出百分比,原理很简单,看一下ffmpeg输出信息就懂了

最后,这个脚本并不是很完善,大家有需要可以把脚本下载下来自己再修改一下,当然也很希望大家把修改后的代码再提交上来,方便更多的人,哈哈~