NodeJS使用FFMPEG获取视频封面

前言

大多数获取视频的封面都是使用FFMpeg获取视频的第一帧,但是很多视频第一帧是纯黑的,我们就要取后面的帧,这时候我们就要知道视频本身有多长。

获取视频时长

容器时长(container duration)的获取方法:

1
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 -i a.mp4

音视频流时长(stream duration)的获取方法:

1
ffprobe -v error -select_streams v:0 -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 a.mp4

一个媒体文件里边有多个音视频流,各个流的时长也未必一样,一般播放器会以video stream的时长作为播放时长。

生成封面

1
ffmpeg -i a.mp4 -y -f image2 -ss 2 -frames 1 a001.jpg

方式2

1
ffmpeg -i a.mp4 -y -f image2 -ss 2 -t 0.001 a002.jpg

-ss 从几秒开始

NodeJS调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let cp = require('child_process');

const execGetSec = (pathFile) => {
const cmd = `ffprobe -v error -select_streams v:0 -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 ${pathFile}`
console.log('当前指令:', cmd)
cp.exec(cmd, (err, stdout, errout) => {
if (!err) {
console.log('结果:', stdout)
}
})
}

const execJpg = (pathFile, saveFilePath) => {
const cmd = `ffmpeg -i ${pathFile} -y -f image2 -ss 2 -frames 1 ${saveFilePath}`
console.log('当前指令:', cmd)
cp.exec(cmd, (err, stdout, errout) => {
if (!err) {
console.log(`${saveFilePath} success...`)
}
})
}

execGetSec("D:\\Video\\a.mp4");
// execJpg("D:\\Video\\a.mp4", "D:\\Video\\a002.jpg");

封装

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
let cp = require('child_process');

const execGetSec = (pathFile) => {
return new Promise(((resolve) => {
const cmd = `ffprobe -v error -select_streams v:0 -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 ${pathFile}`
cp.exec(cmd, (err, stdout, errout) => {
if (!err) {
resolve(parseInt(stdout))
} else {
resolve(0);
}
})
}))
}

const execJpg = (pathFile, saveFilePath) => {
return new Promise(((resolve, reject) => {
const cmd = `ffmpeg -i ${pathFile} -y -f image2 -ss 2 -frames 1 ${saveFilePath}`
cp.exec(cmd, (err, stdout, errout) => {
if (!err) {
resolve(saveFilePath);
} else {
resolve("")
}
})
}))
}

const execJpgByTime = (pathFile, saveFilePath) => {
return new Promise((async (resolve, reject) => {
let sec = await execGetSec(pathFile);
let fromSec = 0;
if (sec > 5) {
fromSec = 5;
} else {
fromSec = sec / 2;
}
const cmd = `ffmpeg -i ${pathFile} -y -f image2 -ss ${fromSec} -frames 1 ${saveFilePath}`
cp.exec(cmd, (err, stdout, errout) => {
if (!err) {
resolve(saveFilePath);
} else {
resolve("")
}
})
}))
}

async function main() {
let sec = await execGetSec("D:\\Video\\a.mp4");
console.info(sec);

let path = await execJpg("D:\\Video\\a.mp4", "D:\\Video\\a_001.jpg");
console.info(path);

let path2 = await execJpgByTime("D:\\Video\\a.mp4", "D:\\Video\\a_002.jpg");
console.info(path2);
}

main();

导出/导入

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
let cp = require('child_process');

const execGetSec = (pathFile) => {
return new Promise(((resolve) => {
const cmd = `ffprobe -v error -select_streams v:0 -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 ${pathFile}`
cp.exec(cmd, (err, stdout, errout) => {
if (!err) {
resolve(parseInt(stdout))
} else {
resolve(0);
}
})
}))
}

const execJpgByTime = (pathFile, saveFilePath) => {
return new Promise((async (resolve, reject) => {
let sec = await execGetSec(pathFile);
let fromSec = 0;
if (sec > 5) {
fromSec = 5;
} else {
fromSec = sec / 2;
}
const cmd = `ffmpeg -i ${pathFile} -y -f image2 -ss ${fromSec} -frames 1 ${saveFilePath}`
cp.exec(cmd, (err, stdout, errout) => {
if (!err) {
resolve(saveFilePath);
} else {
resolve("");
}
})
}))
}

exports.execJpgByTime = execJpgByTime;

导入

1
2
3
const {
execJpgByTime
} = require("./coverUtil.js")

CentOS安装ffmpeg

YUM安装

首先更新系统。

1
2
sudo yum install epel-release -y
sudo yum update -y

安装Nux Dextop Yum 源

由于CentOS没有官方FFmpeg rpm软件包。但是,我们可以使用第三方YUM源(Nux Dextop)完成此工作。

CentOS 7

1
2
sudo rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm

CentOS 6

1
2
sudo rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm

安装FFmpeg 和 FFmpeg开发包

1
sudo yum install ffmpeg ffmpeg-devel -y

测试是否安装成功

1
2
ffmpeg
ffprobe

Docker内不要添加sudo

1
2
3
4
5
6
# 安装ffmpeg
RUN yum install epel-release -y
RUN yum update -y
RUN rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
RUN rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm
RUN yum install ffmpeg ffmpeg-devel -y

编译安装

不推荐这种方式,编译时间太长。

安装

安装git

1
yum install -y git

安装yasm

1
2
yum -y install centos-release-scl
yum -y install devtoolset-9

先下载源码包:

1
git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg

然后进入ffmpeg文件夹,依次执行下列语句,当然连起来也可以:

1
2
3
cd ffmpeg
./configure --enable-debug=3 --prefix=/usr/local/ffmpeg
make -j8 && make install

时间较长,不出意外会正常安装好。

环境变量

环境变量

1
cd /etc/profile.d/

创建配置文件

1
vi /etc/profile.d/ffmpeg.sh

内容设置为

1
2
3
# ffmpeg
export FFMPEG_HOME=/usr/local/ffmpeg
export PATH=$PATH:$FFMPEG_HOME/bin

配置生效

1
source /etc/profile

查看是否生效

1
cd $FFMPEG_HOME

然后检查版本。

1
2
ffmpeg -version
ffprobe -version

错误处理

如果报错

nasm/yasm not found or too old. Use –disable-x86asm for a crippled build.

下载地址

http://www.tortall.net/projects/yasm/releases/

1
2
3
4
5
6
wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz

tar -zxvf yasm-1.3.0.tar.gz
cd yasm-1.3.0
./configure
make -j8 && make install