ZLMediaKit+wvp-GB28181-pro,搭建28181协议视频平台
Posted 可乐cc呀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZLMediaKit+wvp-GB28181-pro,搭建28181协议视频平台相关的知识,希望对你有一定的参考价值。
ZLMediaKit+wvp-GB28181-pro,搭建28181协议视频平台
一,简介
ZLMediaKit:实现RTSP/RTMP/HLS/HTTP协议的轻量级流媒体框架,支持大并发连接请求
一个基于C++11的高性能运营级流媒体服务框架,项目地址:ZLMediaKit
特点:
- 基于C++11开发,避免使用裸指针,代码稳定可靠,性能优越。
- 支持多种协议(RTSP/RTMP/HLS/HTTP-FLV/WebSocket-FLV/GB28181/HTTP-TS/WebSocket-TS/HTTP-fMP4/WebSocket-fMP4/MP4/WebRTC),支持协议互转。
- 使用多路复用/多线程/异步网络IO模式开发,并发性能优越,支持海量客户端连接。
- 代码经过长期大量的稳定性、性能测试,已经在线上商用验证已久。
- 支持linux、macos、ios、android、windows全平台。
- 支持画面秒开、极低延时(500毫秒内,最低可达100毫秒)。
- 提供完善的标准C API,可以作SDK用,或供其他语言调用。
- 提供完整的MediaServer服务器,可以免开发直接部署为商用服务器。
- 提供完善的restful api以及web hook,支持丰富的业务逻辑。
- 打通了视频监控协议栈与直播协议栈,对RTSP/RTMP支持都很完善。
- 全面支持H265/H264/AAC/G711/OPUS。
- 功能完善,支持集群、按需转协议、按需推拉流、先播后推、断连续推等功能。
- 极致性能,单机10W级别播放器,100Gb/s级别io带宽能力。
- 极致体验,独家特性
- 谁在使用zlmediakit?
- 全面支持ipv6网络
项目定位:
- 移动嵌入式跨平台流媒体解决方案。
- 商用级流媒体服务器。
- 网络编程二次开发SDK。
-
RTSP[S]
- RTSP[S] 服务器,支持RTMP/MP4/HLS转RTSP[S],支持亚马逊echo show这样的设备
- RTSP[S] 播放器,支持RTSP代理,支持生成静音音频
- RTSP[S] 推流客户端与服务器
- 支持
rtp over udp
rtp over tcp
rtp over http
rtp组播
四种RTP传输方式 - 服务器/客户端完整支持Basic/Digest方式的登录鉴权,全异步可配置化的鉴权接口
- 支持H265编码
- 服务器支持RTSP推流(包括
rtp over udp
rtp over tcp
方式) - 支持H264/H265/AAC/G711/OPUS编码,其他编码能转发但不能转协议
-
RTMP[S]
-
HLS
- 支持HLS文件生成,自带HTTP文件服务器
- 通过cookie追踪技术,可以模拟HLS播放为长连接,可以实现HLS按需拉流、播放统计等业务
- 支持HLS播发器,支持拉流HLS转rtsp/rtmp/mp4
- 支持H264/H265/AAC/G711/OPUS编码
-
TS
- 支持http[s]-ts直播
- 支持ws[s]-ts直播
- 支持H264/H265/AAC/G711/OPUS编码
-
fMP4
- 支持http[s]-fmp4直播
- 支持ws[s]-fmp4直播
- 支持H264/H265/AAC/G711/OPUS编码
-
HTTP[S]与WebSocket
- 服务器支持
目录索引生成
,文件下载
,表单提交请求
- 客户端提供
文件下载器(支持断点续传)
,接口请求器
,文件上传器
- 完整HTTP API服务器,可以作为web后台开发框架
- 支持跨域访问
- 支持http客户端、服务器cookie
- 支持WebSocket服务器和客户端
- 支持http文件访问鉴权
- 服务器支持
-
GB28181与RTP推流
- 支持UDP/TCP RTP(PS/TS/ES)推流服务器,可以转换成RTSP/RTMP/HLS等协议
- 支持RTSP/RTMP/HLS等协议转rtp推流客户端,支持TCP/UDP模式,提供相应restful api,支持主动被动方式。
- 支持H264/H265/AAC/G711/OPUS编码
- 支持海康ehome推流
- 支持GB28181主动拉流模式
-
MP4点播与录制
- 支持录制为FLV/HLS/MP4
- RTSP/RTMP/HTTP-FLV/WS-FLV支持MP4文件点播,支持seek
- 支持H264/H265/AAC/G711/OPUS编码
-
WebRTC
- 支持WebRTC推流,支持转其他协议
- 支持WebRTC播放,支持其他协议转WebRTC
- 支持双向echo test
- 支持simulcast推流
- 支持上下行rtx/nack丢包重传
- 支持单端口、多线程、客户端网络连接迁移(开源界唯一)。
- 支持TWCC rtcp动态调整码率
- 支持remb/pli/sr/rr rtcp
- 支持rtp扩展解析
- 支持GOP缓冲,webrtc播放秒开
- 支持datachannel
-
其他
- 支持丰富的restful api以及web hook事件
- 支持简单的telnet调试
- 支持配置文件热加载
- 支持流量统计、推拉流鉴权等事件
- 支持虚拟主机,可以隔离不同域名
- 支持按需拉流,无人观看自动关断拉流
- 支持先播放后推流,提高及时推流画面打开率
- 提供c api sdk
- 支持FFmpeg拉流代理任意格式的流
- 支持http api生成并返回实时截图
- 支持按需解复用、转协议,当有人观看时才开启转协议,降低cpu占用率
- 支持溯源模式的集群部署,溯源方式支持rtsp/rtmp/hls/http-ts, 边沿站支持hls, 源站支持多个(采用round robin方式溯源)
- rtsp/rtmp/webrtc推流异常断开后,可以在超时时间内重连推流,播放器无感知
wvp-GB28181-pro: 开箱即用的28181协议视频平台
WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的开箱即用的网络视频平台,负责实现核心信令与设备管理后台部分,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR接入。支持国标级联,支持将不带国标功能的摄像机/直播流/直播推流转发到其他国标平台。
项目地址:wvp-GB28181-pro
使用文档:使用文档
应用场景:
- 支持浏览器无插件播放摄像头视频。
- 支持摄像机、平台、NVR等设备接入。 支持国标级联。
- 支持rtsp/rtmp等视频流转发到国标平台。
- 支持rtsp/rtmp等推流转发到国标平台。
基础特性:
- 视频预览;
- 云台控制(方向、缩放控制);
- 视频设备信息同步;
- 离在线监控;
- 录像查询与回放(基于NVR\\DVR,暂不支持快进、seek操作);
- 无人观看自动断流;
- 支持UDP和TCP两种国标信令传输模式;
- 集成web界面, 不需要单独部署前端服务, 直接利用wvp内置文件服务部署, 随wvp一起部署;
- 支持平台接入, 针对大平台大量设备的情况进行优化;
- 支持检索,通道筛选;
- 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题;
- 支持启用udp多端口模式, 提高udp模式下媒体传输性能;
- 支持通道是否含有音频的设置;
- 支持通道子目录查询;
- 支持udp/tcp国标流传输模式;
- 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址
- 支持国标网络校时
- 支持公网部署, 支持wvp与zlm分开部署
- 支持播放h265, g.711格式的流(需要将closeWaitRTPInfo设为false)
- 报警信息处理,支持向前端推送报警信息
新特性:
- 集成web界面, 不需要单独部署前端服务, 直接利用wvp内置文件服务部署, 随wvp一起部署;
- 支持平台接入, 针对大平台大量设备的情况进行优化;
- 支持检索,通道筛选;
- 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题;
- 支持启用udp多端口模式, 提高udp模式下媒体传输性能;
- 支持通道是否含有音频的设置;
- 支持通道子目录查询;
- 支持udp/tcp国标流传输模式;
- 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址
- 支持国标网络校时
- 支持公网部署, 支持wvp与zlm分开部署
- 支持播放h265, g.711格式的流
- 支持固定流地址和自动点播,同时支持未点播时直接播放流地址,代码自动发起点播. ( 查看WIKI)
- 报警信息处理,支持向前端推送报警信息
- 支持订阅与通知方法
- 移动位置订阅
- 移动位置通知处理
- 报警事件订阅
- 报警事件通知处理
- 设备目录订阅
- 设备目录通知处理
- 移动位置查询和显示,可通过配置文件设置移动位置历史是否存储
支持特性:
国标通道向上级联
-
WEB添加上级平台
-
注册
-
心跳保活
-
通道选择
-
通道推送
-
点播
-
云台控制
-
平台状态查询
-
平台信息查询
-
平台远程启动
-
每个级联平台可自定义的虚拟目录
-
目录订阅与通知
-
录像查看与播放
-
GPS订阅与通知(直播推流)
-
添加RTSP视频
-
添加接口鉴权
-
添加RTMP视频
-
云端录像(需要部署单独服务配合使用)
-
多流媒体节点,自动选择负载最低的节点使用。
-
WEB端支持播放H264与H265,音频支持G.711A/G.711U/AAC,覆盖国标常用编码格式。
-
支持电子地图。
-
支持接入WGS84和GCJ02两种坐标系。
二,部署
我这里的部署方式是分开独立部署,你也可以根据 wvp-GB28181-pro
项目下,docker
目录下面的dockerFile
,一起部署。
建议去github,去查看该文件,因为gitee上面该文件已经很久没更新了。我这里打不开github就不截图了。
ZLMediaKit 部署
这里采用docker部署,因为需要c++环境以及一些依赖,这里建议docker直接部署。对于docker不了解的,可以查看我之前的docker系列博客,这里就不再多撰述了
docker系列地址:docker
启动容器成功,你需要将容器内的:/opt/media/conf/config.ini 拷贝出来,到你绑定的目录
#拉取镜像
docker pull zlmediakit/zlmediakit:master
#此镜像为github持续集成自动编译推送,跟代码(master分支)保持最新状态
docker run -d -p 1935:1935 -p 18180:80 -p 8554:554 -p 10000:10000 -p 10000:10000/udp -p 8000:8000/udp -p 30000-30500:30000-30500 -p 30000-30500:30000-30500/udp --name zlmediakit --restart=always --env MODE=standalone -e TZ="Asia/Shanghai" -v /home/docker/zlmediakit/media/conf:/opt/media/conf 12bb9d80c9a2 /bin/bash -c 'MediaServer /opt/media/conf/config.ini'
-
-d:后台运行
-
-p:绑定端口
-
-name: 容器名称
-
–restart : 自启
-
–env : 环境变量
-
-e TZ:时区
-
-v:绑定文件
-
-c : 指定文件启动
知识点:config.ini 文件,66行 mediaServerId 服务器id 是 wvp-pro 需要的,这里很重要
知识点:需要将你的防火墙把
ZLMediaKit
的端口全部开放,阿里云或者其他云的服务器记得把安全组的端口也放开,注意上面端口有UDP,TCP
-
linux查看开放端口列表:
[root@localhost local]# firewall-cmd --list-ports
-
linux开放指定端口:
[root@localhost local]# firewall-cmd --zone=public --add-port=9000/tcp --permanent #关闭指定端口 [root@localhost local]# firewall-cmd --zone=public --remove-port=80/tcp --permanent
-
linux重启防火墙:
[root@localhost local]# firewall-cmd --reload
启动成功:访问localhost:18180
如下界面,即为部署成功
部署wvp-GB28181-pro
clone地址:https://gitee.com/pan648540858/wvp-GB28181-pro.git,将项目clone到本地
这里强烈建议去github,去clone wvp 代码,因为 gitee 上面的代码很久没有做更新了,有很多bug,我这边是自己把bug解决了,希望你在部署的时候,去 github上面去拉取在最新代码,少走一些弯路
将项目里面的mysql.sql里面的文件,创建数据库
修改 ProxyServletConfig.class,这里主要是将serverPort,zlmProxy,recordProxy提取出来做配置化
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.mitre.dsmiley.httpproxy.ProxyServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ObjectUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.ConnectException;
/**
* @author lin
*/
@SuppressWarnings(value = "rawtypes", "unchecked")
@Configuration
public class ProxyServletConfig
private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class);
@Autowired
private IMediaServerService mediaServerService;
@Value("$server.port")
private int serverPort;
@Value("$zlm.zlmproxy")
private String zlmProxy;
@Value("$zlm.recordproxy")
private String recordProxy;
@Bean
public ServletRegistrationBean zlmServletRegistrationBean()
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new ZlmProxyServlet(),"/zlm/*");
servletRegistrationBean.setName("zlm_Proxy");
servletRegistrationBean.addInitParameter("targetUri", zlmProxy);
servletRegistrationBean.addUrlMappings();
if (logger.isDebugEnabled())
servletRegistrationBean.addInitParameter("log", "true");
return servletRegistrationBean;
class ZlmProxyServlet extends ProxyServlet
@Override
protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString)
String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
if (mediaInfo != null)
if (!ObjectUtils.isEmpty(queryStr))
queryStr += "&secret=" + mediaInfo.getSecret();
else
queryStr = "secret=" + mediaInfo.getSecret();
return queryStr;
/**
* 异常处理
*/
@Override
protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e)
try
super.handleRequestException(proxyRequest, proxyResonse, e);
catch (ServletException servletException)
logger.error("zlm 代理失败: ", e);
catch (IOException ioException)
if (ioException instanceof ConnectException)
logger.error("zlm 连接失败");
else if (ioException instanceof ClientAbortException)
logger.error("zlm: 用户已中断连接,代理终止");
else
logger.error("zlm 代理失败: ", e);
catch (RuntimeException exception)
logger.error("zlm 代理失败: ", e);
/**
* 对于为按照格式请求的可以直接返回404
*/
@Override
protected String getTargetUri(HttpServletRequest servletRequest)
String requestURI = servletRequest.getRequestURI();
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
String uri = null;
if (mediaInfo != null)
// String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length());
uri = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort());
else
uri = "http://127.0.0.1:" + serverPort +"/index/hook/null"; // 只是一个能返回404的请求而已, 其他的也可以
return uri;
/**
* 动态替换请求目标
*/
@Override
protected HttpHost getTargetHost(HttpServletRequest servletRequest)
String requestURI = servletRequest.getRequestURI();
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
HttpHost host;
if (mediaInfo != null)
host = new HttpHost(mediaInfo.getIp(), mediaInfo.getHttpPort());
else
host = new HttpHost("127.0.0.1", serverPort);
return host;
/**
* 根据uri获取流媒体信息
*/
MediaServerItem getMediaInfoByUri(String uri)
String[] split = uri.split("/");
String mediaServerId = split[2];
if ("default".equalsIgnoreCase(mediaServerId))
return mediaServerService.getDefaultMediaServer();
else
return mediaServerService.getOne(mediaServerId);
/**
* 去掉url中的标志信息
*/
@Override
protected String rewriteUrlFromRequest(HttpServletRequest servletRequest)
String requestURI = servletRequest.getRequestURI();
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
String url = super.rewriteUrlFromRequest(servletRequest);
if (mediaInfo == null)
logger.error("[ZLM服务访问代理],错误:处理url信息时未找到流媒体信息=>", requestURI);
return url;
if (!ObjectUtils.isEmpty(mediaInfo.getId()))
url = url.replace(mediaInfo.getId() + "/", "");
return url.replace("default/", "");
@Bean
public ServletRegistrationBean recordServletRegistrationBean()
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new RecordProxyServlet(),"/record_proxy/*");
servletRegistrationBean.setName("record_proxy");
servletRegistrationBean.addInitParameter("targetUri", recordProxy);
servletRegistrationBean.addUrlMappings();
if (logger.isDebugEnabled())
servletRegistrationBean.addInitParameter("log", "true");
return servletRegistrationBean;
class RecordProxyServlet extends ProxyServlet
@Override
protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString)
String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
String remoteHost = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort());
if (mediaInfo != null)
if (!ObjectUtils.isEmpty(queryStr))
queryStr += "&remoteHost=" + remoteHost;
else
queryStr = "remoteHost=" + remoteHost;
return queryStr;
/**
* 异常处理
*/
@Override
protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResponse, Exception e)
try
super.handleRequestException(proxyRequest, proxyResponse, e);
catch (ServletException servletException)
logger.error("录像服务 代理失败: ", e);
catch (IOException ioException)
if (ioException instanceof ConnectException)
logger.error("录像服务 连接失败");
else if (ioException instanceof ClientAbortException)
logger.error("录像服务:用户已中断连接,代理终止");
else
logger.error("录像服务 代理失败: ", e);
catch (RuntimeException exception)
logger.error("录像服务 代理失败: ", e);
/**
* 对于为按照格式请求的可以直接返回404
*/
@Override
protected String getTargetUri(HttpServletRequest servletRequest)
String requestURI = servletRequest.getRequestURI();
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
String uri = null;
if (mediaInfo != null)
// String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length());
uri = String.format("http://%s:%s"zlmediakit搭建和配合ffmpeg使用
前言
本来是自己按照文档搭建好使用的,结果用完就扔,部署新服务器的时候,又查了一遍,不仅耽误时间,还做了很多无用功,坑死自己系列。。。所以赶紧记录一下,争取以后再用到不要再翻各种文档了
1.zlmediakit搭建:
链接:https://zhuanlan.zhihu.com/p/366774975
这篇文章写得已经非常详细了,就不再拾人牙慧了
2.ffmpeg安装:
虽然上面的搭建文档说不用安装ffmpeg,但是如果使用命令的话,肯定还是要安装的(不安装指的是在项目中调用api)
1).sudo apt-get update
2).sudo apt-get install ffmpeg
3.测试(;推流路径格式是:rtsp://ip/…(后面可随意命名)):
例:把test文件推到某个rtsp地址上去
ffmpeg -re -i test.mp4 -vcodec h264 -acodec aac -f rtsp -rtsp_transport tcp rtsp://ip/live/test
注意事项:
1)要推的视频路径写绝对路径;
2)推流地址格式为: rtsp://ip/…(后面可随意定义)
3)zlmediakit的默认路径是554,如果改了端口号,ip后要加上。
4)如果使用docker安装zlmediakit,那么要注意映射的端口不是554的话,端口号也得写上
4. 附带推流测试地址(附带右边列表很多相关资料):
https://github.com/ZLMediaKit/ZLMediaKit/wiki/ZLMediaKit%E6%8E%A8%E6%B5%81%E6%B5%8B%E8%AF%95
以上是关于ZLMediaKit+wvp-GB28181-pro,搭建28181协议视频平台的主要内容,如果未能解决你的问题,请参考以下文章