Dust8 的博客

读书百遍其义自见

0%

查看环境

查看内存情况

free -h 命令查看内存情况和swap分区大小

1
2
3
              total        used        free      shared  buff/cache   available
Mem: 1.9Gi 259Mi 1.4Gi 1.0Mi 207Mi 1.5Gi
Swap: 0B 0B 0B

可以看到swap目前是0, 也就是没有设置虚拟内存.

查看磁盘空间大小

df -h 命令查看磁盘空间情况. 因为下面需要在磁盘创建一个文件当作虚拟内存, 所以看下在哪个目录下创建比较好.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Filesystem      Size  Used Avail Use% Mounted on
overlay 5.8G 589M 5.0G 11% /
devtmpfs 887M 0 887M 0% /dev
tmpfs 954M 0 954M 0% /dev/shm
tmpfs 191M 1.8M 189M 1% /run
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 954M 0 954M 0% /sys/fs/cgroup
/dev/mmcblk0p1 128M 63M 66M 49% /boot
/dev/mmcblk0p7 15G 4.7G 9.9G 33% /data
/dev/mmcblk0p4 2.4G 2.2G 157M 94% /media/root-ro
/dev/mmcblk0p5 5.8G 589M 5.0G 11% /media/root-rw
/dev/mmcblk0p6 2.0G 226M 1.6G 13% /opt
/dev/mmcblk0p2 2.9G 52M 2.8G 2% /recovery
tmpfs 191M 0 191M 0% /run/user/1000

可以看到/data目录空间比较大, 可以在此目录创建.

创建虚拟内存

1.创建swap文件

1
sudo dd if=/dev/zero of=/data/swapfile bs=1M count=4096

/data/swapfile 就是创建的虚拟内存文件路径, bs=1M 代表文件大小单位是1M, count=4096 代表创建多少个单位, 合起来就是4个G.

  1. 设置swap文件

    1
    sudo mkswap /data/swapfile
  2. 激活swap文件

    1
    sudo swapon /data/swapfile
  3. 校验是否生效
    运行 free -h, 查看swap部分total是否为0, 不是0则生效.

    1
    2
    3
                  total        used        free      shared  buff/cache   available
    Mem: 1.9Gi 259Mi 1.4Gi 1.0Mi 207Mi 1.5Gi
    Swap: 4.0Gi 0B 4.0Gi

设置开机启动

上面的设置是临时生效的, 为了重启仍然生效, 需要修改 /etc/fstab 文件, 设置开机自动挂载.
/etc/fstab 文件后面添加一行.

1
/data/swapfile  swap    swap   defaults  0   0

上面是一般情况就设置成功了, 有时碰到特殊情况, 例如系统是定制的, /etc/fstab 文件重启后会被还原为默认配置. 这时可以通过 systemd 来管理开机启动, 也就是设置一个开机启动的服务, 该服务启动后执行激活swap文件命令.
/usr/lib/systemd/system/ 目录创建一个 my-swap.service 文件, 文件内容为

1
2
3
4
5
6
7
8
[Unit]
Description=my swap

[Service]
ExecStart=sudo swapon /data/swapfile

[Install]
WantedBy=multi-user.target

完成后可以通过systemctl cat my-swap 查看该配置, 目测下是否正确.
运行systemctl start my-swap 执行该服务, 然后运行free -h 检查是否激活swap成功, 如果没有就在检查下配置.
运行systemctl enable my-swap 则正式启用该服务, 重启后会自动运行该服务.

取消虚拟内存

  1. 运行 sudo swapoff /data/swapfile 关闭系统交换区
  2. 运行 sudo rm /data/swapfile 删除系统交换区文件, 释放占用的磁盘空间.
  3. 取消开机启动. 删除添加到 /etc/fstab 里的/data/swapfile 那一行. 如果使用的是systemd, 则运行systemctl disable my-swap, 在删除my-swap.service文件.

参考链接

跳帧是视频流处理中, 跳过一些帧不处理.

为什么跳帧

一个是设备性能低, 无法实时处理视频流, 导致任务堆积, 造成程序报错无法运行. 例如在分析RTSP视频流的时候, AI推理太慢, 导致视频流接收堆积, 程序报错.
一个是减少设备运行负载, 可以运行更多的任务. 例如某些任务不需要每帧图片进行分析, 通过跳帧既符合需求, 也不浪费设备资源.

跳帧原理

如果是N帧中取一帧, 可以通过总帧数对N取余, 判断余数是否等于零, 如果等于则取该帧.
如果是每隔N帧取一帧, 也是同理, 通过总帧数对N+1取余, 判断余数.
如果是M分钟取N帧, 也是同理, 需要先获取FPS也就是每分钟的帧数, 公式为总帧数 % (M * FPS / N).

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import cv2

