web页面集成

This commit is contained in:
648540858 2020-10-10 17:33:02 +08:00
parent 320fa66aae
commit 1d3efd3c49
32 changed files with 240 additions and 223 deletions

View File

@ -31,6 +31,9 @@ public class MediaServerConfig {
@JSONField(name = "general.streamNoneReaderDelayMS")
private String generalStreamNoneReaderDelayMS;
@JSONField(name = "general.localIP")
private String localIP;
@JSONField(name = "hls.fileBufSize")
private String hlsFileBufSize;
@ -163,6 +166,18 @@ public class MediaServerConfig {
@JSONField(name = "rtp.videoMtuSize")
private String rtpVideoMtuSize;
@JSONField(name = "rtp_proxy.checkSource")
private String rtpProxyCheckSource;
@JSONField(name = "rtp_proxy.dumpDir")
private String rtpProxyDumpDir;
@JSONField(name = "rtp_proxy.port")
private String rtpProxyPort;
@JSONField(name = "rtp_proxy.timeoutSec")
private String rtpProxyTimeoutSec;
@JSONField(name = "rtsp.authBasic")
private String rtspAuthBasic;
@ -664,4 +679,44 @@ public class MediaServerConfig {
public void setShellPhell(String shellPhell) {
this.shellPhell = shellPhell;
}
public String getLocalIP() {
return localIP;
}
public void setLocalIP(String localIP) {
this.localIP = localIP;
}
public String getRtpProxyCheckSource() {
return rtpProxyCheckSource;
}
public void setRtpProxyCheckSource(String rtpProxyCheckSource) {
this.rtpProxyCheckSource = rtpProxyCheckSource;
}
public String getRtpProxyDumpDir() {
return rtpProxyDumpDir;
}
public void setRtpProxyDumpDir(String rtpProxyDumpDir) {
this.rtpProxyDumpDir = rtpProxyDumpDir;
}
public String getRtpProxyPort() {
return rtpProxyPort;
}
public void setRtpProxyPort(String rtpProxyPort) {
this.rtpProxyPort = rtpProxyPort;
}
public String getRtpProxyTimeoutSec() {
return rtpProxyTimeoutSec;
}
public void setRtpProxyTimeoutSec(String rtpProxyTimeoutSec) {
this.rtpProxyTimeoutSec = rtpProxyTimeoutSec;
}
}

View File

@ -16,10 +16,6 @@ public class SipConfig {
String sipId;
@Value("${sip.password}")
String sipPassword;
@Value("${media.ip}")
String mediaIp;
@Value("${media.port}")
Integer mediaPort;
@Value("${sip.ptz.speed:50}")
Integer speed;
@ -56,22 +52,6 @@ public class SipConfig {
this.sipPassword = sipPassword;
}
public String getMediaIp() {
return mediaIp;
}
public void setMediaIp(String mediaIp) {
this.mediaIp = mediaIp;
}
public Integer getMediaPort() {
return mediaPort;
}
public void setMediaPort(Integer mediaPort) {
this.mediaPort = mediaPort;
}
public Integer getSpeed() {
return speed;
}

View File

@ -103,9 +103,14 @@ public class DeviceChannel {
private String password;
/**
* 云台控制
* 云台类型
*/
private int PTZType;
/**
* 云台类型描述字符串
*/
private String PTZTypeText;
/**
* 在线/离线
@ -328,6 +333,27 @@ public class DeviceChannel {
public void setPTZType(int PTZType) {
this.PTZType = PTZType;
switch (PTZType) {
case 0:
this.PTZTypeText = "未知";
break;
case 1:
this.PTZTypeText = "球机";
break;
case 2:
this.PTZTypeText = "半球";
break;
case 3:
this.PTZTypeText = "固定枪机";
break;
case 4:
this.PTZTypeText = "遥控枪机";
break;
}
}
public String getPTZTypeText() {
return PTZTypeText;
}
public String getSsrc() {

View File

@ -60,9 +60,7 @@ public class SIPCommander implements ISIPCommander {
@Qualifier(value="udpSipProvider")
private SipProvider udpSipProvider;
@Value("${media.ip}")
private String mediaIp;
/**
* 云台方向放控制使用配置文件中的默认镜头移动速度
*
@ -207,18 +205,19 @@ public class SIPCommander implements ISIPCommander {
String ssrc = streamSession.createPlaySsrc();
String transport = device.getTransport();
MediaServerConfig mediaInfo = storager.getMediaInfo();
//
StringBuffer content = new StringBuffer(200);
content.append("v=0\r\n");
content.append("o="+channelId+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n");
content.append("o="+channelId+" 0 0 IN IP4 "+mediaInfo.getLocalIP()+"\r\n");
content.append("s=Play\r\n");
content.append("c=IN IP4 "+sipConfig.getMediaIp()+"\r\n");
content.append("c=IN IP4 "+mediaInfo.getLocalIP()+"\r\n");
content.append("t=0 0\r\n");
if("TCP".equals(transport)) {
content.append("m=video "+sipConfig.getMediaPort()+" TCP/RTP/AVP 96 98 97\r\n");
content.append("m=video "+mediaInfo.getRtpProxyPort()+" TCP/RTP/AVP 96 98 97\r\n");
}
if("UDP".equals(transport)) {
content.append("m=video "+sipConfig.getMediaPort()+" RTP/AVP 96 98 97\r\n");
content.append("m=video "+mediaInfo.getRtpProxyPort()+" RTP/AVP 96 98 97\r\n");
}
content.append("a=recvonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
@ -239,16 +238,16 @@ public class SIPCommander implements ISIPCommander {
deviceChannel.setSsrc(ssrc);
storager.updateChannel(device.getDeviceId(), deviceChannel);
}
MediaServerConfig mediaInfo = storager.getMediaInfo();
StreamInfo streamInfo = new StreamInfo();
streamInfo.setSsrc(ssrc);
// String streamId = Integer.toHexString(Integer.parseInt(streamInfo.getSsrc()));
String streamId = String.format("%08x", Integer.parseInt(streamInfo.getSsrc())).toUpperCase(); // ZLM 要求大写且首位补零
streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaIp, mediaInfo.getHttpPort(), streamId));
streamInfo.setWS_FLV(String.format("ws://%s:%s/rtp/%s.flv", mediaIp, mediaInfo.getHttpPort(), streamId));
streamInfo.setRTMP(String.format("rtmp://%s:%s/rtp/%s", mediaIp, mediaInfo.getRtmpPort(), streamId));
streamInfo.setHLS(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaIp, mediaInfo.getHttpPort(), streamId));
streamInfo.setRTSP(String.format("rtsp://%s:%s/rtp/%s", mediaIp, mediaInfo.getRtspPort(), streamId));
streamInfo.setFlv(String.format("http://%s:%s/rtp/%s.flv", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId));
streamInfo.setWS_FLV(String.format("ws://%s:%s/rtp/%s.flv", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId));
streamInfo.setRTMP(String.format("rtmp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtmpPort(), streamId));
streamInfo.setHLS(String.format("http://%s:%s/rtp/%s/hls.m3u8", mediaInfo.getLocalIP(), mediaInfo.getHttpPort(), streamId));
streamInfo.setRTSP(String.format("rtsp://%s:%s/rtp/%s", mediaInfo.getLocalIP(), mediaInfo.getRtspPort(), streamId));
storager.startPlay(device.getDeviceId(), channelId, streamInfo);
return streamInfo;
@ -269,7 +268,7 @@ public class SIPCommander implements ISIPCommander {
@Override
public String playbackStreamCmd(Device device, String channelId, String startTime, String endTime) {
try {
MediaServerConfig mediaInfo = storager.getMediaInfo();
String ssrc = streamSession.createPlayBackSsrc();
//
StringBuffer content = new StringBuffer(200);
@ -277,13 +276,13 @@ public class SIPCommander implements ISIPCommander {
content.append("o="+sipConfig.getSipId()+" 0 0 IN IP4 "+sipConfig.getSipIp()+"\r\n");
content.append("s=Playback\r\n");
content.append("u="+channelId+":0\r\n");
content.append("c=IN IP4 "+sipConfig.getMediaIp()+"\r\n");
content.append("c=IN IP4 "+mediaInfo.getLocalIP()+"\r\n");
content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" "+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n");
if(device.getTransport().equals("TCP")) {
content.append("m=video "+sipConfig.getMediaPort()+" TCP/RTP/AVP 96 98 97\r\n");
content.append("m=video "+mediaInfo.getRtpProxyPort()+" TCP/RTP/AVP 96 98 97\r\n");
}
if(device.getTransport().equals("UDP")) {
content.append("m=video "+sipConfig.getMediaPort()+" RTP/AVP 96 98 97\r\n");
content.append("m=video "+mediaInfo.getRtpProxyPort()+" RTP/AVP 96 98 97\r\n");
}
content.append("a=recvonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
@ -300,6 +299,7 @@ public class SIPCommander implements ISIPCommander {
ClientTransaction transaction = transmitRequest(device, request);
streamSession.put(ssrc, transaction);
return ssrc;
} catch ( SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
return null;
@ -473,6 +473,8 @@ public class SIPCommander implements ISIPCommander {
*/
@Override
public boolean catalogQuery(Device device) {
// 清空通道
storager.cleanChannelsForDevice(device.getDeviceId());
try {
StringBuffer catalogXml = new StringBuffer(200);
catalogXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>");

View File

@ -1,43 +0,0 @@
package com.genersoft.iot.vmp.media.zlm;
import com.google.common.collect.ImmutableMap;
import org.mitre.dsmiley.httpproxy.ProxyServlet;
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 javax.servlet.Servlet;
import java.util.Map;
/**
* 对查询流媒体信息的请求进行反向代理
*/
@Configuration
public class SolrProxyServletConfiguration {
// 读取配置文件中路由设置
@Value("${proxy.servlet_url}")
private String servlet_url;
// 读取配置中代理目标地址
@Value("${proxy.target_url}")
private String target_url;
@Bean
public Servlet createProxyServlet(){
// 创建新的ProxyServlet
return new ProxyServlet();
}
@Bean
public ServletRegistrationBean proxyServletRegistration(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(createProxyServlet(), servlet_url);
//设置网址以及参数
Map<String, String> params = ImmutableMap.of(
"targetUri", target_url,
"log", "true");
registrationBean.setInitParameters(params);
return registrationBean;
}
}

View File

@ -0,0 +1,58 @@
package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import org.apache.http.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
@RestController
@RequestMapping("/zlm")
public class ZLMHTTPProxyController {
private final static Logger logger = LoggerFactory.getLogger(ZLMHTTPProxyController.class);
@Autowired
private IVideoManagerStorager storager;
@ResponseBody
@RequestMapping(value = "/**/**/**", produces = "application/json;charset=UTF-8")
public Object proxy(HttpServletRequest request, HttpServletResponse response){
if (storager.getMediaInfo() == null) {
return "未接入流媒体";
}
String requestURI = String.format("http://%s:%s%s?%s&%s",
storager.getMediaInfo().getLocalIP(),
storager.getMediaInfo().getHttpPort(),
request.getRequestURI().replace("/zlm",""),
storager.getMediaInfo().getHookAdminParams(),
request.getQueryString()
);
// 发送请求
RestTemplate restTemplate = new RestTemplate();
//将指定的url返回的参数自动封装到自定义好的对应类对象中
Object result = null;
try {
result = restTemplate.getForObject(requestURI,Object.class);
}catch (HttpClientErrorException httpClientErrorException) {
response.setStatus(httpClientErrorException.getStatusCode().value());
}
return result;
}
}

View File

@ -8,6 +8,7 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.genersoft.iot.vmp.conf.MediaServerConfig;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.utils.IpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -22,6 +23,8 @@ import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import javax.servlet.http.HttpServletRequest;
/**
* @Description:针对 ZLMediaServer的hook事件监听
* @author: swwheihei
@ -267,7 +270,7 @@ public class ZLMHttpHookListener {
*/
@ResponseBody
@PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
public ResponseEntity<String> onServerStarted(@RequestBody JSONObject json){
public ResponseEntity<String> onServerStarted(HttpServletRequest request, @RequestBody JSONObject json){
if (logger.isDebugEnabled()) {
logger.debug("ZLM HOOK on_server_started API调用参数" + json.toString());

View File

@ -176,4 +176,10 @@ public interface IVideoManagerStorager {
* 更新缓存
*/
public void updateCatch();
/**
* 清空通道
* @param deviceId
*/
void cleanChannelsForDevice(String deviceId);
}

View File

@ -171,4 +171,9 @@ public class VideoManagerJdbcStoragerImpl implements IVideoManagerStorager {
public void updateCatch() {
}
@Override
public void cleanChannelsForDevice(String deviceId) {
}
}

View File

@ -399,13 +399,13 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
for (int i = 0; i < deviceChannelList.size(); i++) {
String key = (String)deviceChannelList.get(i);
String[] s = key.split("_");
String channelId = s[3];
String channelId = s[3].split(":")[0];
HashSet<String> subChannel = channelMap.get(channelId);
if (subChannel == null) {
subChannel = new HashSet<>();
}
if (s.length > 4) {
subChannel.add(s[4]);
if ("null".equals(s[6])) {
subChannel.add(s[6]);
}
channelMap.put(channelId, subChannel);
System.out.println();
@ -414,4 +414,15 @@ public class VideoManagerRedisStoragerImpl implements IVideoManagerStorager {
deviceMap.put(device.getDeviceId(),channelMap);
}
}
@Override
public void cleanChannelsForDevice(String deviceId) {
List<DeviceChannel> result = new ArrayList<>();
List<Object> deviceChannelList = redis.keys(VideoManagerConstants.CACHEKEY_PREFIX + deviceId + "_" + "*");
if (deviceChannelList != null && deviceChannelList.size() > 0 ) {
for (int i = 0; i < deviceChannelList.size(); i++) {
redis.del((String)deviceChannelList.get(i));
}
}
}
}

View File

@ -0,0 +1,48 @@
package com.genersoft.iot.vmp.utils;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IpUtil {
public static String getIpAddr(HttpServletRequest request) {
String ipAddress = null;
try {
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1")) {
// 根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
// 对于通过多个代理的情况第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
// = 15
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
} catch (Exception e) {
ipAddress="";
}
// ipAddress = this.getRequest().getRemoteAddr();
return ipAddress;
}
}

View File

@ -85,9 +85,9 @@ public class DeviceController {
public DeferredResult<ResponseEntity<Device>> devicesSync(@PathVariable String deviceId){
if (logger.isDebugEnabled()) {
logger.debug("设备信息同步API调用deviceId" + deviceId);
}
logger.debug("设备信息同步API调用deviceId" + deviceId);
Device device = storager.queryVideoDevice(deviceId);
cmder.catalogQuery(device);
DeferredResult<ResponseEntity<Device>> result = new DeferredResult<ResponseEntity<Device>>();

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.vmanager.play;
import com.alibaba.fastjson.JSON;
import com.genersoft.iot.vmp.common.StreamInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -43,9 +44,7 @@ public class PlayController {
}
if(streamInfo!=null) {
JSONObject json = new JSONObject();
json.put("ssrc", streamInfo.getSsrc());
return new ResponseEntity<String>(json.toString(),HttpStatus.OK);
return new ResponseEntity<String>(JSON.toJSONString(streamInfo),HttpStatus.OK);
} else {
logger.warn("设备预览API调用失败");
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);

View File

@ -35,17 +35,6 @@ sip:
id: 34020000002000000001
# 默认设备认证密码,后续扩展使用设备单独密码
password: 12345678
media:
# ip: 10.200.64.88
ip: 192.168.1.20
port: 10000
# 自定义代理相关配置
# 代理的本地路由
proxy:
servlet_url: /media/*
# 要代理的地址
target_url: http://127.0.0.1:10080
auth: #32位小写md5加密默认密码为admin
username: admin

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1 +0,0 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title>default</title><script src=./dist/liveplayer-lib.min.js></script><link href=/css/app.ea73b2a1.css rel=preload as=style><link href=/css/chunk-vendors.b5921ce8.css rel=preload as=style><link href=/js/app.d31a42f9.js rel=preload as=script><link href=/js/chunk-vendors.21c802f5.js rel=preload as=script><link href=/css/chunk-vendors.b5921ce8.css rel=stylesheet><link href=/css/app.ea73b2a1.css rel=stylesheet></head><body><noscript><strong>We're sorry but default doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.21c802f5.js></script><script src=/js/app.d31a42f9.js></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>

File diff suppressed because one or more lines are too long

View File

@ -1,63 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>liveplayer</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">
<script type="text/javascript" src="liveplayer-element.min.js"></script>
<script type="text/javascript">
window.onload = function() {
}
window.addEventListener("message", function(event) {
var data = event.data;
switch (data.cmd) {
case 'switchUrl':
// 处理业务逻辑
console.log("收到消息:"+JSON.stringify(data.params));
var player = document.getElementById('player01');
player.setAttribute("video-url",data.params["path"]);
break;
}
});
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
}
}
return (false);
}
function onError(event){
console.log("播放器错误:"+JSON.stringify(event));
}
function sendMsgToParent(cmd,data){
window.parent.postMessage({
cmd: cmd,
params: {
success: true,
data: data
}
}, '*');
}
</script>
</head>
<body>
<live-player id="player01" live="true" stretch="true" show-custom-button="false" autoplay error="onError">
</live-player>
<script>
var videoPath = getQueryVariable("url");
console.log('播放地址:' + videoPath);
var player = document.getElementById('player01');
player.setAttribute("video-url", videoPath);
</script>
</body>
</html>

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB