自实现网络通话
Easul Lv6

前言

使用该组合的目的是在尽量少的外网暴露端口下,更轻量且便捷的进行通话操作,同时边缘物联网盒子的资源占用也更小。
但由于通话过程中盒子端会有一定的延迟现象(网络高峰期出现),且通信的信令协调需要手动实现,在简单情景下使用比较适合

主交互过程

image

服务端

ZLMediaKit的部署

部署后可在页面直接通过 webrtc 来访问
这里记录的是如何进行编译安装,使用的环境是 amd64 下的 Deepin23.1

折叠代码块BASH 复制代码
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
# 1. 基础包的安装
# 更新相关的包
sudo apt-get update
# 安装必备的包
# build-essential: 用于安装支持 C++11 的编译器
# cmake: 用于构建项目
sudo apt-get install -y build-essential cmake
# 以下的包为可选包
sudo apt-get install -y ffmpeg libssl-dev libsdl-dev libavcodec-dev libavutil-dev

# 2. 克隆并初始化ZLMediaKit
# 从 gitee 进行代码克隆,这里加了 --depth 1,用来进行浅克隆,也就是只克隆最新一次的提交
# 不包含完整的提交记录,从而减少克隆时间
git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit
cd ZLMediaKit
# 初始化子模块,因为ZLMediaKit依赖第三方代码,所以不克隆子模块就没有办法进行编译了
git submodule update --init

# 3. 进行一些必要依赖库的编译
# 创建一个临时文件夹用于依赖组件的存放
mkdir temp
cd temp
# 创建software将一些编译后的组件放到这里
mkdir software
# 克隆最新的openssl的库
git clone https://github.com/openssl/openssl.git
cd openssl
# 指定编译存放的路径,并进行相关编译
# 因为不想影响原本已经装过的openssl,所以就install到了临时目录下
./config --prefix=/home/easul/software/ZLMediaKit/temp/software/openssl
make -j4
make install
cd ../software/openssl
cp -r lib64 lib
# 克隆最新的libstrp的库
cd ../../
git clone https://github.com/cisco/libsrtp.git
# 编译并安装 libsrtp,openssl使用刚刚编译过的包
cd libsrtp
./configure --enable-openssl --with-openssl-dir=/home/easul/software/ZLMediaKit/temp/software/openssl
make -j4
# 这个因为不太清楚放到临时目录行不行,所以就安装到了系统目录
# 如果后边需要删除,可以参考如下的 编译后的一些可能需要的操作
sudo make install

# 4. 构建和编译ZLMediaKit
cd ../../
mkdir build
cd build
# 这里相关参数的解析为
# cmake .. : 表示使用上一级的 CMakeLists.txt 作为构建入口
# -DENABLE_WEBRTC=true: 指定开启编译WebRTC
# -DOPENSSL_ROOT_DIR: 指定刚刚 openssl 编译的目录
# -DOPENSSL_LIBRARIES: 指定刚刚 openssl 编译的lib的目录
# -DCMAKE_VERBOSE_MAKEFILE=ON: 加上编译输出选项
cmake .. -DENABLE_WEBRTC=true -DOPENSSL_ROOT_DIR=/home/easul/software/ZLMediaKit/temp/software/openssl -DOPENSSL_LIBRARIES=/home/easul/software/ZLMediaKit/temp/software/openssl/lib -DCMAKE_VERBOSE_MAKEFILE=ON
# --build . : 进行编译,编译内容放到当前目录
# --target MediaServer: 编译的二进制文件为 MediaServer
# -- :参数分隔符,前边的参数传给 cmake,后边的参数原封不动的传递给底层构建工具,例如make
# -j1: 使用单线程编译
# VERBOSE=1: 开启详细日志
# 如果不加
cmake --build . --target MediaServer -- -j1 VERBOSE=1

# 5. 运行MediaServer
cd ../release/linux/Debug
# 运行起来后,使用 https://你的服务器ip/webrtc 来访问webrtc页面
./MediaServer

编译后的一些可能需要的操作

折叠代码块BASH 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 以下为编译 libsrtp 之后的所有放到系统中的文件存在的路径
# /usr/local/include/srtp3/srtp.h
# /usr/local/include/srtp3/cipher.h
# /usr/local/include/srtp3/auth.h
# /usr/local/include/srtp3/crypto_types.h
# /usr/local/lib/libsrtp3.a
# /usr/local/lib/pkgconfig/libsrtp3.pc
#
# 因为在编译 libsrtp 的时候,OpenSSL没有被直接编译到静态文件中
# 所以不能够直接将编译完的OpenSSL给删掉,libsrtp 还需要使用编译完的OpenSSL
# 删除的时候可以使用如下的命令来进行删除
rm -rf /usr/local/include/srtp3
rm -f /usr/local/lib/libsrtp3.*
rm -f /usr/local/lib/pkgconfig/libsrtp3.pc