uri = "xxx"
cap = cv2.VideoCapture(uri)
# 查询视频帧率. 后面可以用来计算skip_n参数, 例如1分钟取3帧, 公式为int(1*fps/3), 假设fps是24, 则skip_n为8.
fps = cap.get(cv2.CAP_PROP_FPS)
skip_n = 8 # 跳帧, 每隔N帧取一帧
frame_count = 0
while True:
ret, frame = cap.read()
frame_count += 1

# 判断是否需要跳帧
if frame_count % skip_n != 0:
continue

cv2.imshow("frame", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break

cv2.destroyAllWindows()
cap.release()

摄像头通过RS485控制云台的转动.

IP监控系统协议组件

  • 控制流, 通过SIP业务控制,RTSP URL查询/返回结果
  • 实时流, 实时视频
  • 存储流和回放流, TRP/TS回放流, iSCSI检索/回放

管理控制协议-SIP

SIP(Session Initiation Protocol, 会话初始协议) 是应用层的会话控制协议, 用于创建,修改和释放一个或多个参与者参加的会话.
SIP协议采用基于文本格式的客户/服务器模式.
SIP的基本功能包含:

  • 用户定位
  • 用户能力协商
  • 用户可用性确定
  • 会话建立
  • 会话管理

SDP是会话描述协议, 用于为SIP, RTSP, HTTP等协议描述会话信息.

SIP消息分为Request消息和Response消息两类.
Request消息通过Request-Line中的Method来区分消息的类别
Response消息通过Status-Line中的Status Code来区分消息类别

常见的SIP请求消息如下:

  • INVITE: 表示主叫用户发起会话请求, 邀请其他用户加入一个会话. 也可以用在呼叫建立后用户更新会话
  • ACK: 客户端向服务器证实它已经收到了对INVITE请求的最终响应
  • BYE: 表示终止一个已经建立的呼叫
  • CANCEL: 表示在收到对请求的最终响应之前取消该请求, 对于已经完成的请求则无影响
  • REGISTER:表示客户端向SIP服务器端注册列在To字段中的地址信息
  • OPTIONS: 表示查询被叫的相关信息和功能

常见的SIP响应消息如下:

  • 100试呼叫(Trying)
  • 200成功响应(OK)
  • 400错误请求(Bad Request)
  • 401未授权(Unauthorized)
  • 403禁止(Forbidden)
  • 404用户不存在(Not Found)
  • 408请求超时(Request Timeout)

管理控制协议-SNMP

SNMP(Simple Network Management Protocol, 简单网络管理协议) 是对网络进行管理的一个框架, 它提供一组基本的操作来监视和维护网络.
SNMP可以实现自动化管理并可以屏蔽不同设备的物理差异, 实现对不同厂商产品的自动化管理.
SNMP网络元素分为NMS和Agent两种.

  • NMS(Network Management Station, 网络管理站)
  • Agent是驻留在设备上的一个进程, 负责接收处理来自NMS的请求报文

传输协议-TS&PS

  • TS流(Transport Stream): 传输流, 将具有共同时间基准或独立时间基准的一个或多个PES组合而成的单一数据流(用于数据传输)
  • PS流(Program Stream): 节目流, 将具有共同时间基准的一个或多个PES组合而成的单一数据流(用于播放和编辑系统, 如m2p)

TS流和PS流的区别:
TS流的包结构是长度固定的, PS流的包结构是可变长度的.这导致了TS流的抵抗传输误码的能力强于PS流.

传输协议-RTP

RTP(Real-time Transport Protocol): 实时传输协议, 详细说明了在互联网上传递音频和视频的标准数据包格式, 是一个传输层的基于UDP的协议.
RTP协议提供的信息包括: 时间戳(用于同步), 序列号(用于丢包和重排序检测), 以及负载格式(用于说明数据的编码格式).
RTP协议被用来为音视频等实时数据提供端到端的网络传输, 传输的模型可以是单点传送和多点传送.
RTP协议是为支持实时业务而设计的, 保证业务的接收和发送在很短时间内完成.

存储回放协议-iSCSI

iSCSI(Internet Small Computer System Interface): Internet 小型计算机系统接口, 是一种基于TCP/IP的协议, 用来建立和管理IP存储设备, 主机和客户机之间的相互连接, 并创建存储区域网络(SAN)

存储回放协议-RTSP

RTSP(RealTime Streaming Protocol)实时流传输协议, 是TCP/IP协议体系中的一个应用层协议.该协议定义了一对多应用程序如何有效的通过IP网络传送多媒体数据. RTSP在体系结构上位于RTP/RTCP之上, 它使用TCP/UDP完成数据传输.

互联协议-ONVIF

ONVIF(Open Network Video Interface Forum), 开放型网络视频接口论坛, 描述了网络视频的模型,接口,数据类型以及数据交互模式, 目标是实现一个网络视频框架协议, 使不同厂商所生产的网络视频产品完全互通.
ONVIF规范的基本功能定义有:

  • 设备发现
  • 设备管理
  • 实时流媒体
  • 事件处理
  • PTZ控制
  • 其他

互联协议-国标GB28181

是国内定的标准, 类似ONVIF. 主要针对视频监控.
国标主要业务包含:

  • 域间注册
  • 域间保活
  • 云台控制
  • 实况业务
  • 录像检索和回放

前端智能应用

  • 人脸抓拍, 检测并抓拍人脸图片
  • 人数统计, 客流统计, 人数统计, 人流量统计等均为人数统计功能, 统计视频中出现的人数
  • 周界防范, 穿越警戒线, 区域入侵, 进入/离开区域等
  • 跟踪球, 对运动目标自动统计
  • 违停球, 对违停车辆进行自动抓拍, 生成证据链上传中心

webhook 是用来和其他系统通信的, 把内部系统的事件发送到外部系统.

可以看作是发布/订阅模式, 或者观察者模式, 虽然有些不同, 但大致是一样的.
好处是外部系统如果想知道内部系统的事件, 不需要轮询, 有变化就会被通知到.

当内部系统(发布者)有事件发生, 就会发送api请求(通常是post)到外部系统(订阅者), 请求载荷就是事件数据, 外部系统的接口收到请求, 做出响应.

常见场景

github

例如提交代码到github, 就会运行ci/cd. 这里就是利用的webhook, github系统和ci/cd系统可以是2个不同的公司产品. github是发布者, ci/cd是订阅者, 当ci/cd订阅了代码提交事件, 一旦代码被提交, github就会发送请求到ci/cd, 告诉ci/cd发生了代码提交事件, ci/cd就知道需要拉取新的代码进行测试部署.

1
2
3
4
5
+---------------+                 +---------------+
| | webhook | |
| github | -------------> | CI/CD |
| | | |
+---------------+ +---------------+

支付宝/微信支付

做支付的时候, 支付宝和微信支付都需要配置支付回调, 支付回调地址就是我们的业务系统. 支付宝/微信支付系统就是发布者, 业务系统就是订阅者. 当发生支付成功事件时, 支付宝/微信支付就会发送请求到我们业务系统, 请求的数据包含订单号,状态等, 我们业务系统收到后就知道哪个订单支付成功了.

飞书机器人/钉钉机器人

飞书机器人/钉钉机器人都提供一个webhook地址, 我们可以向这个地址发送请求. 例如我们开发一个报警系统, 一旦监控的网站没有响应, 就向webhook地址发送请求, 飞书/钉钉接收到请求后, 就会在群里发送消息了. 这里报警系统就是发布者, 飞书机器人/钉钉机器人就是订阅者.

流媒体服务器

常见的流媒体服务器中(srs, ZLMediaKit,monibuca等 ), 都提供webhook配置, 把内部的事件发送出去. 例如在监控领域, 当有人向流媒体服务器请求某个摄像头视频流时, 流媒体服务器就会把这个事件发送到业务系统, 业务系统收到后就可以控制摄像头推流到流媒体服务器; 当某个视频流无人观看时, 流媒体也会把这个事件发送到业务系统, 业务系统就可以控制摄像头停止推流. 这样就避免了无人观看视频时, 摄像头还一直推流, 节约了流量.

如何开发webhook接收者

其实就是服务端的一个普通web http 接口.

如何开发webhook发布者

其实就是一个能发送web http请求的客服端.
最基本就是: 一个是url地址, 确定了向谁发送.
更完整的可以包括:

  • 事件类型. 事件很多, 需要响应的处理. 可以使用namespace.noun.verb 格式命名, 例如github.issue.created.
  • 事件数据. 每个事件可能有不同的内容, 例如issue地址
  • 订阅事件类型集合. 由于事件类型很多, 可以只订阅指定类型的事件. 例如[“*”]订阅全部事件, [“github.issue.created”]只订阅issue创建的事件.
  • 名称. 用来描述这个webhook

更加健壮的可以包括:

  • 发送请求重试功能, 每次重试时间间隔递增. 例如1s, 4s, 16s, 256s.
  • 发送请求队列

参考链接

需求

把摄像头集中管理, 多端可以预览视频, 可以进行喊话, 对讲功能.

碰到的问题:

  • 摄像头官方的流量费太高; 并且提供的功能不全, 例如喊话, 对讲功能
  • 第三方的搭建困难; 功能不全, 例如无喊话, 对讲功能; HLS延迟太大(基本40-60s)

调研结果

可以使用wvp-GB28181-pro(SIP服务器)+ZLmMediaKit(媒体服务器)来实现.

摄像头品牌多, 但基本都支持国标GB28181, GB28181是国家标准, 由公安部提出. 有2个版本, GB28181-2011是第一个版本, GB28181-2016是第二个版本, 进行了补充(例如支持TCP). 场景由内网的UDP, 到外网导致不稳定, 改为TCP.

GB28181 协议是在SIP协议上修改的, 通过TCP, UDP进行传输. 有控制和传输两方面, 控制是各种消息, 例如注册注销, 保活, invite(开始推流), bye(停止推流); 传输包括协议消息和媒体传输. 媒体传输是通过RTP/RTCP协议传输PS格式封装的视音频数据.

大体可以看作是4部分组成:

  • 媒体流接收者者(播放器)
  • SIP服务器
  • 媒体服务器
  • 媒体流发布者(摄像机)

详细的流程可以看GB2818标准的文件, 这里简单描述下流程:

  • 摄像机注册到SIP服务器, 媒体服务器注册到SIP服务器
  • SIP服务器发送invite消息给摄像机, 告诉推流地址为媒体服务器
  • 摄像机推流到媒体服务器
  • 媒体服务器接收流后, 转码为各种格式, 例如HLS, flv等
  • 播放器开始播放视频

实际使用中会调整, SIP服务会提供restful接口来管理摄像机, 提供webhook接口来控制摄像机. 媒体服务器会把内部事件通过webhook请求发送到SIP服务器, 例如当播放器请求的流不存在事件, SIP服务器就会发送推流消息给摄像机, 让它推流到媒体服务器; 当无人观看流, 也会发送到SIP服务器, 就可以控制摄像机停止推流.
这样按需推流可以节省流量, 不给服务器增加不必要的负担.

下面是ZLMediaKit提供的按需推流的流程图:
使用ZLMediaKit的MediaServer进程可以实现按需推流

SIP服务器

GB28181库比较少, 一些比较复杂的, 难扩展的(C++)就不列了.

平台 语言 优点 缺点
648540858/wvp-GB28181-pro java 比较全, 有ui 未实现广播, 对讲
panjjo/gosip go 支持功能比较少,未实现广播, 对讲. 无ui

常见流媒体平台

平台 语言 GB28181 优点 缺点
ossrs/srs c++ 只支持部分SIP消息(注册, 邀请, 推流等), 无法停止推流(sip消息-bye). 4.0版本是分支, 5.0才合并到主分支. 运营商级, 支持集群. 接收流端口可以复用 gb28181功能不全.
ZLMediaKit/ZLMediaKit c++ 未支持sip信令, 只支持了接收推流功能 运营商级 未集成gui, 第三方都不更新. 接收流端口有多端口模式(兼容性好), 单端口模式(比较麻烦).
langhuihui/monibuca go 只支持部分SIP消息, 通过panjjo/gosip包来支持的 插件化, 好扩展, 入门容易, 支持完整sip信令 文档描述模糊, 核心开发人员少. gui闭源. v4版本发现GB28181推送的流,转化为HLS后无法播放.

专业术语

  • SIP 会话初始协议(Session Initiation - Protocol):由 IETF - 制定的用于多方多媒体通信的框架协议。它是一个基于- 文本的应用层控制协议,独立于底层传输协议,用于建- 立、修改和终止 IP 网络上的双方或多方多媒体会话。
  • IETF 互联网工程任务组(Internet Engineering Task - Force)
  • SDP 会话描述协议(Session Description Protocol)
  • PS 节目流(Program Stream)
  • RTP 实时传输协议(Real-time Transport Protocol)
  • RTCP 实时传输控制协议(Real-time Transport - Control Protocol)
  • RTSP 实时流化协议(Real-Time Streaming Protocol)
  • RTMP 实时信息传输协议(Real Time Message - Protocol)
  • IPC 网络摄像机(InternetProtocolCamera)
  • UA 用户代理(UserAgent)
  • UAC 用户代理客户端(UserAgentClient)
  • UAS 用户代理服务端(UserAgentServer)
  • SSRC 同步源标识
  • CNAME 规范性名字(Canonical Name)
  • CSeq (Transaction Number)

使用 wvp-GB28181-pro

  • 运行zlmediakit
    docker run -id -p 1935:1935 -p 8080:80 -p 8443:443 -p 8554:554 -p 10000:10000 -p 10000:10000/udp -p 8000:8000/udp -p 9000:9000/udp -p 30000-30005:30000-30005 zlmediakit/zlmediakit:master
  • 下载wvp-GB28181-pro代码, 按照官方文档编译, 配置, 运行. 使用到了mysql, redis. 按需推流还需要配置user-settings/auto-apply-playtrue.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 运行redis. 因为免得和本地的redis端口冲突, docker运行的端口在默认端口+1. mysql同理
    docker run -p 6380:6379 --name wvp-redis -d redis
    # 运行mysql
    docker run -itd --name wvp-mysql -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql

    # 复制将要导入的sql文件到docker容器
    docker cp mysql.sql wvp-mysql:/opt/mysql.sql
    # 运行mysql客户端
    docker exec -it wvp-mysql mysql -uroot -p
    # 创建数据库
    create database wvp;
    # 切换当前使用数据库
    use wvp;
    # 导致数据
    source /opt/mysql.sql;
  • HLS地址是zlmediakit生成的, 详情查看wiki https://github.com/zlmediakit/ZLMediaKit/wiki/%E6%92%AD%E6%94%BEurl%E8%A7%84%E5%88%99. 格式是http://somedomain.com/live/0/hls.m3u8, 例如http://127.0.0.1:8080/rtp/34020000001320000001_34020000001320000004/hls.m3u8, rtp是固定的应用名, 34020000001320000001_34020000001320000004是流ID, 由摄像机设备ID和通道ID组成.

如果需要查看docker容器日志

1
2
3
4
docker logs 容器id

# 实时日志
docker logs 容器id -f

公网部署

如果部署到公网需要开放端口和修改配置.

1
2
3
4
5
6
# 端口号, 备注
# 处理 http, 其他服务可以开放TCP和UDP
18080 wvp http
5060 wvp sip
8080 zlmediakit http
30000-30005 zlmediakit rtp

配置需要修改

1
2
3
4
5
6
7
8
9
10
sip:
# [必须修改] 本机的IP. 必须是网卡的ip, 也就是内网ip. 云服务器有公网ip和内网ip.
ip: 192.168.0.5

media:
# [必须修改] zlm服务器的内网IP, sdp-ip与stream-ip使用默认值的情况下
ip: 192.168.0.5
# 需改成外网ip
stream-ip: xxx.xxx.xxx.xxx
sdp-ip: xxx.xxx.xxx.xxx

踩坑

问题1. 注意开放端口

为了简单运行, 是用docker来运行的, 官方文档的命令可能没映射需要的端口.
srs 的GB28181模块需要开放5060端口, 这个端口号是默认的SIP端口.
docker run --rm -it --env CANDIDATE="192.168.1.107" -p 1935:1935 -p 1985:1985 -p 5060:5060 -p 8080:8080 -p 9000:9000 registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5 ./objs/srs -c conf/gb28181.conf

使用wvp-GB28181-pro时配置ZLMediaKit的接收流端口, 是多端口port-range, 而ZLMediaKit默认的docker运行命令是不带有的, 需要增加端口段映射-p 30000-30005:30000-30005, 因为如果端口太多, docker显示太长, 这里只映射了5个.
docker run -id -p 1935:1935 -p 8080:80 -p 8443:443 -p 8554:554 -p 10000:10000 -p 10000:10000/udp -p 8000:8000/udp -p 9000:9000/udp -p 30000-30005:30000-30005 zlmediakit/zlmediakit:master

问题2. wvp-GB28181-pro编译

wvp-GB28181-pro前端是后端集成的, 所以先编译完前端, 在编译后端. 前端编译完后文件保存在src\main\resources\static. 要使用文档的cnpm, 如果使用npm会报错. 运行后端后, 访问localhost:18080, 账户admin密码admin.

问题3. ZLMediaKit无法连接

wvp-GB28181-pro配置中需要指定ZLMediaKit的id, ZLMediaKit默认是your_server_id, 而wvp-GB28181-pro的配置默认是FQ3TF8yT83wh5Wvz, 需要改成一致即可.

问题4. 数据库找不到platform_gb_stream表

日志报该错误, 查看导入的sql文件, 发现先创建了该表, 后面又删掉了. 是在https://github.com/648540858/wvp-GB28181-pro/commit/170b0a18004639a1855939bcdd3384ea546deed4改版的时候弄错了. 修改sql文件, 把删除语句放到创建语句前面.

参考链接