本文做者郭文涛,奇舞团前端开发工程师。html
阅读本文你将得到如下知识:前端
了解视频的基本原理。node
了解 FFmpeg 是什么,和一些经常使用的用法。nginx
用 FFmpeg 搭建简单的视频直播推流。git
FFmpeg 在 NodeJS 中的一些用法。程序员
短视频大行其道的年代,做为程序员势必须要了解:视频编辑背后的原理和技术。本文简略的描述了视频的组成原理和经常使用的视频编辑工具,以及在 NodeJS
中的用法。github
想要了解视频原理,首先应该从图像原理开始提及。算法
图像画面由一个数字序列表示的图像中的一个最小单位色块,被称之像素(pixel/px)。docker
注意:像素只有位图才会有,是用来记录位图图像的。shell
咱们所说的图像大小为1920*1080
,指的就是长宽各有 1920 和 1080 的像素点,那么一张1920*1080
的图片总共有的像素点为:1920*1080 = 2073600
个像素点。
图像的大小:像素数量 * 像素大小 = 图片大小
,而 像素大小 和 像素深度有关系。RGB
表示的真彩色能表示256×256×256=16,777,216
,就是咱们常见的1600万色
,是人眼可见的所有色彩,超出没有意义。RGB
的像素深度有1bit、4bit、8bit、16bit、24bit、32bit
,如在ps
中下图在新建一张画布选择8bit
(指 rgb 每种颜色占 8bit),那这样1 px = 3 * 8bit = 24bit
,俗称24 位图。根据以上公式就能算出以下图图像的大小:500 * 378 * 24 / 8 = 567000Byte = 567000Byte / 1024 = 553.7109375 Kb
,和 ps 显示的图像大小同样。
但每每真实的图片大小远比以上计算的结果小不少, 这是由于导出的图片都通过压缩的,关于图片压缩技术可自行搜索学习。
视频就是图片一帧一帧连起来的产物,连起来的越快看着越流畅,用 帧率(就是每秒播放图片的数量 FPS)来衡量视频的流畅度。那么根据图片大小的算法就能算出视频的大小。
视频的大小 = 时长(秒) * 帧率(FPS)* 图片大小
;
那么1920×1280
分辨率, 30FPS,时长 1 秒的视频的大小就是:1920 * 1280 * 24 / 8 * 30 / 1024 / 1024 = 210.9375 M
,那么 1 小时的影片须要:210.9 * 60 * 60 / 1024 = 741.4453125 G
,不由产生疑问,为啥我下载的大片才 1G 多?莫慌,视频要是这么简单,那咱们就太天真了,因此就有了下文 「视频编码」 。
帧(Frame):就是一张静止的画面, 是视频的最小单位。
帧速率(FPS):每秒播放图片的数量。
码率(Bit Rate):视频文件在单位时间内使用的数据流量,决定视频的质量和大小,单位是 kb/s 或者 Mb/s。通常来讲一样分辨率下,视频文件的码流越大,压缩比就越小,画面质量就越高。码流越大,说明单位时间内取样率越大,数据流,精度就越高,处理出来的文件就越接近原始文件,图像质量越好,画质越清晰,要求播放设备的解码能力也越高。
码率的常见三种模式:
- CBR
- 全程码率恒定
- 文件大小可预测
- 编码压力小,直播经常使用
- VBR
- 码率可变
- 简单场景码率低,复杂场景码率高
- CRF
- 固定质量模式
- CRF值越低,视频看起来质量越高
复制代码
视频和音频就像是饭和菜,封装格式就至关于碗。
注意: 下文全部
视频
均表明包含音频的视频
。
常见封装格式有 MP四、AVI、FLV、mov、RMVB、MKV、WMV、3GP、ASF
等。
视频编码是对采用视频压缩算法将一种视频格式转换成另外一种视频格式的描述,音频编码同理。
常见的视频编码格式有:AC-1
、MPEG2/H.262
、VP8
、MPEG4
、VP9
、H.261
、H.263
、H.264
、H.265
等。
常见的音频编码格式有:WMA
、MP3
、AC-3
、AAC
、APE
、FLAC
、WAV
等。
主要是将视频像素数据(RGB,YUV 等)压缩成为视频码流,从而下降视频的数据量,也就是处理像素。
YUV: 和
RGB
同样是一种颜色编码格式,相比RGB
更利于压缩。其中"Y"表示明亮度(Lumina nce 或 Luma),也就是灰阶值;而"U"和"V"表示的则是色度(Chrominance 或 Chroma),做用是描述影像色彩及饱和度,用于指定像素的颜色。
视频压缩分为下面两种类型:
1. 帧内压缩
也叫空间压缩,相似于图像压缩,属于有损压缩算法,达不到很高的压缩比。
2. 帧间压缩
主要是经过记录关键帧,经过压缩关键帧之间连续帧的冗余信息(连续帧内相同的像素区域)的过程。
为了记录关键帧,将视频的画面帧分为三类:
GOP(Group of Pictures)值
编码器将多张图像进行编码后生产成一段一段的 GOP ,每一组 IPB 帧的序列包含多少帧,也就是一个 I 帧结束后须要通过多少帧才能出现下一个 I 帧。因此同码率下 GOP 值越大,B 帧和 P 帧越多,视频质量越高。
在压缩或者解压缩视频的过程用到编解码器(Codec)。总的过程能够:
视频的编码的过程:
下图来源于即时通信网。
视频解码的过程:
音频压缩是在保证信号在听觉方面不产生失真的前提下,对音频数据信号进行尽量大的压缩, 去除冗余信息。冗余信号包含人耳听觉范围外的音频信号以及被掩蔽掉的音频信号等。例如,人耳所能察觉的声音信号的频率范围为 20Hz ~ 20KHz,除此以外的其它频率人耳没法察觉,均可视为冗余信号。此外,根据人耳听觉的生理和心理声学现象,当一个强音信号与一个弱音信号同时存在时,弱音信号将被强音信号所掩蔽而听不见,这样弱音信号就能够视为冗余信号而不用传送。
音频压缩不是今天的主角,想深刻学习可参考以下连接:
FFmpeg is a collection of libraries and tools to process multimedia content such as audio, video, subtitles and related metadata.
简单说就是一个跨平台的视频处理的程序。
整个过程基本能够说成:解复用 => 解码 => 编码 => 复用器。
_______ ______________
| | | |
| input | demuxer | encoded data | decoder
| file | ---------> | packets | -----+
|_______| |______________| |
v
_________
| |
| decoded |
| frames |
|_________|
________ ______________ |
| | | | |
| output | <-------- | encoded data | <----+
| file | muxer | packets | encoder
|________| |______________|
复制代码
FFmpeg 分为 3 个版本:Static、 Shared、 Dev
Mac 安装:
brew install ffmpeg
复制代码
其余安装请参考官网。
它能分别对视频的的各个组成进行编码,它对音视频的编码格式支持也比较全面。例如:对视频容器的转换、音视频的压缩、视频截取、截图、滤镜、音频提取等等,很是强大。
命令行语法:
ffmpeg [全局参数] [输入文件参数] -i [输入文件] [输出文件参数] [输出文件]
复制代码
视频信息:
// 获取视频信息
ffmpeg -i input.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'input2.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2mp41
encoder : Lavf58.29.100
description : Packed by Bilibili XCoder v2.0.2
Duration: 00:08:24.45, start: 0.000000, bitrate: 2180 kb/s // 时长,码率
Stream #0:0(und): Video: hevc (Main) (hev1 / 0x31766568), yuv420p(tv), 1920x1080 [SAR 1:1 DAR 16:9], 2046 kb/s, 25 fps, 25 tbr, 16k tbn, 25 tbc (default) // 第一个流是视频流,编码格式是hevc(封装格式为hev1),每一帧表示为yuv420p,分辨率1920*1080,码率2046kb/s, fps为25。
Metadata:
handler_name : VideoHandler
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default) // 第二个流是音频流,编码格式是aac(封装格式为mp4a)采样率是44100 Hz,声道是立体声,码率92Kbit/s。
Metadata:
handler_name : SoundHandler
复制代码
码率的转换:
ffmpeg -i input.mp4 -b:v 64k -bufsize 64k output.mp4
复制代码
帧率转换:
ffmpeg -i input.mp4 -r 5 output.mp4
复制代码
分辨率转换:
ffmpeg -i input.mp4 -vf scale=480:-1 output.mp4 // 1080p 转为 480p
复制代码
视频倍速:
ffmpeg -i test1 "setpts=PTS/5" test4.mp4 // 视频5倍速转换
fmpeg -i input.mp4 -filter:a "atempo=2.0" 4s.mp4 // 音频2倍速播放
ffmpeg -i input.mp4 -filter_complex "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]" -map "[v]" -map "[a]" -vn 4s.mp4 // 音视频同时2倍速
复制代码
提取音视频:
ffmpeg -i input.mp4 -an output.mp4 //提取视频
ffmpeg -i input.mp4 -vn output.mp3 //提取音频
复制代码
视频比例转换:
ffmpeg -i input.mp4 -aspect 21:9 output.mp4
复制代码
视频容器转换:
ffmpeg -i input.mp4 output.avi
复制代码
视频截图:
ffmpeg -ss 00:00:05 -i input.mp4 -vframes 1 -q:v 5 -f image2 pic-%03d.jpeg
// -ss 00:00:05 从第五秒开始 -vframes 1 只截取1帧 -q:v 5 图片质量1-5
复制代码
视频截取:
ffmpeg -ss 00:00:02 -i input.mp4 -t 6.5 -c copy cut.mp4
ffmpeg -ss 00:00:02 -i input.mp4 -to 00:00:10 -c copy cut.mp4
复制代码
连续图片或视频生成 gif 图:
ffmpeg -i output.mp4 -to 10 -r 30 -vf scale=100:-1 gg.gif // 截取视频某个部分生成gif 100:-1 指定宽度,高度保持原始比例
ffmpeg -r 5 -i pic-%03d.jpeg 11.gif // 多图生成gif
// 图片还可生成视频
ffmpeg -r 20 -i pic-%03d.jpeg gif.mp4
ffmpeg -f concat -i "concat:part1.mp4|part2.mp4|3.mp4|part4.mp4" -c copy output.mp4 // 多个视频拼接成一个
复制代码
图片或视频加滤镜:
// 模糊滤镜
ffmpeg -y -i pic-012.jpeg -vf boxblur=7 blur.jpeg
// 变色
ffmpeg -i pic-012.jpeg -vf colorbalance=rm=1 colorbalance1.jpg // 调整rgb某个维度的权重实现变色。
ffmpeg -i pic-012.jpeg -vf colorchannelmixer=.3:.4:.3:0:.3:.4:.3:0:.3:.4:.3 colorchannelmixer1.jpg // 对rgba四个通道进行从新计算,并分别给定权重比例。
ffmpeg -i pic-012.jpeg -vf hue=h=30:s=1 hue1.jpg // 改变色调,至关在调色板上调色
ffmpeg -i pic-012.jpeg -vf lutyuv="y=negval:u=negval:v=negval" lutyuv1.jpg // lutyuv用于yuv颜色空间
ffmpeg -i pic-012.jpeg -vf negate=0 negate1.jpg // 反转
ffmpeg -i pic-012.jpeg -vf swapuv swapuv1.jpg // UV 互换
ffmpeg -i pic-012.jpeg -vf crop=w=200:h=300:x=500:y=800 crop1.jpg // 裁剪
复制代码
添加水印:
ffmpeg -i input.mp4 -i pic-012.jpeg -filter_complex "[1:v] scale=176:144 [logo];[0:v][logo]overlay=x=0:y=0" out.mp4 //给视频添加图片水印
ffmpeg -i input.mp4 -vf "drawtext=fontsize=100:fontcolor=white:alpha=0.3:text='%{localtime\:%Y\-%m\-%d %H-%M-%S}':y=h-line_h-100:x=(w-text_w)/2" output22.mp4// 添加文字水印
ffmpeg -i input.mp4 -i pic-012.jpeg -filter_complex "[1:v] scale=176:144 [logo];[0:v][logo]overlay=x=0:y=0" out.mp4
ffmpeg -i input.mp4 -vf drawtext="fontsize=100:text='我是水印':fontcolor=green:enable=lt(mod(t\,3)\,1)" interval-sy.mp4
// t 时间,s
// mod(t\,2) 计算t%2
// lt(mod(t\,2)\,1) 若是mod(t\,2)<1,返回1,不然返回0
// enable=lt(mod(t\,2)\,1) 每隔1s显示一次水印,enable=lt(mod(t\,3)\,1) 每隔3s.
复制代码
添加字幕:
// 第一步 用you-get下载B站视频
// 第二步 用 danmaku2ass.py 转换弹幕 https://github.com/m13253/danmaku2ass
// 第三步 能够用ffmpeg转换弹幕
ffpmeg -i input.ass input.srt
// 第四步 给视频添加字幕或弹幕 字幕可添加多个
ffmpeg -i input.mp4 -vf subtitles=input.ass output.mp4
复制代码
为音频添加封面:
ffmpeg -loop 1 -i cover.jpg -i input.mp3 -c:v libx264 -c:a aac -b:a 192k -shortest output.mp4
// -loop 1表示一直循环, -shortest 音频结束视频输出就结束
复制代码
视频画中画:
ffmpeg -re -i input.mp4 -vf "movie=output.mp4,scale = 480*320[test]; [in][test] overlay [out]" -vcodec libx264 videoInvideo.mp4
复制代码
多宫格:
ffmpeg -y -i input.mp4 -i input.mp4 \
-i input.mp4 -i input.mp4 \
-filter_complex "nullsrc=size=640x480[base]; \
[0:v]scale=320x240[topleft]; \
[1:v]scale=320x240[topright]; \
[2:v]scale=320x240[bottomleft]; \
[3:v]scale=320x240[bottomright]; \
[base][topleft]overlay=shortest=1[tmp1]; \
[tmp1][topright]overlay=shortest=1:x=320[tmp2]; \
[tmp2][bottomleft]overlay=shortest=1:y=240[tmp3]; \
[tmp3][bottomright]overlay=shortest=1:x=320:y=240" \
-vcodec libx264 9_video_filtered.flv
// nullsrc建立画布
复制代码
视频压缩:
ffmpeg -i input.mp3 -ab 128 output.mp3 // 压缩音频
ffmpeg -i input.mp4 -vf scale=1280:-1 -c:v libx264 -preset veryslow -crf 24 output.mp4 // 压缩视频
复制代码
视频直播推流:
// 录制视频保存在本地
ffmpeg -f avfoundation -i "1" -vcodec libx264 -preset ultrafast -f h264 -r 30 ~/Downloads/test.h264
// 推送已下载在文件夹的视频
ffmpeg -re -i ~/Downloads/xxx.mp4 -vcodec libx264 -acodec aac -strict -2 -f flv rtmp://localhost:1935/live
// 录制桌面
ffmpeg -f avfoundation -i "1" -vcodec libx264 -preset ultrafast -acodec libfaac -f flv rtmp://localhost:1935/rtmplive/room
// 录制桌面和麦克风
ffmpeg -f avfoundation -i "1:0" -vcodec libx264 -preset ultrafast -acodec libmp3lame -ar 44100 -ac 1 -f flv rtmp://localhost:1935/live/room
// 录制桌面和麦克风,并打开摄像头拍摄
ffmpeg -f avfoundation -framerate 30 -i "1:0" \-f avfoundation -framerate 30 -video_size 640x480 -i "0" \-c:v libx264 -preset ultrafast \-filter_complex 'overlay=main_w-overlay_w-10:main_h-overlay_h-10' -acodec libmp3lame -ar 44100 -ac 1 -f flv rtmp://localhost:2016/rtmplive/room
复制代码
直播 DEMO:
安装支持rtmp
的 docker 镜像:docker pull tiangolo/nginx-rtmp
启动tiangolo/nginx-rtmp
容器:docker run -d -p 1935:1935 --name nginx-rtmp tiangolo/nginx-rtmp
查看nginx
配置: docker exec -it nginx-rtmp /bin/bash
推流地址:rtmp://10.17.8.189:1935/live
Ffmpeg 推流:ffmpeg -f avfoundation -i "1:0" -vcodec libx264 -preset ultrafast -acodec libmp3lame -ar 44100 -ac 1 -f flv rtmp://localhost:1935/live/room
用支持支持rtmp
的播放器(IINA)或者ffplay
打开: ffplay rtmp://10.17.8.189:1935/live
一个简单的直播 demo 就跑起来了。
Fluent-ffmpeg 是将复杂的 ffmpeg 命令抽象成 NodeJS 的模块,前提是系统已安装 FFmpeg。
一些简单的用法
// 视频信息
ffmpeg.ffprobe(input, function (err, metadata) {
console.dir(metadata);
});
// 提取音频
ffmpeg(input)
.audioCodec("libmp3lame")
.on("error", function (err) {
console.log("发生错误: " + err.message);
})
.on("end", function () {
console.log("提取音频完成 🍻🍻!");
})
.save(resOut);
// 提取视频
ffmpeg(input)
.noAudio()
.on("error", function (err) {
console.log("发生错误: " + err.message);
})
.on("end", function () {
console.log("提取视频完成 🍻🍻!");
})
.save(resOut);
复制代码
回顾一下,广义上的视频是由:音频和视频两部分组成,它们分别有对应的各自的编码规范,视频容器是将不一样编码格式的音、视频组合在一块儿的一种封装格式。视频编码格式主要是对视频的大小进行压缩,分为帧内压缩和帧间压缩,帧间压缩主要是经过记录关键帧形式来进行压缩。
FFmpeg
是处理音视频编码的一种程序,主要原理:demuxer => decoder => encoder => muxer
。
Fluent-ffmpeg
是将复杂的 ffmpeg
命令抽象成 nodeJs
的模块,前提是系统已安装 FFmpeg
,这对于前端工程师来讲,能够用它处理众多音视频操做。
下一篇预告:FFmpeg
和 wasm
在浏览器中的碰撞