参考文档如下

客户端

这里都是用 screen 进行会话管理,且以下内容中使用的命令,如果报错,则需要再安装一下

盒子推送和接收音频流

由于原本的设想是只让服务端暴露一个端口进行数据传输,故而这里都是在盒子使用frp进行数据通道建立,然后再进行数据的传输与拉取。
这里将盒子本地端口 12347 映射到了服务器的 26002 端口,将盒子本地端口 12345 映射到了服务器的 26000,最终盒子和服务端都相当于使用了本地端口来进行数据通信。

折叠代码块BASH 复制代码
1
2
3
4
5
6
# 1. 启动frp
screen -dmS frp /root/workspace/temp/frp/frp_0.65.0_linux_arm_hf/frpc -c /root/workspace/temp/frp/frp_0.65.0_linux_arm_hf/frpc.toml > /root/workspace/temp/frp/frp_0.65.0_linux_arm_hf/frpc.log
# 2. 盒子发送音频到服务端
screen -dmS audio_send bash -c "arecord -D plughw:1,0 -f S16_LE -r 8000 -c 1 | /root/workspace/ffmpeg/ffmpeg -f s16le -ar 8000 -ac 1 -i - -c:a libopus -af "aresample=async=1:first_pts=0" -f mpegts tcp://127.0.0.1:12347?listen=1"
# 3. 盒子接收音频
screen -dmS audio_recv bash -c "socat TCP-LISTEN:12345,reuseaddr,fork - | aplay -D plughw:1,0 -f S16_LE -r 8000 -c 1 -q -t raw"

用户的电脑

折叠代码块BASH 复制代码
1
2
# 4. 将本地音频流推流给ZLM
screen -dmS audio_send bash -c "arecord -D plughw:0,0 -f S16_LE -r 8000 -c 1 | ffmpeg -f s16le -ar 8000 -ac 1 -i - -af "highpass=f=80,afftdn" -c:a pcm_mulaw -f rtsp rtsp://127.0.0.1/live/box_audio"

服务器

服务器端部署了frp,因为盒子端的相关数据只需要在内网环境下进行访问,故而这里frp端口不需要暴露到外网,从而减少了外网端口的使用。

折叠代码块BASH 复制代码
1
2
3
4
5
6
# 5. 服务端接收盒子音频并推流到ZLM
# 然后可通过ZLM的WebRTC测试页面进行流的播放,链接如下格式
# https://127.0.0.1/webrtc/?app=live&stream=test&type=play
screen -dmS audio_recv ffmpeg -thread_queue_size 1024 -i tcp://0.0.0.0:26002 -b:v 600k -maxrate 600k -bufsize 300k -af "highpass=f=80,afftdn" -c:a libopus -fflags nobuffer -flags low_delay -max_delay 0 -f rtsp rtsp://127.0.0.1/live/test
# 6. 服务端给盒子推流
screen -dmS audio_send ffmpeg -fflags nobuffer -flags low_delay -nostats -hide_banner -i rtsp://127.0.0.1/live/box_audio -map a -ar 8000 -ac 1 -c:a pcm_s16le -f s16le tcp://127.0.0.1:26000

其他

盒子端使用不同协议处理数据流

由于盒子端使用 TCPUDP 协议进行数据推送时效果和能力也不相同,故以下提供了盒子端进行两种协议下推送和接收流的方式。

TCP

折叠代码块BASH 复制代码
1
2
3
4
# 盒子接收
screen -dmS audio_recv bash -c "socat -u TCP-LISTEN:12345,reuseaddr,fork - | aplay -D plughw:1,0 -f S16_LE -r 8000 -c 1 -q -t raw"
# 服务端发送
screen -dmS audio_send ffmpeg -fflags nobuffer -flags low_delay -nostats -hide_banner -i rtsp://127.0.0.1/live/box_audio -map a -ar 8000 -ac 1 -c:a pcm_s16le -max_muxing_queue_size 1024 -buffer_size 1024 -f s16le tcp://127.0.0.1:26000

UDP

