0 背景
在编译FreeSwitch的时候,我们顺手编译了一下作为依赖的FFmpeg,除了提供libavformat和libswscale这两个库之外,FFmpeg还有其他很多有趣的功能,这次我们来玩一下FFmpeg的推流功能,主要是RTMP和RTP这两种协议。
1 编译Nginx
在常规情况下需要一个web服务器作为RTMP服务器,设备A将数据流推送到RTMP服务器上,设备B再从RTMP服务器上拉取数据流播放。
一般来说可以使用Nginx作为RTMP服务器,由于需要额外的RTMP模块,因此需要我们重新编译一份Nginx。
我现在有一台虚拟机,一台宿主机,计划在虚拟机上搭Nginx服务以及作为设备A进行推流,宿主机内使用VLC和Wireshark进行抓包。
1
2
3
4
5
6
7
8
9
|
# 下载源码
git clone https://github.com/nginx/nginx.git
git clone https://github.com/arut/nginx-rtmp-module.git
# 编译并安装
cd nginx
./auto/configure --with-http_ssl_module --add-module=../nginx-rtmp-module
make
make install 2>&1 | tee make.log
|
2 配置Nginx的RTMP服务器
1
2
3
4
5
6
7
8
9
10
11
12
13
|
vim /usr/local/nginx/conf/nginx.conf
# 在尾部添加:
rtmp {
server {
listen 1935; # 默认的RTMP端口
chunk_size 4096;
application live {
live on;
record off; # 不保存录制
}
}
}
|
在添加了这么个RTMP服务配置之后就可以启动nginx了:
1
|
/usr/local/nginx/sbin/nginx
|
3 推流及测试
3.1 RTMP测试
使用这个命令来推送RTMP数据流:
1
2
|
# 服务器地址还是填虚拟机地址
ffmpeg -re -i input.mp4 -c copy -f flv rtmp://your_server_ip/live/stream
|
如果直接执行上面这条指令的话会出现如下报错:
1
|
error while loading shared libraries: libavdevice.so.57: cannot open shared object file: No such file or directory
|
我们之前编译ffmpeg中的lib并没有被系统识别到,因此导致了问题。
之前编译freeswitch的时候是通过导入环境变量这种一次性手段解决的,为了一劳永逸解决这个问题,我们选择下面的方法:
这个方法只是在CentOS 7.9中实验过,别的系统自行测试
ffmpeg编译出来的so文件默认位于/usr/local/lib
中,系统并不读取这个路径,所以我们需要在配置文件中手动添加一个:
1
2
3
4
5
6
7
|
vim /etc/ld.so.conf.d/usr.conf
# 在其中添加刚刚的路径:
/usr/local/lib
# 编辑完成后保存退出并更新动态库缓存
ldconfig
|
下来可以测试一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
[root@localhost ~]# ldconfig -p| grep av
libxcb-screensaver.so.0 (libc6,x86-64) => /lib64/libxcb-screensaver.so.0
libavutil.so.55 (libc6,x86-64) => /usr/local/lib/libavutil.so.55
libavutil.so (libc6,x86-64) => /usr/local/lib/libavutil.so
libavresample.so.3 (libc6,x86-64) => /usr/local/lib/libavresample.so.3
libavresample.so (libc6,x86-64) => /usr/local/lib/libavresample.so
libavformat.so.57 (libc6,x86-64) => /usr/local/lib/libavformat.so.57
libavformat.so (libc6,x86-64) => /usr/local/lib/libavformat.so
libavfilter.so.6 (libc6,x86-64) => /usr/local/lib/libavfilter.so.6
libavfilter.so (libc6,x86-64) => /usr/local/lib/libavfilter.so
libavdevice.so.57 (libc6,x86-64) => /usr/local/lib/libavdevice.so.57
libavdevice.so (libc6,x86-64) => /usr/local/lib/libavdevice.so
libavcodec.so.57 (libc6,x86-64) => /usr/local/lib/libavcodec.so.57
libavcodec.so (libc6,x86-64) => /usr/local/lib/libavcodec.so
libavahi-common.so.3 (libc6,x86-64) => /lib64/libavahi-common.so.3
libavahi-client.so.3 (libc6,x86-64) => /lib64/libavahi-client.so.3
|
可以看到,ffmpeg编译出来的.so文件都已经被加载,此时就可以重新测试推流了。
可以使用vlc拉流查看结果。
3.2 RTP测试
在设计的时候。rtp协议规定每个rtp数据流只能推送音频或视频数据,所以并不能像rtmp一样一个地址包圆。
同时rtp规定偶数端口用于rtp数据流的传输,奇数端口用来传输对应的rtcp数据,在初期测试的时候我没有搞明白这个原因然后被vlc疯狂gank不明所以。
所以我们可使用这个命令分别推送视频流和音频流:
1
2
3
4
5
6
7
8
|
# 这里的地址填的是目标设备地址,也就是播放这个视频的设备地址
# 感觉很多教程并没有说明白这一点,然后就被坑了
# 第二行表示推送视频流,禁用音频流
# 第三行表示推送音频流,禁用视频流
# 二三两行分别将两条数据流推送到同一主机的两个端口上,
ffmpeg -re -i x.mp4 \
-vcodec copy -an -f rtp rtp://192.168.107.242:1234 \
-acodec copy -vn -f rtp rtp://192.168.107.242:1236
|
这时终端会同时产生一个sdp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
t=0 0
a=tool:libavformat 57.83.100
m=video 1234 RTP/AVP 96
c=IN IP4 192.168.107.242
b=AS:567
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAMqzKcB4AiflhAAADA+kAAOpgDxgwp4A=,aOvvLA==; profile-level-id=640032
m=audio 1236 RTP/AVP 97
c=IN IP4 192.168.107.242
b=AS:128
a=rtpmap:97 MPEG4-GENERIC/44100/2
a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1210
|
保存后用vlc打开就能拉取rtp数据流进行播放了。

3B1B万岁!