流媒体服务器安装及其代码文件
简述
-
流媒体服务器版本:wvp-pro与zlmediakit
部署步骤:wvp-GB28181-pro的安装与调试 - 知乎 -
录像服务:wvp-pro-assist
wvp-pro 安装部署步骤
-
官网下载源码包:WVP-PRO文档,也含有部署教程wvp-GB28181-pro: WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的网络视频平台,负责实现核心信令与设备管理后台部分,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR、DVR接入。支持国标级联,支持rtsp/rtmp等视频流转发到国标平台,支持rtsp/rtmp等推流转发到国标平台。
https://gitee.com/pan648540858/wvp-GB28181-pro.git
-
配置文件(环境为内网穿透后的213本地)
spring: # 设置接口超时时间 mvc: async: request-timeout: 20000 thymeleaf: cache: false # [可选]上传文件大小限制 servlet: multipart: max-file-size: 10MB max-request-size: 100MB # REDIS数据库配置 redis: # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 host: 192.168.0.213 # [必须修改] 端口号 port: 6379 # [可选] 数据库 DB database: 7 # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 password: xiaomu2023 # [可选] 超时时间 timeout: 10000 # mysql数据源 datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.0.213:3307/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true username: root password: xiaomu2023 hikari: connection-timeout: 20000 # 是客户端等待连接池连接的最大毫秒数 initialSize: 10 # 连接池初始化连接数 maximum-pool-size: 200 # 连接池最大连接数 minimum-idle: 5 # 连接池最小空闲连接数 idle-timeout: 300000 # 允许连接在连接池中空闲的最长时间(以毫秒为单位) max-lifetime: 1200000 # 是池中连接关闭后的最长生命周期(以毫秒为单位) #[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 # [可选] HTTPS配置, 默认不开启 ssl: # [可选] 是否开启HTTPS访问 enabled: false # [可选] 证书文件路径,放置在resource/目录下即可,修改xxx为文件名 key-store: classpath:test.monitor.89iot.cn.jks # [可选] 证书密码 key-store-password: gpf64qmw # [可选] 证书类型, 默认为jks,根据实际修改 key-store-type: JKS # 作为28181服务器的配置 sip: # [必须修改] 本机的IP,对应你的网卡,监听什么ip就是使用什么网卡, # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4 # 如果不明白,就使用0.0.0.0,大部分情况都是可以的 # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。 ip: 127.0.0.1 # [可选] 28181服务监听的端口 port: 15060 # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) # 后两位为行业编码,定义参照附录D.3 # 3701020049标识山东济南历下区 信息行业接入 # [可选] domain: 4101050000 # [可选] id: 41010500002000000001 # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 password: bajiuwulian1006 # 是否存储alarm信息 alarm: true #zlm 默认服务器配置 media: id: xiaomu2012 # [必须修改] zlm服务器的内网IP ip: 192.168.0.213 # [必须修改] zlm服务器的http.port http-port: 88 # [可选] 返回流地址时的ip,置空使用 media.ip stream-ip: 192.168.0.213 # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip sdp-ip: 192.168.0.213 # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip hook-ip: 192.168.0.213 # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 http-ssl-port: 8443 # [可选] zlm服务器的hook.admin_params=secret secret: LluEd5evhoTswSZ1qDnafNEFTCFnnWCe # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 rtmp-prot: 1935 rtsp-prot: 8554 rtp: # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 enable: true # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 port-range: 30000,35000 # 端口范围 # [可选] 国标级联在此范围内选择端口发送媒体流, send-port-range: 30000,35000 # 端口范围 # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 record-assist-port: 18081 # [根据业务需求配置] user-settings: # 点播/录像回放 等待超时时间,单位:毫秒 play-timeout: 180000 # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true auto-apply-play: true # 设备/通道状态变化时发送消息 device-status-notify: true # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个 allowed-origins: - http://localhost:18080 - http://192.168.0.213:18080 # [可选] 日志配置, 一般不需要改 logging: config: classpath:logback-spring-local.xml server: port: 18080
-
打包至服务器 现wvp-pro包路径为192.168.0.213服务器的/home/wvp-GB28181-pro/target 下
-
编写服务器运行脚本start.sh
创建start.sh文件,使用chomd 777 start.sh 为配置文件赋权
#start.sh 命令
nohup java -jar wvp-pro.jar --spring.config.location=application-dev.yml > log.out 2>&1 &
-
启动直接./start.sh即可 注意修改事项: 本人在使用过程中,遇到wvp连接zlm后初始化重启zlm服务器的问题,经过测试发现,不重启只修改配置也不影响,而对于服务器性能较差的开发者来说,在zlm重启的时间内,wvp就已经将其置为掉线,重启之后也无法连接,这时便又需要重启wvp服务,如此反复,因此我直接修改wvp-pro包的代码,以上皆为本人需要,各位请自行考虑是否需要更改:在wvp源码中找到MediaServerServiceImpl下的setZLMConfig进行修改
/** * 对zlm服务器进行基础配置 * @param mediaServerItem 服务ID * @param restart 是否重启zlm */ @Override public void setZLMConfig(MediaServerItem mediaServerItem, boolean restart) { logger.info("[ZLM] 正在设置 :{} -> {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); String protocol = sslEnabled ? "https" : "http"; String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort); Map<String, Object> param = new HashMap<>(); param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline if (mediaServerItem.getRtspPort() != 0) { param.put("ffmpeg.snap", "%s -rtsp_transport tcp -i %s -y -f mjpeg -t 0.001 %s"); } param.put("hook.enable","0"); // 将此属性置为0,默认为1,不然每次重启后都无法推流,需要鉴权 param.put("hook.on_flow_report",""); param.put("hook.on_play",String.format("%s/on_play", hookPrex)); param.put("hook.on_http_access",""); param.put("hook.on_publish", String.format("%s/on_publish", hookPrex)); param.put("hook.on_record_ts",""); param.put("hook.on_rtsp_auth",""); param.put("hook.on_rtsp_realm",""); param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex)); param.put("hook.on_shell_login",""); param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex)); param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex)); param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex)); param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrex)); param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrex)); param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrex)); if (mediaServerItem.getRecordAssistPort() > 0) { param.put("hook.on_record_mp4",String.format("http://127.0.0.1:%s/api/record/on_record_mp4", mediaServerItem.getRecordAssistPort())); }else { param.put("hook.on_record_mp4",""); } param.put("hook.timeoutSec","20"); param.put("protocol.continue_push_ms", "3000" ); if (mediaServerItem.isRtpEnable() && !ObjectUtils.isEmpty(mediaServerItem.getRtpPortRange())) { param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-")); } if (userSetting.getRecordPath() != null) { File recordPathFile = new File(userSetting.getRecordPath()); File mp4SavePathFile = recordPathFile.getParentFile().getAbsoluteFile(); param.put("protocol.mp4_save_path", mp4SavePathFile.getAbsoluteFile()); param.put("record.appName", recordPathFile.getName()); } JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param); if (responseJSON != null && responseJSON.getInteger("code") == 0) { if (false) { // 将此restart置为false,永不重启,如需重启自己进入docker修改配置后重启即可 logger.info("[ZLM] 设置成功,开始重启以保证配置生效 {} -> {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); zlmresTfulUtils.restartServer(mediaServerItem); }else { logger.info("[ZLM] 设置成功 {} -> {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); } }else { logger.info("[ZLM] 设置zlm失败 {} -> {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); } }
zlmediakit 推流服务器
zlm包下载与配置
# 使用docker 拉取zlm最新的镜像
docker pull zlmediakit/zlmediakit:master
# 启动命令
docker run -d -p 11935:1935 -p 88:80 -p 8443:443 -p 8544:554 -p 10000:10000 -v /opt/media/bin/www/record/:/opt/media/bin/www/record/ zlmediakit/zlmediakit:master
# 进入zml流媒体服务器docker容器
docker exec -it 容器id /bin/bash
# 进入config目录
cd ../config
# 编辑config,ini文件
vim config.ini
[api]
apiDebug=1
defaultSnap=./www/logo.png
secret=LluEd5evhoTswSZ1qDnafNEFTCFnnWCe #记录此行secret值,将其填入wvp-pro的yml配置文件种
snapRoot=./www/snap/
[hook]
alive_interval=10.0
enable=0 #将hook中的该值修改为0 这样推流时不用鉴权,如需鉴权请自行开启
[general]
check_nvidia_dev=1
enableVhost=0
enable_ffmpeg_log=0
flowThreshold=1024
maxStreamWaitMS=15000
mediaServerId=xiaomu2012 #将次mediaServerId修改为你所需的,与wvp-pro中的mediaServerId替换
#随后退出容器,重启即可
wvp-pro-assist 录像服务配置
源码地址在gitee随便找的:wvp-pro-assist: wvp的辅助录像程序,也可单独跟zlm一起使用,提供录像控制,录像合并下载接口。 里面一些包含有作者根据自身项目的修改,因此下载后可能部分功能不能使用的bug,本人便遇到过,无法保存录像和无法生成可播放的MP4文件格式两种bug
wvp-pro-assist 配置文件
使用which查看ffmpeg的安装目录,如果没有则需要自行安装 ,将地址填入下方yaml配置文件中
which ffmpeg
which ffprobe
配置录像目录,我使用的docker映射,其它redis 数据库等配置自行更改
record: /opt/media/bin/www/record
spring:
# REDIS数据库配置
redis:
# [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
host: 127.0.0.1
# [必须修改] 端口号
port: 6379
# [可选] 数据库 DB
database: 8
# [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
password:
# [可选] 超时时间
timeout: 10000
# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
server:
port: 18081
# [可选] HTTPS配置, 默认不开启
ssl:
# [可选] 是否开启HTTPS访问
enabled: false
# [可选] 证书文件路径,放置在resource/目录下即可,修改xxx为文件名
key-store: classpath:xxx.jks
# [可选] 证书密码
key-password: password
# [可选] 证书类型, 默认为jks,根据实际修改
key-store-type: JKS
# [根据业务需求配置]
userSettings:
# [可选 ] zlm配置的录像路径,
record: /opt/media/bin/www/record
# [可选 ] 录像保存时长(单位: 天)每天晚12点自动对过期文件执行清理
recordDay: 7
# [可选 ] 录像下载合成临时文件保存时长, 不配置默认取值recordDay(单位: 天)每天晚12点自动对过期文件执行清理
# recordTempDay: 7
# [必选 ] ffmpeg路径
ffmpeg: /usr/bin/ffmpeg
# [必选 ] ffprobe路径, 一般安装ffmpeg就会自带, 一般跟ffmpeg在同一目录,用于查询文件的信息
ffprobe: /usr/bin/ffprobe
# [可选 ] 限制 ffmpeg 合并文件使用的线程数,间接限制cpu使用率, 默认2 限制到50%
threads: 2
swagger-ui:
# [可选] 日志配置, 一般不需要改
logging:
file:
name: logs/wvp.log
max-history: 30
max-size: 10MB
total-size-cap: 300MB
level:
root: WARN
top:
panll:
assist: info
无法生成可播放的MP4文件格式bug解决方案:
每次重启之后才去读取zlm中的录像文件,进行转换,这种肯定不能满足业务需求,也有可能录像辅助wvp-pro-assist中有配置的方法,我并没有过多的去观察,直接创建定时任务,去转换本地中的录像文件,编译为wvp-pro可播放识别的文件类型 更好的优化方法为写一个监听方法,每次录像目录文件内容发送变化在进行文件格式转化,避免重复执行,影响效率。