折叠代码块BASH 复制代码
1
2
3
4
# 盒子接收
screen -dmS audio_recv bash -c "socat -u UDP-LISTEN:12345,reuseaddr,fork - | aplay -D plughw:1,0 -f S16_LE -r 8000 -c 1 -q -t raw"
# 服务端发送
screen -dmS audio_send ffmpeg -fflags nobuffer -flags low_delay -nostats -hide_banner -i rtsp://127.0.0.1/live/box_audio -map a -ar 8000 -ac 1 -c:a pcm_s16le -f s16le udp://127.0.0.1:26000?pkt_size=512

视音频同时处理

这里记录一下盒子端将摄像头和麦克风的数据一起处理的方式

盒子推服务端

折叠代码块BASH 复制代码
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
# 盒子推音频
arecord -D plughw:1,0 -f cd | nc -u 192.168.1.12 22555
# 盒子推视频
ffmpeg -rtsp_transport tcp -i "rtsp://camera_user:camera_pass@ip:554/h264/ch1/sub/av_stream" -c:v copy -f mpegts - | nc -u 192.168.1.12 22554

# 服务端监听UDP端口并转码合流,推送到ZLMedia
ffmpeg \
-f mpegts -i udp://0.0.0.0:22554?listen=1 \
-f s16le -ar 44100 -ac 2 -i udp://0.0.0.0:22555?listen=1 \
-c:v libx264 -preset veryfast -tune zerolatency \
-x264opts keyint=25:min-keyint=25:scenecut=-1 \
-c:a aac -b:a 64k \
-f rtsp rtsp://192.168.1.88/live/test
# 服务端托管m3u8来播放视音频
ffmpeg \
-f mpegts -i udp://0.0.0.0:22554?listen=1 \
-f s16le -ar 44100 -ac 2 -i udp://0.0.0.0:22555?listen=1 \
-c:v libx264 -preset veryfast -tune zerolatency \
-x264opts keyint=25:min-keyint=25:scenecut=-1 \
-c:a aac -b:a 64k \
-f hls \
-hls_time 2 \
-hls_list_size 5 \
-hls_flags delete_segments \
/home/easul/Desktop/result/box_av.m3u8

服务端推盒子端

这里默认网络可以直连

分开推

折叠代码块BASH 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 服务端生成音频流
arecord -D plughw:0,0 -f cd - | \
ffmpeg -f s16le -ar 48000 -ac 2 -i - \
-c:a aac -b:a 64k -f flv rtmp://192.168.1.88/live/box_audio

# 盒子拉音频流
./ffmpeg -i rtmp://192.168.1.12/live/box_audio \
-f alsa -ac 2 hw:1,0

# 也可以使用这个
./ffmpeg -i rtmp://192.168.1.12/live/box_audio \
-vn \
-ar 48000 -ac 2 -c:a pcm_s16le \
-f alsa hw:1,0

一起推

折叠代码块BASH 复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
# 盒子只将视频推到服务端
ffmpeg -rtsp_transport tcp -i "rtsp://camera_user:camera_pass@IP:554/h264/ch1/sub/av_stream" -vcodec h264 -an -bf 0 -f flv "rtmp://192.168.1.88/live/test"
# 盒子只将音频推到服务端
ffmpeg -rtsp_transport tcp -i "rtsp://camera_user:camera_pass@IP:554/h264/ch1/sub/av_stream" -c:v copy -c:a copy -bf 0 -f flv "rtmp://192.168.1.88/live/test"
# 盒子同时将因为和视频推送到服务端,服务端可以在ZLMedia的WebRTC来查看
arecord -D plughw:0,0 -f cd -r 8000 -c 1 | \
ffmpeg -rtsp_transport tcp -fflags nobuffer -flags low_delay -rtbufsize 64k \
-i "rtsp://camera_user:camera_pass@IP:554/h264/ch1/sub/av_stream" \
-f s16le -ar 8000 -ac 1 -i - \
-c:v libx264 -preset ultrafast -tune zerolatency -crf 23 -bf 0 \
-c:a pcm_mulaw -af aresample=async=1:first_pts=0 \
-map 0:v:0 -map 1:a:0 \
-f rtsp "rtsp://192.168.1.88/live/test"

其他服务端组件尝试

在尝试过程中,同样使用过

  • srs : 实时流媒体服务器,主要用于协议转换,从而方便前端调用。
  • janus-gateway: 用于前端WebRTC使用以及通话中RTP的处理。
  • coturn: 用于WebRTC 系统里的网络中继服务器(用于实时通话流量传输)。

由于编译、运行或者与当前场景集成遇到的问题暂时无法快速解决,故未使用这些项目。

 评论
来发评论吧~
Powered By Valine
v1.5.2