mirror of
https://gitee.com/pan648540858/wvp-GB28181-pro.git
synced 2026-05-27 07:27:49 +08:00
Merge branch 'wvp-28181-2.0' of gitee.com:pan648540858/wvp-GB28181-pro into wvp-28181-2.0
This commit is contained in:
commit
3c68f088d1
10
.github/ISSUE_TEMPLATE/-------.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/-------.md
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
name: "[ 新功能 ]"
|
||||||
|
about: 新功能
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
29
.github/ISSUE_TEMPLATE/--bug---.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE/--bug---.md
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
name: "[ BUG ] "
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**描述错误**
|
||||||
|
描述下您遇到的问题
|
||||||
|
|
||||||
|
**如何复现**
|
||||||
|
有明确复现步骤的问题会很容易被解决
|
||||||
|
|
||||||
|
**预期行为**
|
||||||
|
清晰简洁的描述您期望发生的事情
|
||||||
|
|
||||||
|
**截图**
|
||||||
|
|
||||||
|
|
||||||
|
**环境信息:**
|
||||||
|
- 1. 部署方式 wvp-pro docker / zlm(docker) + 编译wvp-pro/ wvp-prp + zlm都是编译部署/
|
||||||
|
- 2. 部署环境 windows / ubuntu/ centos ...
|
||||||
|
- 3. 端口开放情况
|
||||||
|
- 4. 是否是公网部署
|
||||||
|
- 5. 是否使用https
|
||||||
|
- 6. 方便的话提供下使用的设备品牌或平台
|
||||||
|
- 7. 你做过哪些尝试
|
||||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,3 +1,3 @@
|
|||||||
[submodule "be.teletask.onvif-java"]
|
[submodule "be.teletask.onvif-java"]
|
||||||
path = be.teletask.onvif-java
|
path = be.teletask.onvif-java
|
||||||
url = https://gitee.com/18010473990/be.teletask.onvif-java.git
|
url = https://gitee.com/pan648540858/be.teletask.onvif-java.git
|
||||||
|
|||||||
18
DOCKERFILE
18
DOCKERFILE
@ -30,25 +30,25 @@ RUN apt-get update && \
|
|||||||
cmake curl vim ca-certificates tzdata libmysqlclient-dev redis-server libssl-dev libx264-dev libfaac-dev ffmpeg
|
cmake curl vim ca-certificates tzdata libmysqlclient-dev redis-server libssl-dev libx264-dev libfaac-dev ffmpeg
|
||||||
WORKDIR /home
|
WORKDIR /home
|
||||||
|
|
||||||
RUN git clone https://gitee.com/18010473990/maven.git && \
|
RUN git clone https://gitee.com/pan648540858/maven.git && \
|
||||||
cp maven/settings.xml /usr/share/maven/conf/ && \
|
cp maven/settings.xml /usr/share/maven/conf/ && \
|
||||||
git clone https://gitee.com/18010473990/wvp-GB28181.git && \
|
git clone https://gitee.com/pan648540858/wvp-GB28181-pro.git && \
|
||||||
git clone https://gitee.com/18010473990/wvp-pro-assist.git
|
git clone https://gitee.com/pan648540858/wvp-pro-assist.git
|
||||||
# 编译前端界面
|
# 编译前端界面
|
||||||
WORKDIR /home/wvp-GB28181/web_src
|
WORKDIR /home/wvp-GB28181-pro/web_src
|
||||||
|
|
||||||
RUN npm install && \
|
RUN npm install && \
|
||||||
npm run build && \
|
npm run build && \
|
||||||
mkdir -p /opt/wvp/config && \
|
mkdir -p /opt/wvp/config && \
|
||||||
mkdir -p /opt/assist/config && \
|
mkdir -p /opt/assist/config && \
|
||||||
cp /home/wvp-GB28181/src/main/resources/application-dev.yml /opt/wvp/config/application.yml && \
|
cp /home/wvp-GB28181-pro/src/main/resources/application-docker.yml /opt/wvp/config/application.yml && \
|
||||||
cp /home/wvp-pro-assist/src/main/resources/application-dev.yml /opt/assist/config/application.yml
|
cp /home/wvp-pro-assist/src/main/resources/application-dev.yml /opt/assist/config/application.yml
|
||||||
|
|
||||||
# wvp打包
|
# wvp打包
|
||||||
WORKDIR /home/wvp-GB28181
|
WORKDIR /home/wvp-GB28181-pro
|
||||||
RUN mvn compile && \
|
RUN mvn compile && \
|
||||||
mvn package && \
|
mvn package && \
|
||||||
cp /home/wvp-GB28181/target/wvp*.jar /opt/wvp/
|
cp /home/wvp-GB28181-pro/target/wvp*.jar /opt/wvp/
|
||||||
|
|
||||||
# wvp 录像管理打包
|
# wvp 录像管理打包
|
||||||
WORKDIR /home/wvp-pro-assist
|
WORKDIR /home/wvp-pro-assist
|
||||||
@ -72,7 +72,7 @@ RUN mkdir -p /opt/media && \
|
|||||||
|
|
||||||
# 清理
|
# 清理
|
||||||
RUN rm -rf /home/wiki && \
|
RUN rm -rf /home/wiki && \
|
||||||
rm -rf /home/wvp-GB28181 && \
|
rm -rf /home/wvp-GB28181-pro && \
|
||||||
apt-get autoremove -y git maven nodejs npm && \
|
apt-get autoremove -y git maven nodejs npm && \
|
||||||
apt-get clean -y && \
|
apt-get clean -y && \
|
||||||
rm -rf /var/lib/apt/lists/*dic
|
rm -rf /var/lib/apt/lists/*dic
|
||||||
@ -89,7 +89,7 @@ RUN echo '#!/bin/bash' > run.sh && \
|
|||||||
echo 'if [${WVP_CONFIG}]; then' >> run.sh && \
|
echo 'if [${WVP_CONFIG}]; then' >> run.sh && \
|
||||||
echo ' java -jar *.jar --spring.config.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 ${WVP_CONFIG}' >> run.sh && \
|
echo ' java -jar *.jar --spring.config.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 ${WVP_CONFIG}' >> run.sh && \
|
||||||
echo 'else' >> run.sh && \
|
echo 'else' >> run.sh && \
|
||||||
echo ' java -jar *.jar --spring.config.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 --media.ip=127.0.0.1 --media.sdp-ip=${WVP_IP} --sip.ip=${WVP_IP} --media.stream-ip=${WVP_IP}' >> run.sh && \
|
echo ' java -jar *.jar --spring.config.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 --media.ip=127.0.0.1 --media.hook-ip=127.0.0.1 --media.sdp-ip=${WVP_IP} --sip.ip=${WVP_IP} --media.stream-ip=${WVP_IP}' >> run.sh && \
|
||||||
echo 'fi' >> run.sh
|
echo 'fi' >> run.sh
|
||||||
RUN chmod +x run.sh
|
RUN chmod +x run.sh
|
||||||
|
|
||||||
|
|||||||
18
README.md
18
README.md
@ -1,4 +1,4 @@
|
|||||||

|

|
||||||
# 开箱即用的的28181协议视频平台
|
# 开箱即用的的28181协议视频平台
|
||||||
|
|
||||||
[](https://travis-ci.org/xia-chu/ZLMediaKit)
|
[](https://travis-ci.org/xia-chu/ZLMediaKit)
|
||||||
@ -13,13 +13,7 @@ WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的开箱即用的网
|
|||||||
流媒体服务基于ZLMediaKit-https://github.com/xiongziliang/ZLMediaKit
|
流媒体服务基于ZLMediaKit-https://github.com/xiongziliang/ZLMediaKit
|
||||||
|
|
||||||
前端页面基于MediaServerUI进行修改.
|
前端页面基于MediaServerUI进行修改.
|
||||||
# 快速体验
|
|
||||||
```shell
|
|
||||||
docker pull 648540858/wvp_pro
|
|
||||||
|
|
||||||
docker run --env WVP_IP="你的IP" -it -p 18080:18080 -p 30000-30500:30000-30500/udp -p 30000-30500:30000-30500/tcp -p 80:80 -p 5060:5060 -p 5060:5060/udp 648540858/wvp_pro
|
|
||||||
```
|
|
||||||
docker使用详情查看:[https://hub.docker.com/r/648540858/wvp_pro](https://hub.docker.com/r/648540858/wvp_pro)
|
|
||||||
# 应用场景:
|
# 应用场景:
|
||||||
支持浏览器无插件播放摄像头视频。
|
支持浏览器无插件播放摄像头视频。
|
||||||
支持摄像机、平台、NVR等设备接入。
|
支持摄像机、平台、NVR等设备接入。
|
||||||
@ -34,7 +28,7 @@ docker使用详情查看:[https://hub.docker.com/r/648540858/wvp_pro](https://
|
|||||||
[https://github.com/648540858/wvp-GB28181-pro/wiki](https://github.com/648540858/wvp-GB28181-pro/wiki)
|
[https://github.com/648540858/wvp-GB28181-pro/wiki](https://github.com/648540858/wvp-GB28181-pro/wiki)
|
||||||
|
|
||||||
# gitee同步仓库
|
# gitee同步仓库
|
||||||
https://gitee.com/18010473990/wvp-GB28181.git
|
https://gitee.com/pan648540858/wvp-GB28181-pro.git
|
||||||
|
|
||||||
# 截图
|
# 截图
|
||||||

|

|
||||||
@ -112,10 +106,16 @@ https://gitee.com/18010473990/wvp-GB28181.git
|
|||||||
- [ ] 添加用户管理
|
- [ ] 添加用户管理
|
||||||
- [X] WEB端支持播放H264与H265,音频支持G.711A/G.711U/AAC,覆盖国标常用编码格式。
|
- [X] WEB端支持播放H264与H265,音频支持G.711A/G.711U/AAC,覆盖国标常用编码格式。
|
||||||
|
|
||||||
|
# docker快速体验
|
||||||
|
```shell
|
||||||
|
docker pull 648540858/wvp_pro
|
||||||
|
|
||||||
|
docker run --env WVP_IP="你的IP" -it -p 18080:18080 -p 30000-30500:30000-30500/udp -p 30000-30500:30000-30500/tcp -p 80:80 -p 5060:5060 -p 5060:5060/udp 648540858/wvp_pro
|
||||||
|
```
|
||||||
|
docker使用详情查看:[https://hub.docker.com/r/648540858/wvp_pro](https://hub.docker.com/r/648540858/wvp_pro)
|
||||||
|
|
||||||
# gitee同步仓库
|
# gitee同步仓库
|
||||||
https://gitee.com/18010473990/wvp-GB28181.git
|
https://gitee.com/pan648540858/wvp-GB28181-pro.git
|
||||||
|
|
||||||
# 使用帮助
|
# 使用帮助
|
||||||
QQ群: 901799015, 690854210(ZLM大群)
|
QQ群: 901799015, 690854210(ZLM大群)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
FROM ubuntu:20.04 AS build
|
FROM ubuntu:20.04 AS build
|
||||||
|
|
||||||
ARG gitUrl="https://gitee.com/18010473990"
|
ARG gitUrl="https://gitee.com/pan648540858"
|
||||||
ARG zlmGitUrl="https://gitee.com/xia-chu/ZLMediaKit"
|
ARG zlmGitUrl="https://gitee.com/xia-chu/ZLMediaKit"
|
||||||
|
|
||||||
RUN export DEBIAN_FRONTEND=noninteractive &&\
|
RUN export DEBIAN_FRONTEND=noninteractive &&\
|
||||||
@ -9,9 +9,9 @@ RUN export DEBIAN_FRONTEND=noninteractive &&\
|
|||||||
cmake ca-certificates openssl ffmpeg
|
cmake ca-certificates openssl ffmpeg
|
||||||
|
|
||||||
RUN cd /home && \
|
RUN cd /home && \
|
||||||
git clone "https://gitee.com/18010473990/maven.git" && \
|
git clone "https://gitee.com/pan648540858/maven.git" && \
|
||||||
cp maven/settings.xml /usr/share/maven/conf/ && \
|
cp maven/settings.xml /usr/share/maven/conf/ && \
|
||||||
git clone "${gitUrl}/wvp-GB28181.git" && \
|
git clone "${gitUrl}/wvp-GB28181-pro.git" && \
|
||||||
git clone "${gitUrl}/wvp-pro-assist.git" && \
|
git clone "${gitUrl}/wvp-pro-assist.git" && \
|
||||||
git clone --depth=1 "${zlmGitUrl}" && \
|
git clone --depth=1 "${zlmGitUrl}" && \
|
||||||
mkdir -p /opt/wvp/config /opt/assist/config /opt/media/www/record
|
mkdir -p /opt/wvp/config /opt/assist/config /opt/media/www/record
|
||||||
@ -23,7 +23,7 @@ RUN cd /home/wvp-GB28181/web_src && \
|
|||||||
RUN cd /home/wvp-GB28181 && \
|
RUN cd /home/wvp-GB28181 && \
|
||||||
mvn clean package -Dmaven.test.skip=true && \
|
mvn clean package -Dmaven.test.skip=true && \
|
||||||
cp /home/wvp-GB28181/target/*.jar /opt/wvp/ && \
|
cp /home/wvp-GB28181/target/*.jar /opt/wvp/ && \
|
||||||
cp /home/wvp-GB28181/src/main/resources/application-dev.yml /opt/wvp/config/application.yml
|
cp /home/wvp-GB28181/src/main/resources/application-docker.yml /opt/wvp/config/application.yml
|
||||||
|
|
||||||
RUN cd /home/wvp-pro-assist && \
|
RUN cd /home/wvp-pro-assist && \
|
||||||
mvn clean package -Dmaven.test.skip=true && \
|
mvn clean package -Dmaven.test.skip=true && \
|
||||||
|
|||||||
Binary file not shown.
11
pom.xml
11
pom.xml
@ -212,17 +212,6 @@
|
|||||||
<!-- <version>1.0.8</version>-->
|
<!-- <version>1.0.8</version>-->
|
||||||
<!-- </dependency>-->
|
<!-- </dependency>-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- onvif协议栈 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>be.teletask</groupId>
|
|
||||||
<artifactId>onvif-java</artifactId>
|
|
||||||
<version>1.0.2</version>
|
|
||||||
<scope>system</scope>
|
|
||||||
<systemPath>${project.basedir}/libs/onvif-java-1.0.2.jar</systemPath>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
|||||||
@ -208,6 +208,7 @@ create table stream_proxy
|
|||||||
enable_hls bit null,
|
enable_hls bit null,
|
||||||
enable_mp4 bit null,
|
enable_mp4 bit null,
|
||||||
enable bit not null,
|
enable bit not null,
|
||||||
|
enable_remove_none_reader bit not null,
|
||||||
createTime varchar(50) not null,
|
createTime varchar(50) not null,
|
||||||
primary key (app, stream)
|
primary key (app, stream)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import org.springframework.boot.SpringApplication;
|
|||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
import springfox.documentation.oas.annotations.EnableOpenApi;
|
import springfox.documentation.oas.annotations.EnableOpenApi;
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ public class StreamInfo {
|
|||||||
private String rtsps;
|
private String rtsps;
|
||||||
private String rtc;
|
private String rtc;
|
||||||
private String mediaServerId;
|
private String mediaServerId;
|
||||||
private JSONArray tracks;
|
private Object tracks;
|
||||||
|
|
||||||
public static class TransactionInfo{
|
public static class TransactionInfo{
|
||||||
public String callId;
|
public String callId;
|
||||||
@ -105,11 +105,11 @@ public class StreamInfo {
|
|||||||
this.rtsp = rtsp;
|
this.rtsp = rtsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONArray getTracks() {
|
public Object getTracks() {
|
||||||
return tracks;
|
return tracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTracks(JSONArray tracks) {
|
public void setTracks(Object tracks) {
|
||||||
this.tracks = tracks;
|
this.tracks = tracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,9 @@ package com.genersoft.iot.vmp.common;
|
|||||||
*/
|
*/
|
||||||
public class VideoManagerConstants {
|
public class VideoManagerConstants {
|
||||||
|
|
||||||
public static final String WVP_SERVER_PREFIX = "VMP_wvp_server";
|
public static final String WVP_SERVER_PREFIX = "VMP_SIGNALLING_SERVER_INFO_";
|
||||||
|
|
||||||
|
public static final String WVP_SERVER_STREAM_PUSH_PREFIX = "VMP_SIGNALLING_STREAM_PUSH_";
|
||||||
|
|
||||||
public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_";
|
public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_";
|
||||||
|
|
||||||
@ -25,6 +27,7 @@ public class VideoManagerConstants {
|
|||||||
public static final String PLAYER_PREFIX = "VMP_PLAYER_";
|
public static final String PLAYER_PREFIX = "VMP_PLAYER_";
|
||||||
|
|
||||||
public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_";
|
public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_";
|
||||||
|
public static final String DOWNLOAD_PREFIX = "VMP_DOWNLOAD_";
|
||||||
|
|
||||||
public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_PLATFORM_KEEPLIVE_";
|
public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_PLATFORM_KEEPLIVE_";
|
||||||
|
|
||||||
@ -51,4 +54,7 @@ public class VideoManagerConstants {
|
|||||||
public static final String MEDIA_SSRC_USED_PREFIX = "VMP_media_used_ssrc_";
|
public static final String MEDIA_SSRC_USED_PREFIX = "VMP_media_used_ssrc_";
|
||||||
|
|
||||||
public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_media_transaction_";
|
public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_media_transaction_";
|
||||||
|
|
||||||
|
//************************** redis 消息*********************************
|
||||||
|
public static final String WVP_MSG_STREAM_PUSH_CHANGE_PREFIX = "WVP_MSG_STREAM_PUSH_CHANGE";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,9 +26,10 @@ public class DynamicTask {
|
|||||||
return new ThreadPoolTaskScheduler();
|
return new ThreadPoolTaskScheduler();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String startCron(String key, Runnable task, String corn) {
|
public String startCron(String key, Runnable task, int cycleForCatalog) {
|
||||||
stopCron(key);
|
stopCron(key);
|
||||||
ScheduledFuture future = threadPoolTaskScheduler.schedule(task, new CronTrigger(corn));
|
// scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔
|
||||||
|
ScheduledFuture future = threadPoolTaskScheduler.scheduleWithFixedDelay(task, cycleForCatalog * 1000L);
|
||||||
futureMap.put(key, future);
|
futureMap.put(key, future);
|
||||||
return "startCron";
|
return "startCron";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -163,9 +163,9 @@ public class ProxyServletConfig {
|
|||||||
* 异常处理
|
* 异常处理
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){
|
protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResponse, Exception e){
|
||||||
try {
|
try {
|
||||||
super.handleRequestException(proxyRequest, proxyResonse, e);
|
super.handleRequestException(proxyRequest, proxyResponse, e);
|
||||||
} catch (ServletException servletException) {
|
} catch (ServletException servletException) {
|
||||||
logger.error("录像服务 代理失败: ", e);
|
logger.error("录像服务 代理失败: ", e);
|
||||||
} catch (IOException ioException) {
|
} catch (IOException ioException) {
|
||||||
|
|||||||
@ -0,0 +1,59 @@
|
|||||||
|
package com.genersoft.iot.vmp.conf;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableAsync(proxyTargetClass = true)
|
||||||
|
public class ThreadPoolTaskConfig {
|
||||||
|
|
||||||
|
public static final int cpuNum = Runtime.getRuntime().availableProcessors();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,
|
||||||
|
* 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
|
||||||
|
* 当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核心线程数(默认线程数)
|
||||||
|
*/
|
||||||
|
private static final int corePoolSize = cpuNum;
|
||||||
|
/**
|
||||||
|
* 最大线程数
|
||||||
|
*/
|
||||||
|
private static final int maxPoolSize = cpuNum*2;
|
||||||
|
/**
|
||||||
|
* 允许线程空闲时间(单位:默认为秒)
|
||||||
|
*/
|
||||||
|
private static final int keepAliveTime = 30;
|
||||||
|
/**
|
||||||
|
* 缓冲队列大小
|
||||||
|
*/
|
||||||
|
private static final int queueCapacity = 500;
|
||||||
|
/**
|
||||||
|
* 线程池名前缀
|
||||||
|
*/
|
||||||
|
private static final String threadNamePrefix = "wvp-sip-handle-";
|
||||||
|
|
||||||
|
@Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名
|
||||||
|
public ThreadPoolTaskExecutor taskExecutor() {
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
executor.setCorePoolSize(corePoolSize);
|
||||||
|
executor.setMaxPoolSize(maxPoolSize);
|
||||||
|
executor.setQueueCapacity(queueCapacity);
|
||||||
|
executor.setKeepAliveSeconds(keepAliveTime);
|
||||||
|
executor.setThreadNamePrefix(threadNamePrefix);
|
||||||
|
|
||||||
|
// 线程池对拒绝任务的处理策略
|
||||||
|
// CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
|
||||||
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
|
// 初始化
|
||||||
|
executor.initialize();
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -27,6 +27,8 @@ public class UserSetup {
|
|||||||
|
|
||||||
private Boolean logInDatebase = Boolean.TRUE;
|
private Boolean logInDatebase = Boolean.TRUE;
|
||||||
|
|
||||||
|
private String serverId = "000000";
|
||||||
|
|
||||||
private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
|
private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
|
||||||
|
|
||||||
public Boolean getSavePositionHistory() {
|
public Boolean getSavePositionHistory() {
|
||||||
@ -104,4 +106,12 @@ public class UserSetup {
|
|||||||
public void setLogInDatebase(Boolean logInDatebase) {
|
public void setLogInDatebase(Boolean logInDatebase) {
|
||||||
this.logInDatebase = logInDatebase;
|
this.logInDatebase = logInDatebase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getServerId() {
|
||||||
|
return serverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerId(String serverId) {
|
||||||
|
this.serverId = serverId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
package com.genersoft.iot.vmp.conf;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description: 获取数据库配置
|
|
||||||
* @author: swwheihei
|
|
||||||
* @date: 2020年5月6日 下午2:46:00
|
|
||||||
*/
|
|
||||||
@Configuration("vmConfig")
|
|
||||||
public class VManagerConfig {
|
|
||||||
|
|
||||||
@Value("${spring.application.database:redis}")
|
|
||||||
private String database;
|
|
||||||
|
|
||||||
|
|
||||||
public String getDatabase() {
|
|
||||||
return database;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDatabase(String database) {
|
|
||||||
this.database = database;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,10 @@
|
|||||||
package com.genersoft.iot.vmp.conf;
|
package com.genersoft.iot.vmp.conf;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@ -12,13 +15,22 @@ public class WVPTimerTask {
|
|||||||
private IRedisCatchStorage redisCatchStorage;
|
private IRedisCatchStorage redisCatchStorage;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SipConfig sipConfig;
|
private IMediaServerService mediaServerService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MediaConfig mediaConfig;
|
private UserSetup userSetup;
|
||||||
|
|
||||||
// @Scheduled(cron="0/2 * * * * ? ") //每3秒执行一次
|
@Value("${server.port}")
|
||||||
// public void execute(){
|
private int serverPort;
|
||||||
//// redisCatchStorage.updateWVPInfo();
|
|
||||||
// }
|
@Autowired
|
||||||
|
private SipConfig sipConfig;
|
||||||
|
|
||||||
|
@Scheduled(fixedRate = 2 * 1000) //每3秒执行一次
|
||||||
|
public void execute(){
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
jsonObject.put("ip", sipConfig.getIp());
|
||||||
|
jsonObject.put("port", serverPort);
|
||||||
|
redisCatchStorage.updateWVPInfo(userSetup.getServerId(), jsonObject, 3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181;
|
|||||||
|
|
||||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.ISIPProcessorObserver;
|
||||||
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
|
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
|
||||||
import gov.nist.javax.sip.SipProviderImpl;
|
import gov.nist.javax.sip.SipProviderImpl;
|
||||||
import gov.nist.javax.sip.SipStackImpl;
|
import gov.nist.javax.sip.SipStackImpl;
|
||||||
@ -28,28 +29,12 @@ public class SipLayer{
|
|||||||
private SipConfig sipConfig;
|
private SipConfig sipConfig;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SIPProcessorObserver sipProcessorObserver;
|
private ISIPProcessorObserver sipProcessorObserver;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private SipSubscribe sipSubscribe;
|
|
||||||
|
|
||||||
private SipStackImpl sipStack;
|
private SipStackImpl sipStack;
|
||||||
|
|
||||||
private SipFactory sipFactory;
|
private SipFactory sipFactory;
|
||||||
|
|
||||||
/**
|
|
||||||
* 消息处理器线程池
|
|
||||||
*/
|
|
||||||
private ThreadPoolExecutor processThreadPool;
|
|
||||||
|
|
||||||
public SipLayer() {
|
|
||||||
int processThreadNum = Runtime.getRuntime().availableProcessors() * 10;
|
|
||||||
LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<>(10000);
|
|
||||||
processThreadPool = new ThreadPoolExecutor(processThreadNum,processThreadNum,
|
|
||||||
0L,TimeUnit.MILLISECONDS,processQueue,
|
|
||||||
new ThreadPoolExecutor.CallerRunsPolicy());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Bean("sipFactory")
|
@Bean("sipFactory")
|
||||||
private SipFactory createSipFactory() {
|
private SipFactory createSipFactory() {
|
||||||
|
|||||||
@ -16,6 +16,8 @@ public class RecordInfo {
|
|||||||
|
|
||||||
private String channelId;
|
private String channelId;
|
||||||
|
|
||||||
|
private String sn;
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private int sumNum;
|
private int sumNum;
|
||||||
@ -61,4 +63,12 @@ public class RecordInfo {
|
|||||||
public void setChannelId(String channelId) {
|
public void setChannelId(String channelId) {
|
||||||
this.channelId = channelId;
|
this.channelId = channelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSn() {
|
||||||
|
return sn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSn(String sn) {
|
||||||
|
this.sn = sn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import org.springframework.scheduling.annotation.Scheduled;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.sip.*;
|
import javax.sip.*;
|
||||||
|
import javax.sip.header.CallIdHeader;
|
||||||
|
import javax.sip.message.Response;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -23,6 +25,7 @@ public class SipSubscribe {
|
|||||||
private Map<String, Date> timeSubscribes = new ConcurrentHashMap<>();
|
private Map<String, Date> timeSubscribes = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
// @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次
|
// @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次
|
||||||
|
// @Scheduled(fixedRate= 100 * 60 * 60 )
|
||||||
@Scheduled(cron="0 0 * * * ?") //每小时执行一次, 每个整点
|
@Scheduled(cron="0 0 * * * ?") //每小时执行一次, 每个整点
|
||||||
public void execute(){
|
public void execute(){
|
||||||
logger.info("[定时任务] 清理过期的订阅信息");
|
logger.info("[定时任务] 清理过期的订阅信息");
|
||||||
@ -58,11 +61,15 @@ public class SipSubscribe {
|
|||||||
this.event = event;
|
this.event = event;
|
||||||
if (event instanceof ResponseEvent) {
|
if (event instanceof ResponseEvent) {
|
||||||
ResponseEvent responseEvent = (ResponseEvent)event;
|
ResponseEvent responseEvent = (ResponseEvent)event;
|
||||||
this.type = "response";
|
Response response = responseEvent.getResponse();
|
||||||
this.msg = responseEvent.getResponse().getReasonPhrase();
|
this.dialog = responseEvent.getDialog();
|
||||||
this.statusCode = responseEvent.getResponse().getStatusCode();
|
this.type = "response";
|
||||||
this.callId = responseEvent.getDialog().getCallId().getCallId();
|
if (response != null) {
|
||||||
this.dialog = responseEvent.getDialog();
|
this.msg = response.getReasonPhrase();
|
||||||
|
this.statusCode = response.getStatusCode();
|
||||||
|
}
|
||||||
|
this.callId = ((CallIdHeader)response.getHeader(CallIdHeader.NAME)).getCallId();
|
||||||
|
|
||||||
}else if (event instanceof TimeoutEvent) {
|
}else if (event instanceof TimeoutEvent) {
|
||||||
TimeoutEvent timeoutEvent = (TimeoutEvent)event;
|
TimeoutEvent timeoutEvent = (TimeoutEvent)event;
|
||||||
this.type = "timeout";
|
this.type = "timeout";
|
||||||
|
|||||||
@ -66,6 +66,7 @@ public class PlatformKeepaliveExpireEventLister implements ApplicationListener<P
|
|||||||
storager.updateParentPlatformStatus(event.getPlatformGbID(), false);
|
storager.updateParentPlatformStatus(event.getPlatformGbID(), false);
|
||||||
publisher.platformNotRegisterEventPublish(event.getPlatformGbID());
|
publisher.platformNotRegisterEventPublish(event.getPlatformGbID());
|
||||||
parentPlatformCatch.setKeepAliveReply(0);
|
parentPlatformCatch.setKeepAliveReply(0);
|
||||||
|
redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
|
||||||
}else {
|
}else {
|
||||||
// 再次发送心跳
|
// 再次发送心跳
|
||||||
String callId = sipCommanderForPlatform.keepalive(parentPlatform);
|
String callId = sipCommanderForPlatform.keepalive(parentPlatform);
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
package com.genersoft.iot.vmp.gb28181.transmit;
|
||||||
|
|
||||||
|
import javax.sip.SipListener;
|
||||||
|
|
||||||
|
public interface ISIPProcessorObserver extends SipListener {
|
||||||
|
}
|
||||||
@ -7,6 +7,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.timeout.ITimeoutProcessor;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.sip.*;
|
import javax.sip.*;
|
||||||
@ -22,7 +25,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
* @date: 2021年11月5日 下午15:32
|
* @date: 2021年11月5日 下午15:32
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class SIPProcessorObserver implements SipListener {
|
public class SIPProcessorObserver implements ISIPProcessorObserver {
|
||||||
|
|
||||||
private final static Logger logger = LoggerFactory.getLogger(SIPProcessorObserver.class);
|
private final static Logger logger = LoggerFactory.getLogger(SIPProcessorObserver.class);
|
||||||
|
|
||||||
@ -33,6 +36,10 @@ public class SIPProcessorObserver implements SipListener {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SipSubscribe sipSubscribe;
|
private SipSubscribe sipSubscribe;
|
||||||
|
|
||||||
|
// @Autowired
|
||||||
|
// @Qualifier(value = "taskExecutor")
|
||||||
|
// private ThreadPoolTaskExecutor poolTaskExecutor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加 request订阅
|
* 添加 request订阅
|
||||||
* @param method 方法名
|
* @param method 方法名
|
||||||
@ -64,6 +71,7 @@ public class SIPProcessorObserver implements SipListener {
|
|||||||
* @param requestEvent RequestEvent事件
|
* @param requestEvent RequestEvent事件
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Async
|
||||||
public void processRequest(RequestEvent requestEvent) {
|
public void processRequest(RequestEvent requestEvent) {
|
||||||
String method = requestEvent.getRequest().getMethod();
|
String method = requestEvent.getRequest().getMethod();
|
||||||
ISIPRequestProcessor sipRequestProcessor = requestProcessorMap.get(method);
|
ISIPRequestProcessor sipRequestProcessor = requestProcessorMap.get(method);
|
||||||
@ -72,6 +80,7 @@ public class SIPProcessorObserver implements SipListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
requestProcessorMap.get(method).process(requestEvent);
|
requestProcessorMap.get(method).process(requestEvent);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,18 +88,9 @@ public class SIPProcessorObserver implements SipListener {
|
|||||||
* @param responseEvent responseEvent事件
|
* @param responseEvent responseEvent事件
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Async
|
||||||
public void processResponse(ResponseEvent responseEvent) {
|
public void processResponse(ResponseEvent responseEvent) {
|
||||||
logger.debug(responseEvent.getResponse().toString());
|
logger.debug(responseEvent.getResponse().toString());
|
||||||
// CSeqHeader cseqHeader = (CSeqHeader) responseEvent.getResponse().getHeader(CSeqHeader.NAME);
|
|
||||||
// String method = cseqHeader.getMethod();
|
|
||||||
// ISIPResponseProcessor sipRequestProcessor = responseProcessorMap.get(method);
|
|
||||||
// if (sipRequestProcessor == null) {
|
|
||||||
// logger.warn("不支持方法{}的response", method);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// sipRequestProcessor.process(responseEvent);
|
|
||||||
|
|
||||||
|
|
||||||
Response response = responseEvent.getResponse();
|
Response response = responseEvent.getResponse();
|
||||||
logger.debug(responseEvent.getResponse().toString());
|
logger.debug(responseEvent.getResponse().toString());
|
||||||
int status = response.getStatusCode();
|
int status = response.getStatusCode();
|
||||||
@ -126,8 +126,12 @@ public class SIPProcessorObserver implements SipListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (responseEvent.getDialog() != null) {
|
||||||
|
responseEvent.getDialog().delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,18 +147,15 @@ public class SIPProcessorObserver implements SipListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processIOException(IOExceptionEvent exceptionEvent) {
|
public void processIOException(IOExceptionEvent exceptionEvent) {
|
||||||
// System.out.println("processIOException");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
|
public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
|
||||||
// System.out.println("processTransactionTerminated");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
|
public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
|
||||||
CallIdHeader callId = dialogTerminatedEvent.getDialog().getCallId();
|
CallIdHeader callId = dialogTerminatedEvent.getDialog().getCallId();
|
||||||
System.out.println("processDialogTerminated:::::" + callId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -53,7 +53,7 @@ public class CheckForAllRecordsThread extends Thread {
|
|||||||
// 自然顺序排序, 元素进行升序排列
|
// 自然顺序排序, 元素进行升序排列
|
||||||
this.recordInfo.getRecordList().sort(Comparator.naturalOrder());
|
this.recordInfo.getRecordList().sort(Comparator.naturalOrder());
|
||||||
RequestMessage msg = new RequestMessage();
|
RequestMessage msg = new RequestMessage();
|
||||||
msg.setKey(DeferredResultHolder.CALLBACK_CMD_RECORDINFO + recordInfo.getDeviceId() + recordInfo.getChannelId());
|
msg.setKey(DeferredResultHolder.CALLBACK_CMD_RECORDINFO + recordInfo.getDeviceId() + recordInfo.getSn());
|
||||||
msg.setData(recordInfo);
|
msg.setData(recordInfo);
|
||||||
deferredResultHolder.invokeAllResult(msg);
|
deferredResultHolder.invokeAllResult(msg);
|
||||||
logger.info("处理完成,返回结果");
|
logger.info("处理完成,返回结果");
|
||||||
|
|||||||
@ -35,9 +35,11 @@ public class DeferredResultHolder {
|
|||||||
|
|
||||||
public static final String CALLBACK_CMD_PLAY = "CALLBACK_PLAY";
|
public static final String CALLBACK_CMD_PLAY = "CALLBACK_PLAY";
|
||||||
|
|
||||||
public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
|
public static final String CALLBACK_CMD_PLAYBACK = "CALLBACK_PLAY";
|
||||||
|
|
||||||
public static final String CALLBACK_ONVIF = "CALLBACK_ONVIF";
|
public static final String CALLBACK_CMD_DOWNLOAD = "CALLBACK_DOWNLOAD";
|
||||||
|
|
||||||
|
public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
|
||||||
|
|
||||||
public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
|
public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
|
||||||
|
|
||||||
@ -110,7 +112,7 @@ public class DeferredResultHolder {
|
|||||||
if (result == null) {
|
if (result == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK));
|
result.setResult(ResponseEntity.ok().body(msg.getData()));
|
||||||
}
|
}
|
||||||
map.remove(msg.getKey());
|
map.remove(msg.getKey());
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.genersoft.iot.vmp.gb28181.transmit.cmd;
|
package com.genersoft.iot.vmp.gb28181.transmit.cmd;
|
||||||
|
|
||||||
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||||
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
|
||||||
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
|
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
|
||||||
@ -121,6 +122,26 @@ public interface ISIPCommander {
|
|||||||
void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent);
|
void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent);
|
||||||
void streamByeCmd(String deviceId, String channelId);
|
void streamByeCmd(String deviceId, String channelId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回放暂停
|
||||||
|
*/
|
||||||
|
void playPauseCmd(Device device, StreamInfo streamInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回放恢复
|
||||||
|
*/
|
||||||
|
void playResumeCmd(Device device, StreamInfo streamInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回放拖动播放
|
||||||
|
*/
|
||||||
|
void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回放倍速播放
|
||||||
|
*/
|
||||||
|
void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 语音广播
|
* 语音广播
|
||||||
*
|
*
|
||||||
@ -235,8 +256,9 @@ public interface ISIPCommander {
|
|||||||
* @param device 视频设备
|
* @param device 视频设备
|
||||||
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
|
* @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||||
|
* @param sn
|
||||||
*/
|
*/
|
||||||
boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime);
|
boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, SipSubscribe.Event errorEvent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询报警信息
|
* 查询报警信息
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import javax.sip.Dialog;
|
||||||
import javax.sip.InvalidArgumentException;
|
import javax.sip.InvalidArgumentException;
|
||||||
import javax.sip.PeerUnavailableException;
|
import javax.sip.PeerUnavailableException;
|
||||||
import javax.sip.SipFactory;
|
import javax.sip.SipFactory;
|
||||||
@ -11,6 +12,9 @@ import javax.sip.address.SipURI;
|
|||||||
import javax.sip.header.*;
|
import javax.sip.header.*;
|
||||||
import javax.sip.message.Request;
|
import javax.sip.message.Request;
|
||||||
|
|
||||||
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
|
||||||
|
import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@ -30,6 +34,9 @@ public class SIPRequestHeaderProvider {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SipFactory sipFactory;
|
private SipFactory sipFactory;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private VideoStreamSessionManager streamSession;
|
||||||
|
|
||||||
public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
|
public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
|
||||||
Request request = null;
|
Request request = null;
|
||||||
@ -210,4 +217,50 @@ public class SIPRequestHeaderProvider {
|
|||||||
request.setContent(content, contentTypeHeader);
|
request.setContent(content, contentTypeHeader);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Request createInfoRequest(Device device, StreamInfo streamInfo, String content)
|
||||||
|
throws PeerUnavailableException, ParseException, InvalidArgumentException {
|
||||||
|
Request request = null;
|
||||||
|
Dialog dialog = streamSession.getDialog(streamInfo.getDeviceID(), streamInfo.getChannelId());
|
||||||
|
|
||||||
|
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(),
|
||||||
|
device.getHostAddress());
|
||||||
|
// via
|
||||||
|
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
|
||||||
|
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(),
|
||||||
|
device.getTransport(), null);
|
||||||
|
viaHeader.setRPort();
|
||||||
|
viaHeaders.add(viaHeader);
|
||||||
|
// from
|
||||||
|
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),
|
||||||
|
sipConfig.getDomain());
|
||||||
|
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
|
||||||
|
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, dialog.getLocalTag());
|
||||||
|
// to
|
||||||
|
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(streamInfo.getChannelId(),
|
||||||
|
sipConfig.getDomain());
|
||||||
|
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
|
||||||
|
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, dialog.getRemoteTag());
|
||||||
|
|
||||||
|
// callid
|
||||||
|
CallIdHeader callIdHeader = dialog.getCallId();
|
||||||
|
|
||||||
|
// Forwards
|
||||||
|
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
|
||||||
|
|
||||||
|
// ceq
|
||||||
|
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory()
|
||||||
|
.createCSeqHeader(InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()), Request.INFO);
|
||||||
|
|
||||||
|
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,
|
||||||
|
fromHeader, toHeader, viaHeaders, maxForwards);
|
||||||
|
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
|
||||||
|
.createSipURI(sipConfig.getId(), sipConfig.getIp() + ":" + sipConfig.getPort()));
|
||||||
|
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
|
||||||
|
|
||||||
|
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application",
|
||||||
|
"MANSRTSP");
|
||||||
|
request.setContent(content, contentTypeHeader);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
|
package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||||
import com.genersoft.iot.vmp.conf.UserSetup;
|
import com.genersoft.iot.vmp.conf.UserSetup;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||||
@ -17,6 +18,7 @@ import com.genersoft.iot.vmp.service.IMediaServerService;
|
|||||||
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
|
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
|
||||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||||
|
import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache;
|
||||||
import gov.nist.javax.sip.SipProviderImpl;
|
import gov.nist.javax.sip.SipProviderImpl;
|
||||||
import gov.nist.javax.sip.SipStackImpl;
|
import gov.nist.javax.sip.SipStackImpl;
|
||||||
import gov.nist.javax.sip.message.SIPRequest;
|
import gov.nist.javax.sip.message.SIPRequest;
|
||||||
@ -1194,14 +1196,15 @@ public class SIPCommander implements ISIPCommander {
|
|||||||
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
* @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime) {
|
public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, SipSubscribe.Event errorEvent) {
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
StringBuffer recordInfoXml = new StringBuffer(200);
|
StringBuffer recordInfoXml = new StringBuffer(200);
|
||||||
recordInfoXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
|
recordInfoXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
|
||||||
recordInfoXml.append("<Query>\r\n");
|
recordInfoXml.append("<Query>\r\n");
|
||||||
recordInfoXml.append("<CmdType>RecordInfo</CmdType>\r\n");
|
recordInfoXml.append("<CmdType>RecordInfo</CmdType>\r\n");
|
||||||
recordInfoXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
|
recordInfoXml.append("<SN>" + sn + "</SN>\r\n");
|
||||||
recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
|
recordInfoXml.append("<DeviceID>" + channelId + "</DeviceID>\r\n");
|
||||||
recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");
|
recordInfoXml.append("<StartTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "</StartTime>\r\n");
|
||||||
recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n");
|
recordInfoXml.append("<EndTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "</EndTime>\r\n");
|
||||||
@ -1218,7 +1221,7 @@ public class SIPCommander implements ISIPCommander {
|
|||||||
Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(),
|
Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(),
|
||||||
"z9hG4bK-ViaRecordInfo-" + tm, "fromRec" + tm, null, callIdHeader);
|
"z9hG4bK-ViaRecordInfo-" + tm, "fromRec" + tm, null, callIdHeader);
|
||||||
|
|
||||||
transmitRequest(device, request);
|
transmitRequest(device, request, errorEvent);
|
||||||
} catch (SipException | ParseException | InvalidArgumentException e) {
|
} catch (SipException | ParseException | InvalidArgumentException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return false;
|
return false;
|
||||||
@ -1486,7 +1489,7 @@ public class SIPCommander implements ISIPCommander {
|
|||||||
StringBuffer cmdXml = new StringBuffer(200);
|
StringBuffer cmdXml = new StringBuffer(200);
|
||||||
cmdXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
|
cmdXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
|
||||||
cmdXml.append("<Query>\r\n");
|
cmdXml.append("<Query>\r\n");
|
||||||
cmdXml.append("<CmdType>CataLog</CmdType>\r\n");
|
cmdXml.append("<CmdType>Catalog</CmdType>\r\n");
|
||||||
cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
|
cmdXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
|
||||||
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
|
cmdXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
|
||||||
cmdXml.append("</Query>\r\n");
|
cmdXml.append("</Query>\r\n");
|
||||||
@ -1496,7 +1499,7 @@ public class SIPCommander implements ISIPCommander {
|
|||||||
CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
|
CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
|
||||||
: udpSipProvider.getNewCallId();
|
: udpSipProvider.getNewCallId();
|
||||||
|
|
||||||
Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, device.getSubscribeCycleForCatalog(), "presence" , callIdHeader);
|
Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, device.getSubscribeCycleForCatalog(), "Catalog" , callIdHeader);
|
||||||
transmitRequest(device, request, errorEvent, okEvent);
|
transmitRequest(device, request, errorEvent, okEvent);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1543,4 +1546,111 @@ public class SIPCommander implements ISIPCommander {
|
|||||||
clientTransaction.sendRequest();
|
clientTransaction.sendRequest();
|
||||||
return clientTransaction;
|
return clientTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回放暂停
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void playPauseCmd(Device device, StreamInfo streamInfo) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
StringBuffer content = new StringBuffer(200);
|
||||||
|
content.append("PAUSE RTSP/1.0\r\n");
|
||||||
|
content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n");
|
||||||
|
content.append("PauseTime: now\r\n");
|
||||||
|
Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
|
||||||
|
logger.info(request.toString());
|
||||||
|
ClientTransaction clientTransaction = null;
|
||||||
|
if ("TCP".equals(device.getTransport())) {
|
||||||
|
clientTransaction = tcpSipProvider.getNewClientTransaction(request);
|
||||||
|
} else if ("UDP".equals(device.getTransport())) {
|
||||||
|
clientTransaction = udpSipProvider.getNewClientTransaction(request);
|
||||||
|
}
|
||||||
|
if (clientTransaction != null) {
|
||||||
|
clientTransaction.sendRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (SipException | ParseException | InvalidArgumentException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回放恢复
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void playResumeCmd(Device device, StreamInfo streamInfo) {
|
||||||
|
try {
|
||||||
|
StringBuffer content = new StringBuffer(200);
|
||||||
|
content.append("PLAY RTSP/1.0\r\n");
|
||||||
|
content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n");
|
||||||
|
content.append("Range: npt=now-\r\n");
|
||||||
|
Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
|
||||||
|
logger.info(request.toString());
|
||||||
|
ClientTransaction clientTransaction = null;
|
||||||
|
if ("TCP".equals(device.getTransport())) {
|
||||||
|
clientTransaction = tcpSipProvider.getNewClientTransaction(request);
|
||||||
|
} else if ("UDP".equals(device.getTransport())) {
|
||||||
|
clientTransaction = udpSipProvider.getNewClientTransaction(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
clientTransaction.sendRequest();
|
||||||
|
|
||||||
|
} catch (SipException | ParseException | InvalidArgumentException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回放拖动播放
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) {
|
||||||
|
try {
|
||||||
|
StringBuffer content = new StringBuffer(200);
|
||||||
|
content.append("PLAY RTSP/1.0\r\n");
|
||||||
|
content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n");
|
||||||
|
content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n");
|
||||||
|
|
||||||
|
Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
|
||||||
|
logger.info(request.toString());
|
||||||
|
ClientTransaction clientTransaction = null;
|
||||||
|
if ("TCP".equals(device.getTransport())) {
|
||||||
|
clientTransaction = tcpSipProvider.getNewClientTransaction(request);
|
||||||
|
} else if ("UDP".equals(device.getTransport())) {
|
||||||
|
clientTransaction = udpSipProvider.getNewClientTransaction(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
clientTransaction.sendRequest();
|
||||||
|
|
||||||
|
} catch (SipException | ParseException | InvalidArgumentException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回放倍速播放
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) {
|
||||||
|
try {
|
||||||
|
StringBuffer content = new StringBuffer(200);
|
||||||
|
content.append("PLAY RTSP/1.0\r\n");
|
||||||
|
content.append("CSeq: " + InfoCseqCache.CSEQCACHE.get(streamInfo.getStreamId()) + "\r\n");
|
||||||
|
content.append("Scale: " + String.format("%.1f",speed) + "\r\n");
|
||||||
|
Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString());
|
||||||
|
logger.info(request.toString());
|
||||||
|
ClientTransaction clientTransaction = null;
|
||||||
|
if ("TCP".equals(device.getTransport())) {
|
||||||
|
clientTransaction = tcpSipProvider.getNewClientTransaction(request);
|
||||||
|
} else if ("UDP".equals(device.getTransport())) {
|
||||||
|
clientTransaction = udpSipProvider.getNewClientTransaction(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
clientTransaction.sendRequest();
|
||||||
|
|
||||||
|
} catch (SipException | ParseException | InvalidArgumentException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,10 +25,8 @@ import javax.sip.header.ToHeader;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description:ACK请求处理器
|
* SIP命令类型: ACK请求
|
||||||
* @author: swwheihei
|
|
||||||
* @date: 2020年5月3日 下午5:31:45
|
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
||||||
|
|||||||
@ -28,15 +28,14 @@ import java.text.ParseException;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: BYE请求处理器
|
* SIP命令类型: BYE请求
|
||||||
* @author: lawrencehj
|
|
||||||
* @date: 2021年3月9日
|
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class ByeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
public class ByeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
||||||
|
|
||||||
private Logger logger = LoggerFactory.getLogger(ByeRequestProcessor.class);
|
private final Logger logger = LoggerFactory.getLogger(ByeRequestProcessor.class);
|
||||||
|
private final String method = "BYE";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISIPCommander cmder;
|
private ISIPCommander cmder;
|
||||||
@ -53,8 +52,6 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
|
|||||||
@Autowired
|
@Autowired
|
||||||
private IMediaServerService mediaServerService;
|
private IMediaServerService mediaServerService;
|
||||||
|
|
||||||
private String method = "BYE";
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SIPProcessorObserver sipProcessorObserver;
|
private SIPProcessorObserver sipProcessorObserver;
|
||||||
|
|
||||||
|
|||||||
@ -9,10 +9,8 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
import javax.sip.RequestEvent;
|
import javax.sip.RequestEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description:CANCEL请求处理器
|
* SIP命令类型: CANCEL请求
|
||||||
* @author: swwheihei
|
|
||||||
* @date: 2020年5月3日 下午5:32:23
|
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class CancelRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
public class CancelRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
||||||
|
|||||||
@ -33,10 +33,8 @@ import javax.sip.message.Response;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description:处理INVITE请求
|
* SIP命令类型: INVITE请求
|
||||||
* @author: panll
|
|
||||||
* @date: 2021年1月14日
|
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
@Component
|
@Component
|
||||||
@ -140,12 +138,21 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
|
|||||||
// 解析sdp消息, 使用jainsip 自带的sdp解析方式
|
// 解析sdp消息, 使用jainsip 自带的sdp解析方式
|
||||||
String contentString = new String(request.getRawContent());
|
String contentString = new String(request.getRawContent());
|
||||||
|
|
||||||
// jainSip不支持y=字段, 移除移除以解析。
|
// jainSip不支持y=字段, 移除以解析。
|
||||||
int ssrcIndex = contentString.indexOf("y=");
|
int ssrcIndex = contentString.indexOf("y=");
|
||||||
//ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段
|
// 检查是否有y字段
|
||||||
String ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
|
String ssrcDefault = "0000000000";
|
||||||
String substring = contentString.substring(0, contentString.indexOf("y="));
|
String ssrc;
|
||||||
SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
|
SessionDescription sdp;
|
||||||
|
if (ssrcIndex >= 0) {
|
||||||
|
//ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段
|
||||||
|
ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
|
||||||
|
String substring = contentString.substring(0, contentString.indexOf("y="));
|
||||||
|
sdp = SdpFactory.getInstance().createSessionDescription(substring);
|
||||||
|
}else {
|
||||||
|
ssrc = ssrcDefault;
|
||||||
|
sdp = SdpFactory.getInstance().createSessionDescription(contentString);
|
||||||
|
}
|
||||||
|
|
||||||
// 获取支持的格式
|
// 获取支持的格式
|
||||||
Vector mediaDescriptions = sdp.getMediaDescriptions(true);
|
Vector mediaDescriptions = sdp.getMediaDescriptions(true);
|
||||||
|
|||||||
@ -914,21 +914,20 @@ public class MessageRequestProcessor1 extends SIPRequestProcessorParent implemen
|
|||||||
String uuid = UUID.randomUUID().toString().replace("-", "");
|
String uuid = UUID.randomUUID().toString().replace("-", "");
|
||||||
RecordInfo recordInfo = new RecordInfo();
|
RecordInfo recordInfo = new RecordInfo();
|
||||||
Element rootElement = getRootElement(evt);
|
Element rootElement = getRootElement(evt);
|
||||||
Element deviceIdElement = rootElement.element("DeviceID");
|
String sn = getText(rootElement, "SN");
|
||||||
String channelId = deviceIdElement.getText().toString();
|
String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn;
|
||||||
String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + channelId;
|
|
||||||
if (device != null ) {
|
if (device != null ) {
|
||||||
rootElement = getRootElement(evt, device.getCharset());
|
rootElement = getRootElement(evt, device.getCharset());
|
||||||
}
|
}
|
||||||
recordInfo.setDeviceId(deviceId);
|
recordInfo.setDeviceId(deviceId);
|
||||||
recordInfo.setChannelId(channelId);
|
recordInfo.setSn(sn);
|
||||||
recordInfo.setName(getText(rootElement, "Name"));
|
recordInfo.setName(getText(rootElement, "Name"));
|
||||||
if (getText(rootElement, "SumNum")== null || getText(rootElement, "SumNum") =="") {
|
if (getText(rootElement, "SumNum")== null || getText(rootElement, "SumNum") =="") {
|
||||||
recordInfo.setSumNum(0);
|
recordInfo.setSumNum(0);
|
||||||
} else {
|
} else {
|
||||||
recordInfo.setSumNum(Integer.parseInt(getText(rootElement, "SumNum")));
|
recordInfo.setSumNum(Integer.parseInt(getText(rootElement, "SumNum")));
|
||||||
}
|
}
|
||||||
String sn = getText(rootElement, "SN");
|
|
||||||
Element recordListElement = rootElement.element("RecordList");
|
Element recordListElement = rootElement.element("RecordList");
|
||||||
if (recordListElement == null || recordInfo.getSumNum() == 0) {
|
if (recordListElement == null || recordInfo.getSumNum() == 0) {
|
||||||
logger.info("无录像数据");
|
logger.info("无录像数据");
|
||||||
|
|||||||
@ -35,9 +35,7 @@ import java.text.ParseException;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: Notify请求处理器
|
* SIP命令类型: NOTIFY请求
|
||||||
* @author: lawrencehj
|
|
||||||
* @date: 2021年1月27日
|
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class NotifyRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
public class NotifyRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
||||||
@ -230,8 +228,6 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
|||||||
String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader);
|
String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader);
|
||||||
|
|
||||||
Element rootElement = getRootElement(evt);
|
Element rootElement = getRootElement(evt);
|
||||||
Element deviceIdElement = rootElement.element("DeviceID");
|
|
||||||
String channelId = deviceIdElement.getText();
|
|
||||||
Device device = storager.queryVideoDevice(deviceId);
|
Device device = storager.queryVideoDevice(deviceId);
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
return;
|
return;
|
||||||
@ -254,22 +250,23 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Element eventElement = itemDevice.element("Event");
|
Element eventElement = itemDevice.element("Event");
|
||||||
|
DeviceChannel channel = channelContentHander(itemDevice);
|
||||||
switch (eventElement.getText().toUpperCase()) {
|
switch (eventElement.getText().toUpperCase()) {
|
||||||
case "ON" : // 上线
|
case "ON" : // 上线
|
||||||
logger.info("收到来自设备【{}】的通道上线【{}】通知", device.getDeviceId(), channelId);
|
logger.info("收到来自设备【{}】的通道【{}】上线通知", device.getDeviceId(), channel.getChannelId());
|
||||||
storager.deviceChannelOnline(deviceId, channelId);
|
storager.deviceChannelOnline(deviceId, channel.getChannelId());
|
||||||
// 回复200 OK
|
// 回复200 OK
|
||||||
responseAck(evt, Response.OK);
|
responseAck(evt, Response.OK);
|
||||||
break;
|
break;
|
||||||
case "OFF" : // 离线
|
case "OFF" : // 离线
|
||||||
logger.info("收到来自设备【{}】的通道离线【{}】通知", device.getDeviceId(), channelId);
|
logger.info("收到来自设备【{}】的通道【{}】离线通知", device.getDeviceId(), channel.getChannelId());
|
||||||
storager.deviceChannelOffline(deviceId, channelId);
|
storager.deviceChannelOffline(deviceId, channel.getChannelId());
|
||||||
// 回复200 OK
|
// 回复200 OK
|
||||||
responseAck(evt, Response.OK);
|
responseAck(evt, Response.OK);
|
||||||
break;
|
break;
|
||||||
case "VLOST" : // 视频丢失
|
case "VLOST" : // 视频丢失
|
||||||
logger.info("收到来自设备【{}】的通道视频丢失【{}】通知", device.getDeviceId(), channelId);
|
logger.info("收到来自设备【{}】的通道【{}】视频丢失通知", device.getDeviceId(), channel.getChannelId());
|
||||||
storager.deviceChannelOffline(deviceId, channelId);
|
storager.deviceChannelOffline(deviceId, channel.getChannelId());
|
||||||
// 回复200 OK
|
// 回复200 OK
|
||||||
responseAck(evt, Response.OK);
|
responseAck(evt, Response.OK);
|
||||||
break;
|
break;
|
||||||
@ -278,19 +275,17 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
|||||||
responseAck(evt, Response.OK);
|
responseAck(evt, Response.OK);
|
||||||
break;
|
break;
|
||||||
case "ADD" : // 增加
|
case "ADD" : // 增加
|
||||||
logger.info("收到来自设备【{}】的增加通道【{}】通知", device.getDeviceId(), channelId);
|
logger.info("收到来自设备【{}】的增加通道【{}】通知", device.getDeviceId(), channel.getChannelId());
|
||||||
DeviceChannel deviceChannel = channelContentHander(itemDevice, channelId);
|
storager.updateChannel(deviceId, channel);
|
||||||
storager.updateChannel(deviceId, deviceChannel);
|
|
||||||
responseAck(evt, Response.OK);
|
responseAck(evt, Response.OK);
|
||||||
break;
|
break;
|
||||||
case "DEL" : // 删除
|
case "DEL" : // 删除
|
||||||
logger.info("收到来自设备【{}】的删除通道【{}】通知", device.getDeviceId(), channelId);
|
logger.info("收到来自设备【{}】的删除通道【{}】通知", device.getDeviceId(), channel.getChannelId());
|
||||||
storager.delChannel(deviceId, channelId);
|
storager.delChannel(deviceId, channel.getChannelId());
|
||||||
responseAck(evt, Response.OK);
|
responseAck(evt, Response.OK);
|
||||||
break;
|
break;
|
||||||
case "UPDATE" : // 更新
|
case "UPDATE" : // 更新
|
||||||
logger.info("收到来自设备【{}】的更新通道【{}】通知", device.getDeviceId(), channelId);
|
logger.info("收到来自设备【{}】的更新通道【{}】通知", device.getDeviceId(), channel.getChannelId());
|
||||||
DeviceChannel channel = channelContentHander(itemDevice, channelId);
|
|
||||||
storager.updateChannel(deviceId, channel);
|
storager.updateChannel(deviceId, channel);
|
||||||
responseAck(evt, Response.OK);
|
responseAck(evt, Response.OK);
|
||||||
break;
|
break;
|
||||||
@ -316,13 +311,15 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeviceChannel channelContentHander(Element itemDevice, String channelId){
|
public DeviceChannel channelContentHander(Element itemDevice){
|
||||||
Element channdelNameElement = itemDevice.element("Name");
|
Element channdelNameElement = itemDevice.element("Name");
|
||||||
String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
|
String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
|
||||||
Element statusElement = itemDevice.element("Status");
|
Element statusElement = itemDevice.element("Status");
|
||||||
String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON";
|
String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON";
|
||||||
DeviceChannel deviceChannel = new DeviceChannel();
|
DeviceChannel deviceChannel = new DeviceChannel();
|
||||||
deviceChannel.setName(channelName);
|
deviceChannel.setName(channelName);
|
||||||
|
Element channdelIdElement = itemDevice.element("DeviceID");
|
||||||
|
String channelId = channdelIdElement != null ? channdelIdElement.getTextTrim().toString() : "";
|
||||||
deviceChannel.setChannelId(channelId);
|
deviceChannel.setChannelId(channelId);
|
||||||
// ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理
|
// ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理
|
||||||
if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) {
|
if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) {
|
||||||
|
|||||||
@ -35,10 +35,8 @@ import java.text.ParseException;
|
|||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description:收到注册请求 处理
|
* SIP命令类型: REGISTER请求
|
||||||
* @author: swwheihei
|
|
||||||
* @date: 2020年5月3日 下午4:47:25
|
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class RegisterRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
public class RegisterRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
||||||
|
|||||||
@ -18,10 +18,8 @@ import javax.sip.message.Request;
|
|||||||
import javax.sip.message.Response;
|
import javax.sip.message.Response;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description:SUBSCRIBE请求处理器
|
* SIP命令类型: SUBSCRIBE请求
|
||||||
* @author: swwheihei
|
|
||||||
* @date: 2020年5月3日 下午5:31:20
|
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class SubscribeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
public class SubscribeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor {
|
||||||
|
|||||||
@ -14,10 +14,7 @@ import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText;
|
|||||||
|
|
||||||
public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent implements IMessageHandler{
|
public abstract class MessageHandlerAbstract extends SIPRequestProcessorParent implements IMessageHandler{
|
||||||
|
|
||||||
public static Map<String, IMessageHandler> messageHandlerMap = new ConcurrentHashMap<>();
|
public Map<String, IMessageHandler> messageHandlerMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public MessageRequestProcessor messageRequestProcessor;
|
|
||||||
|
|
||||||
public void addHandler(String cmdType, IMessageHandler messageHandler) {
|
public void addHandler(String cmdType, IMessageHandler messageHandler) {
|
||||||
messageHandlerMap.put(cmdType, messageHandler);
|
messageHandlerMap.put(cmdType, messageHandler);
|
||||||
|
|||||||
@ -6,6 +6,12 @@ import org.springframework.beans.factory.InitializingBean;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令类型: 控制命令
|
||||||
|
* 命令类型: 设备控制: 远程启动, 录像控制(TODO), 报警布防/撤防命令(TODO), 报警复位命令(TODO),
|
||||||
|
* 强制关键帧命令(TODO), 拉框放大/缩小控制命令(TODO), 看守位控制(TODO), 报警复位(TODO)
|
||||||
|
* 命令类型: 设备配置: SVAC编码配置(TODO), 音频参数(TODO), SVAC解码配置(TODO)
|
||||||
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class ControlMessageHandler extends MessageHandlerAbstract implements InitializingBean {
|
public class ControlMessageHandler extends MessageHandlerAbstract implements InitializingBean {
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd;
|
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.cmd;
|
||||||
|
|
||||||
import com.genersoft.iot.vmp.VManageBootstrap;
|
import com.genersoft.iot.vmp.VManageBootstrap;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||||
@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
|||||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
|
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
|
||||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
|
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.ControlMessageHandler;
|
||||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
|
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
|
||||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||||
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
|
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
|
||||||
@ -37,7 +38,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
|
|||||||
private final String cmdType = "DeviceControl";
|
private final String cmdType = "DeviceControl";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private QueryMessageHandler queryMessageHandler;
|
private ControlMessageHandler controlMessageHandler;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IVideoManagerStorager storager;
|
private IVideoManagerStorager storager;
|
||||||
@ -50,7 +51,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
queryMessageHandler.addHandler(cmdType, this);
|
controlMessageHandler.addHandler(cmdType, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1,14 +1,23 @@
|
|||||||
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify;
|
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify;
|
||||||
|
|
||||||
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageHandlerAbstract;
|
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageHandlerAbstract;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.MessageRequestProcessor;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令类型: 通知命令
|
||||||
|
* 命令类型: 状态信息(心跳)报送, 报警通知, 媒体通知, 移动设备位置数据,语音广播通知(TODO), 设备预置位(TODO)
|
||||||
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class NotifyMessageHandler extends MessageHandlerAbstract implements InitializingBean {
|
public class NotifyMessageHandler extends MessageHandlerAbstract implements InitializingBean {
|
||||||
|
|
||||||
private final String messageType = "Notify";
|
private final String messageType = "Notify";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessageRequestProcessor messageRequestProcessor;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
messageRequestProcessor.addHandler(messageType, this);
|
messageRequestProcessor.addHandler(messageType, this);
|
||||||
|
|||||||
@ -46,9 +46,6 @@ public class CatalogNotifyMessageHandler extends SIPRequestProcessorParent imple
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SipConfig config;
|
private SipConfig config;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private EventPublisher publisher;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
notifyMessageHandler.addHandler(cmdType, this);
|
notifyMessageHandler.addHandler(cmdType, this);
|
||||||
|
|||||||
@ -6,6 +6,10 @@ import org.springframework.beans.factory.InitializingBean;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令类型: 查询指令
|
||||||
|
* 命令类型: 设备状态, 设备目录信息, 设备信息, 文件目录检索(TODO), 报警(TODO), 设备配置(TODO), 设备预置位(TODO), 移动设备位置数据(TODO)
|
||||||
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class QueryMessageHandler extends MessageHandlerAbstract implements InitializingBean {
|
public class QueryMessageHandler extends MessageHandlerAbstract implements InitializingBean {
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,77 @@
|
|||||||
|
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.cmd;
|
||||||
|
|
||||||
|
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
|
||||||
|
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||||
|
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
|
||||||
|
import org.dom4j.Element;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.sip.InvalidArgumentException;
|
||||||
|
import javax.sip.RequestEvent;
|
||||||
|
import javax.sip.SipException;
|
||||||
|
import javax.sip.header.FromHeader;
|
||||||
|
import javax.sip.message.Response;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class AlarmQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
|
||||||
|
|
||||||
|
private Logger logger = LoggerFactory.getLogger(AlarmQueryMessageHandler.class);
|
||||||
|
private final String cmdType = "Alarm";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QueryMessageHandler queryMessageHandler;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IVideoManagerStorager storager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SIPCommanderFroPlatform cmderFroPlatform;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SipConfig config;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EventPublisher publisher;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
queryMessageHandler.addHandler(cmdType, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handForDevice(RequestEvent evt, Device device, Element element) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
|
||||||
|
|
||||||
|
logger.info("不支持alarm查询");
|
||||||
|
try {
|
||||||
|
responseAck(evt, Response.NOT_FOUND, "not support alarm query");
|
||||||
|
} catch (SipException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InvalidArgumentException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (ParseException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,10 @@ import org.springframework.beans.factory.InitializingBean;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令类型: 请求动作的应答
|
||||||
|
* 命令类型: 设备控制, 报警通知, 设备目录信息查询, 目录信息查询, 目录收到, 设备信息查询, 设备状态信息查询 ......
|
||||||
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class ResponseMessageHandler extends MessageHandlerAbstract implements InitializingBean {
|
public class ResponseMessageHandler extends MessageHandlerAbstract implements InitializingBean {
|
||||||
|
|
||||||
|
|||||||
@ -64,18 +64,16 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
|
|||||||
rootElement = getRootElement(evt, device.getCharset());
|
rootElement = getRootElement(evt, device.getCharset());
|
||||||
String uuid = UUID.randomUUID().toString().replace("-", "");
|
String uuid = UUID.randomUUID().toString().replace("-", "");
|
||||||
RecordInfo recordInfo = new RecordInfo();
|
RecordInfo recordInfo = new RecordInfo();
|
||||||
Element deviceIdElement = rootElement.element("DeviceID");
|
String sn = getText(rootElement, "SN");
|
||||||
String channelId = deviceIdElement.getText();
|
String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + device.getDeviceId() + sn;
|
||||||
String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + device.getDeviceId() + channelId;
|
|
||||||
recordInfo.setDeviceId(device.getDeviceId());
|
recordInfo.setDeviceId(device.getDeviceId());
|
||||||
recordInfo.setChannelId(channelId);
|
recordInfo.setSn(sn);
|
||||||
recordInfo.setName(getText(rootElement, "Name"));
|
recordInfo.setName(getText(rootElement, "Name"));
|
||||||
if (getText(rootElement, "SumNum") == null || getText(rootElement, "SumNum") == "") {
|
if (getText(rootElement, "SumNum") == null || getText(rootElement, "SumNum") == "") {
|
||||||
recordInfo.setSumNum(0);
|
recordInfo.setSumNum(0);
|
||||||
} else {
|
} else {
|
||||||
recordInfo.setSumNum(Integer.parseInt(getText(rootElement, "SumNum")));
|
recordInfo.setSumNum(Integer.parseInt(getText(rootElement, "SumNum")));
|
||||||
}
|
}
|
||||||
String sn = getText(rootElement, "SN");
|
|
||||||
Element recordListElement = rootElement.element("RecordList");
|
Element recordListElement = rootElement.element("RecordList");
|
||||||
if (recordListElement == null || recordInfo.getSumNum() == 0) {
|
if (recordListElement == null || recordInfo.getSumNum() == 0) {
|
||||||
logger.info("无录像数据");
|
logger.info("无录像数据");
|
||||||
|
|||||||
@ -42,7 +42,6 @@ public class ByeResponseProcessor extends SIPResponseProcessorAbstract {
|
|||||||
@Override
|
@Override
|
||||||
public void process(ResponseEvent evt) {
|
public void process(ResponseEvent evt) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
System.out.println("收到bye");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -3,13 +3,18 @@ package com.genersoft.iot.vmp.media.zlm;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
import com.genersoft.iot.vmp.conf.MediaConfig;
|
import com.genersoft.iot.vmp.conf.MediaConfig;
|
||||||
import com.genersoft.iot.vmp.conf.UserSetup;
|
import com.genersoft.iot.vmp.conf.UserSetup;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||||
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||||
|
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
||||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||||
|
import com.genersoft.iot.vmp.service.IMediaService;
|
||||||
|
import com.genersoft.iot.vmp.service.IStreamProxyService;
|
||||||
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
|
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
|
||||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||||
@ -56,6 +61,12 @@ public class ZLMHttpHookListener {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private IMediaServerService mediaServerService;
|
private IMediaServerService mediaServerService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IStreamProxyService streamProxyService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IMediaService mediaService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ZLMRESTfulUtils zlmresTfulUtils;
|
private ZLMRESTfulUtils zlmresTfulUtils;
|
||||||
|
|
||||||
@ -153,12 +164,20 @@ public class ZLMHttpHookListener {
|
|||||||
subscribe.response(mediaInfo, json);
|
subscribe.response(mediaInfo, json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
String app = json.getString("app");
|
||||||
|
String stream = json.getString("stream");
|
||||||
|
StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(stream);
|
||||||
JSONObject ret = new JSONObject();
|
JSONObject ret = new JSONObject();
|
||||||
|
// 录像回放时不进行录像下载
|
||||||
|
if (streamInfo != null) {
|
||||||
|
ret.put("enableMP4", false);
|
||||||
|
}else {
|
||||||
|
ret.put("enableMP4", userSetup.isRecordPushLive());
|
||||||
|
}
|
||||||
ret.put("code", 0);
|
ret.put("code", 0);
|
||||||
ret.put("msg", "success");
|
ret.put("msg", "success");
|
||||||
ret.put("enableHls", true);
|
ret.put("enableHls", true);
|
||||||
ret.put("enableMP4", userSetup.isRecordPushLive());
|
ret.put("enableMP4", userSetup.isRecordPushLive());
|
||||||
ret.put("enableRtxp", true);
|
|
||||||
return new ResponseEntity<String>(ret.toString(), HttpStatus.OK);
|
return new ResponseEntity<String>(ret.toString(), HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,12 +273,13 @@ public class ZLMHttpHookListener {
|
|||||||
*/
|
*/
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
|
@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
|
||||||
public ResponseEntity<String> onStreamChanged(@RequestBody JSONObject json){
|
public ResponseEntity<String> onStreamChanged(@RequestBody MediaItem item){
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + json.toString());
|
logger.debug("ZLM HOOK on_stream_changed API调用,参数:" + JSONObject.toJSONString(item));
|
||||||
}
|
}
|
||||||
String mediaServerId = json.getString("mediaServerId");
|
String mediaServerId = item.getMediaServerId();
|
||||||
|
JSONObject json = (JSONObject) JSON.toJSON(item);
|
||||||
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, json);
|
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, json);
|
||||||
if (subscribe != null ) {
|
if (subscribe != null ) {
|
||||||
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
|
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
|
||||||
@ -268,13 +288,12 @@ public class ZLMHttpHookListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 流消失移除redis play
|
// 流消失移除redis play
|
||||||
String app = json.getString("app");
|
String app = item.getApp();
|
||||||
String streamId = json.getString("stream");
|
String streamId = item.getStream();
|
||||||
String schema = json.getString("schema");
|
String schema = item.getSchema();
|
||||||
JSONArray tracks = json.getJSONArray("tracks");
|
List<MediaItem.MediaTrack> tracks = item.getTracks();
|
||||||
boolean regist = json.getBoolean("regist");
|
boolean regist = item.isRegist();
|
||||||
if (tracks != null) {
|
if (tracks != null) {
|
||||||
logger.info("[stream: " + streamId + "] on_stream_changed->>" + schema);
|
logger.info("[stream: " + streamId + "] on_stream_changed->>" + schema);
|
||||||
}
|
}
|
||||||
@ -294,12 +313,34 @@ public class ZLMHttpHookListener {
|
|||||||
redisCatchStorage.stopPlayback(streamInfo);
|
redisCatchStorage.stopPlayback(streamInfo);
|
||||||
}
|
}
|
||||||
}else {
|
}else {
|
||||||
if (!"rtp".equals(app) ){
|
if (!"rtp".equals(app)){
|
||||||
|
|
||||||
|
boolean pushChange = false;
|
||||||
|
|
||||||
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
|
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
|
||||||
if (regist) {
|
if (regist) {
|
||||||
zlmMediaListManager.addMedia(mediaServerItem, app, streamId);
|
if ((item.getOriginType() == 1 || item.getOriginType() == 2 || item.getOriginType() == 8)) {
|
||||||
|
pushChange = true;
|
||||||
|
zlmMediaListManager.addMedia(item);
|
||||||
|
StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem, app, streamId, tracks);
|
||||||
|
redisCatchStorage.addPushStream(mediaServerItem, app, streamId, streamInfo);
|
||||||
|
}
|
||||||
}else {
|
}else {
|
||||||
zlmMediaListManager.removeMedia( app, streamId);
|
int result = zlmMediaListManager.removeMedia( app, streamId);
|
||||||
|
redisCatchStorage.removePushStream(mediaServerItem, app, streamId);
|
||||||
|
if (result > 0) {
|
||||||
|
pushChange = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(pushChange) {
|
||||||
|
// 发送流变化redis消息
|
||||||
|
JSONObject jsonObject = new JSONObject();
|
||||||
|
jsonObject.put("serverId", userSetup.getServerId());
|
||||||
|
jsonObject.put("app", app);
|
||||||
|
jsonObject.put("stream", streamId);
|
||||||
|
jsonObject.put("register", regist);
|
||||||
|
jsonObject.put("mediaServerId", mediaServerId);
|
||||||
|
redisCatchStorage.sendStreamChangeMsg(jsonObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -325,14 +366,13 @@ public class ZLMHttpHookListener {
|
|||||||
String mediaServerId = json.getString("mediaServerId");
|
String mediaServerId = json.getString("mediaServerId");
|
||||||
String streamId = json.getString("stream");
|
String streamId = json.getString("stream");
|
||||||
String app = json.getString("app");
|
String app = json.getString("app");
|
||||||
|
JSONObject ret = new JSONObject();
|
||||||
// TODO 如果在给上级推流,也不停止。
|
ret.put("code", 0);
|
||||||
if ("rtp".equals(app)){
|
if ("rtp".equals(app)){
|
||||||
JSONObject ret = new JSONObject();
|
|
||||||
ret.put("code", 0);
|
|
||||||
ret.put("close", true);
|
ret.put("close", true);
|
||||||
StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(streamId);
|
StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(streamId);
|
||||||
if (streamInfoForPlayCatch != null) {
|
if (streamInfoForPlayCatch != null) {
|
||||||
|
// 如果在给上级推流,也不停止。
|
||||||
if (redisCatchStorage.isChannelSendingRTP(streamInfoForPlayCatch.getChannelId())) {
|
if (redisCatchStorage.isChannelSendingRTP(streamInfoForPlayCatch.getChannelId())) {
|
||||||
ret.put("close", false);
|
ret.put("close", false);
|
||||||
} else {
|
} else {
|
||||||
@ -345,6 +385,12 @@ public class ZLMHttpHookListener {
|
|||||||
if (streamInfoForPlayBackCatch != null) {
|
if (streamInfoForPlayBackCatch != null) {
|
||||||
cmder.streamByeCmd(streamInfoForPlayBackCatch.getDeviceID(), streamInfoForPlayBackCatch.getChannelId());
|
cmder.streamByeCmd(streamInfoForPlayBackCatch.getDeviceID(), streamInfoForPlayBackCatch.getChannelId());
|
||||||
redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch);
|
redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch);
|
||||||
|
}else {
|
||||||
|
StreamInfo streamInfoForDownload = redisCatchStorage.queryDownloadByStreamId(streamId);
|
||||||
|
// 进行录像下载时无人观看不断流
|
||||||
|
if (streamInfoForDownload != null) {
|
||||||
|
ret.put("close", false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
|
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
|
||||||
@ -353,9 +399,15 @@ public class ZLMHttpHookListener {
|
|||||||
}
|
}
|
||||||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
|
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
|
||||||
}else {
|
}else {
|
||||||
JSONObject ret = new JSONObject();
|
StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(app, streamId);
|
||||||
ret.put("code", 0);
|
if (streamProxyItem != null && streamProxyItem.isEnable_remove_none_reader()) {
|
||||||
ret.put("close", false);
|
ret.put("close", true);
|
||||||
|
streamProxyService.del(app, streamId);
|
||||||
|
String url = streamProxyItem.getUrl() != null?streamProxyItem.getUrl():streamProxyItem.getSrc_url();
|
||||||
|
logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", app, streamId, url);
|
||||||
|
}else {
|
||||||
|
ret.put("close", false);
|
||||||
|
}
|
||||||
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
|
return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.genersoft.iot.vmp.media.zlm;
|
package com.genersoft.iot.vmp.media.zlm;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
|
||||||
@ -87,6 +88,10 @@ public class ZLMMediaListManager {
|
|||||||
updateMedia(mediaServerItem, app, streamId);
|
updateMedia(mediaServerItem, app, streamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addMedia(MediaItem mediaItem) {
|
||||||
|
storager.updateMedia(streamPushService.transform(mediaItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void updateMedia(MediaServerItem mediaServerItem, String app, String streamId) {
|
public void updateMedia(MediaServerItem mediaServerItem, String app, String streamId) {
|
||||||
//使用异步更新推流
|
//使用异步更新推流
|
||||||
@ -113,14 +118,16 @@ public class ZLMMediaListManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void removeMedia(String app, String streamId) {
|
public int removeMedia(String app, String streamId) {
|
||||||
// 查找是否关联了国标, 关联了不删除, 置为离线
|
// 查找是否关联了国标, 关联了不删除, 置为离线
|
||||||
StreamProxyItem streamProxyItem = gbStreamMapper.selectOne(app, streamId);
|
StreamProxyItem streamProxyItem = gbStreamMapper.selectOne(app, streamId);
|
||||||
|
int result = 0;
|
||||||
if (streamProxyItem == null) {
|
if (streamProxyItem == null) {
|
||||||
storager.removeMedia(app, streamId);
|
result = storager.removeMedia(app, streamId);
|
||||||
}else {
|
}else {
|
||||||
storager.mediaOutline(app, streamId);
|
result =storager.mediaOutline(app, streamId);
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public void clearAllSessions() {
|
// public void clearAllSessions() {
|
||||||
|
|||||||
@ -29,7 +29,6 @@ public class ZLMRESTfulUtils {
|
|||||||
OkHttpClient client = new OkHttpClient();
|
OkHttpClient client = new OkHttpClient();
|
||||||
String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
|
String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
|
||||||
JSONObject responseJSON = null;
|
JSONObject responseJSON = null;
|
||||||
logger.debug(url);
|
|
||||||
|
|
||||||
FormBody.Builder builder = new FormBody.Builder();
|
FormBody.Builder builder = new FormBody.Builder();
|
||||||
builder.add("secret",mediaServerItem.getSecret());
|
builder.add("secret",mediaServerItem.getSecret());
|
||||||
@ -51,8 +50,9 @@ public class ZLMRESTfulUtils {
|
|||||||
try {
|
try {
|
||||||
Response response = client.newCall(request).execute();
|
Response response = client.newCall(request).execute();
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
String responseStr = response.body().string();
|
ResponseBody responseBody = response.body();
|
||||||
if (responseStr != null) {
|
if (responseBody != null) {
|
||||||
|
String responseStr = responseBody.string();
|
||||||
responseJSON = JSON.parseObject(responseStr);
|
responseJSON = JSON.parseObject(responseStr);
|
||||||
}
|
}
|
||||||
}else {
|
}else {
|
||||||
@ -100,7 +100,11 @@ public class ZLMRESTfulUtils {
|
|||||||
public void sendGetForImg(MediaServerItem mediaServerItem, String api, Map<String, Object> params, String targetPath, String fileName) {
|
public void sendGetForImg(MediaServerItem mediaServerItem, String api, Map<String, Object> params, String targetPath, String fileName) {
|
||||||
String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
|
String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
|
||||||
logger.debug(url);
|
logger.debug(url);
|
||||||
HttpUrl.Builder httpBuilder = HttpUrl.parse(url).newBuilder();
|
HttpUrl parseUrl = HttpUrl.parse(url);
|
||||||
|
if (parseUrl == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HttpUrl.Builder httpBuilder = parseUrl.newBuilder();
|
||||||
|
|
||||||
httpBuilder.addQueryParameter("secret", mediaServerItem.getSecret());
|
httpBuilder.addQueryParameter("secret", mediaServerItem.getSecret());
|
||||||
if (params != null) {
|
if (params != null) {
|
||||||
@ -123,16 +127,20 @@ public class ZLMRESTfulUtils {
|
|||||||
if (targetPath != null) {
|
if (targetPath != null) {
|
||||||
File snapFolder = new File(targetPath);
|
File snapFolder = new File(targetPath);
|
||||||
if (!snapFolder.exists()) {
|
if (!snapFolder.exists()) {
|
||||||
snapFolder.mkdirs();
|
if (!snapFolder.mkdirs()) {
|
||||||
|
logger.warn("{}路径创建失败", snapFolder.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
File snapFile = new File(targetPath + "/" + fileName);
|
File snapFile = new File(targetPath + "/" + fileName);
|
||||||
FileOutputStream outStream = new FileOutputStream(snapFile);
|
FileOutputStream outStream = new FileOutputStream(snapFile);
|
||||||
outStream.write(response.body().bytes());
|
|
||||||
|
outStream.write(Objects.requireNonNull(response.body()).bytes());
|
||||||
outStream.close();
|
outStream.close();
|
||||||
} else {
|
} else {
|
||||||
logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message()));
|
logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message()));
|
||||||
}
|
}
|
||||||
response.body().close();
|
Objects.requireNonNull(response.body()).close();
|
||||||
} else {
|
} else {
|
||||||
logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message()));
|
logger.error(String.format("[ %s ]请求失败: %s %s", url, response.code(), response.message()));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,11 @@ import java.util.List;
|
|||||||
|
|
||||||
public class MediaItem {
|
public class MediaItem {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册/注销
|
||||||
|
*/
|
||||||
|
private boolean regist;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用名
|
* 应用名
|
||||||
*/
|
*/
|
||||||
@ -53,6 +58,11 @@ public class MediaItem {
|
|||||||
*/
|
*/
|
||||||
private String originUrl;
|
private String originUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器id
|
||||||
|
*/
|
||||||
|
private String mediaServerId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GMT unix系统时间戳,单位秒
|
* GMT unix系统时间戳,单位秒
|
||||||
*/
|
*/
|
||||||
@ -78,6 +88,14 @@ public class MediaItem {
|
|||||||
*/
|
*/
|
||||||
private String vhost;
|
private String vhost;
|
||||||
|
|
||||||
|
public boolean isRegist() {
|
||||||
|
return regist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegist(boolean regist) {
|
||||||
|
this.regist = regist;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否是docker部署, docker部署不会自动更新zlm使用的端口,需要自己手动修改
|
* 是否是docker部署, docker部署不会自动更新zlm使用的端口,需要自己手动修改
|
||||||
*/
|
*/
|
||||||
@ -376,4 +394,12 @@ public class MediaItem {
|
|||||||
public void setDocker(boolean docker) {
|
public void setDocker(boolean docker) {
|
||||||
this.docker = docker;
|
this.docker = docker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getMediaServerId() {
|
||||||
|
return mediaServerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMediaServerId(String mediaServerId) {
|
||||||
|
this.mediaServerId = mediaServerId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ public class StreamProxyItem extends GbStream {
|
|||||||
private boolean enable;
|
private boolean enable;
|
||||||
private boolean enable_hls;
|
private boolean enable_hls;
|
||||||
private boolean enable_mp4;
|
private boolean enable_mp4;
|
||||||
|
private boolean enable_remove_none_reader; // 无人观看时删除
|
||||||
private String platformGbId;
|
private String platformGbId;
|
||||||
private String createTime;
|
private String createTime;
|
||||||
|
|
||||||
@ -142,4 +143,12 @@ public class StreamProxyItem extends GbStream {
|
|||||||
public void setCreateTime(String createTime) {
|
public void setCreateTime(String createTime) {
|
||||||
this.createTime = createTime;
|
this.createTime = createTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEnable_remove_none_reader() {
|
||||||
|
return enable_remove_none_reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnable_remove_none_reader(boolean enable_remove_none_reader) {
|
||||||
|
this.enable_remove_none_reader = enable_remove_none_reader;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
package com.genersoft.iot.vmp.onvif;
|
|
||||||
|
|
||||||
import be.teletask.onvif.models.OnvifDevice;
|
|
||||||
import com.genersoft.iot.vmp.onvif.dto.ONVIFCallBack;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface IONVIFServer {
|
|
||||||
|
|
||||||
void search(int timeout, ONVIFCallBack<List<String>> callBack);
|
|
||||||
|
|
||||||
void getRTSPUrl(int timeout, OnvifDevice device, ONVIFCallBack<String> callBack);
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
package com.genersoft.iot.vmp.onvif.dto;
|
|
||||||
|
|
||||||
public interface ONVIFCallBack<T> {
|
|
||||||
void run(int errorCode, T t);
|
|
||||||
}
|
|
||||||
@ -1,115 +0,0 @@
|
|||||||
package com.genersoft.iot.vmp.onvif.impl;
|
|
||||||
|
|
||||||
|
|
||||||
import be.teletask.onvif.DiscoveryManager;
|
|
||||||
import be.teletask.onvif.OnvifManager;
|
|
||||||
import be.teletask.onvif.listeners.*;
|
|
||||||
import be.teletask.onvif.models.*;
|
|
||||||
import be.teletask.onvif.responses.OnvifResponse;
|
|
||||||
import com.genersoft.iot.vmp.onvif.IONVIFServer;
|
|
||||||
import com.genersoft.iot.vmp.onvif.dto.ONVIFCallBack;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
/**
|
|
||||||
* 处理onvif的各种操作
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class ONVIFServerIMpl implements IONVIFServer {
|
|
||||||
|
|
||||||
private final static Logger logger = LoggerFactory.getLogger(ONVIFServerIMpl.class);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void search(int timeout, ONVIFCallBack<List<String>> callBack) {
|
|
||||||
DiscoveryManager manager = new DiscoveryManager();
|
|
||||||
manager.setDiscoveryTimeout(timeout);
|
|
||||||
Map<String, Device> deviceMap = new HashMap<>();
|
|
||||||
// 搜索设备
|
|
||||||
manager.discover(new DiscoveryListener() {
|
|
||||||
@Override
|
|
||||||
public void onDiscoveryStarted() {
|
|
||||||
logger.info("Discovery started");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDevicesFound(List<Device> devices) {
|
|
||||||
if (devices == null || devices.size() == 0) return;
|
|
||||||
for (Device device : devices){
|
|
||||||
logger.info(device.getHostName());
|
|
||||||
deviceMap.put(device.getHostName(), device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索结束
|
|
||||||
@Override
|
|
||||||
public void onDiscoveryFinished() {
|
|
||||||
ArrayList<String> result = new ArrayList<>();
|
|
||||||
for (Device device : deviceMap.values()) {
|
|
||||||
logger.info(device.getHostName());
|
|
||||||
result.add(device.getHostName());
|
|
||||||
}
|
|
||||||
callBack.run(0, result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void getRTSPUrl(int timeout, OnvifDevice device, ONVIFCallBack<String> callBack) {
|
|
||||||
if (device.getHostName() == null ){
|
|
||||||
callBack.run(400, null);
|
|
||||||
}
|
|
||||||
OnvifManager onvifManager = new OnvifManager();
|
|
||||||
onvifManager.setOnvifResponseListener(new OnvifResponseListener(){
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResponse(OnvifDevice onvifDevice, OnvifResponse response) {
|
|
||||||
logger.info("[RESPONSE] " + onvifDevice.getHostName()
|
|
||||||
+ "======" + response.getErrorCode()
|
|
||||||
+ "======" + response.getErrorMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(OnvifDevice onvifDevice, int errorCode, String errorMessage) {
|
|
||||||
logger.info("[ERROR] " + onvifDevice.getHostName() + "======" + errorCode + "=======" + errorMessage);
|
|
||||||
callBack.run(errorCode, errorMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
onvifManager.getServices(device, (OnvifDevice onvifDevice, OnvifServices services) -> {
|
|
||||||
if (services.getProfilesPath().equals("/onvif/Media")) {
|
|
||||||
onvifDevice.setPath(services);
|
|
||||||
onvifManager.getMediaProfiles(onvifDevice, new OnvifMediaProfilesListener() {
|
|
||||||
@Override
|
|
||||||
public void onMediaProfilesReceived(OnvifDevice device, List<OnvifMediaProfile> mediaProfiles) {
|
|
||||||
for (OnvifMediaProfile mediaProfile : mediaProfiles) {
|
|
||||||
logger.info(mediaProfile.getName());
|
|
||||||
logger.info(mediaProfile.getToken());
|
|
||||||
if (mediaProfile.getName().equals("mainStream")) {
|
|
||||||
onvifManager.getMediaStreamURI(device, mediaProfile, (OnvifDevice onvifDevice,
|
|
||||||
OnvifMediaProfile profile, String uri) -> {
|
|
||||||
|
|
||||||
uri = uri.replace("rtsp://", "rtsp://"+ device.getUsername() + ":"+ device.getPassword() + "@");
|
|
||||||
logger.info(onvifDevice.getHostName() + "的地址" + uri);
|
|
||||||
callBack.run(0, uri);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}catch (Exception e) {
|
|
||||||
callBack.run(400, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -61,4 +61,6 @@ public interface IMediaServerService {
|
|||||||
boolean checkMediaRecordServer(String ip, int port);
|
boolean checkMediaRecordServer(String ip, int port);
|
||||||
|
|
||||||
void delete(String id);
|
void delete(String id);
|
||||||
|
|
||||||
|
MediaServerItem getDefaultMediaServer();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,7 @@ public interface IMediaService {
|
|||||||
* @param stream
|
* @param stream
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, JSONArray tracks);
|
StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, Object tracks);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据应用名和流ID获取播放地址, 只是地址拼接,返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况
|
* 根据应用名和流ID获取播放地址, 只是地址拼接,返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况
|
||||||
@ -40,5 +40,5 @@ public interface IMediaService {
|
|||||||
* @param stream
|
* @param stream
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr);
|
StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,4 +18,6 @@ public interface IPlayService {
|
|||||||
PlayResult play(MediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
|
PlayResult play(MediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
|
||||||
|
|
||||||
MediaServerItem getNewMediaServerItem(Device device);
|
MediaServerItem getNewMediaServerItem(Device device);
|
||||||
|
|
||||||
|
void onPublishHandlerForDownload(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId, String toString);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
package com.genersoft.iot.vmp.service;
|
package com.genersoft.iot.vmp.service;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
||||||
|
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||||
import com.github.pagehelper.PageInfo;
|
import com.github.pagehelper.PageInfo;
|
||||||
|
|
||||||
public interface IStreamProxyService {
|
public interface IStreamProxyService {
|
||||||
@ -11,7 +13,7 @@ public interface IStreamProxyService {
|
|||||||
* 保存视频代理
|
* 保存视频代理
|
||||||
* @param param
|
* @param param
|
||||||
*/
|
*/
|
||||||
String save(StreamProxyItem param);
|
WVPResult<StreamInfo> save(StreamProxyItem param);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加视频代理到zlm
|
* 添加视频代理到zlm
|
||||||
@ -63,4 +65,10 @@ public interface IStreamProxyService {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem);
|
JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据app与stream获取streamProxy
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.genersoft.iot.vmp.service;
|
package com.genersoft.iot.vmp.service;
|
||||||
|
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
|
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
|
||||||
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
|
||||||
import com.github.pagehelper.PageInfo;
|
import com.github.pagehelper.PageInfo;
|
||||||
@ -32,4 +33,6 @@ public interface IStreamPushService {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
PageInfo<StreamPushItem> getPushList(Integer page, Integer count);
|
PageInfo<StreamPushItem> getPushList(Integer page, Integer count);
|
||||||
|
|
||||||
|
StreamPushItem transform(MediaItem item);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,9 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备业务(目录订阅)
|
||||||
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class DeviceServiceImpl implements IDeviceService {
|
public class DeviceServiceImpl implements IDeviceService {
|
||||||
|
|
||||||
@ -31,8 +34,11 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander);
|
CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander);
|
||||||
catalogSubscribeTask.run();
|
catalogSubscribeTask.run();
|
||||||
// 提前开始刷新订阅
|
// 提前开始刷新订阅
|
||||||
String cron = getCron(device.getSubscribeCycleForCatalog() - 60);
|
// TODO 使用jain sip的当时刷新订阅
|
||||||
dynamicTask.startCron(device.getDeviceId(), catalogSubscribeTask, cron);
|
int subscribeCycleForCatalog = device.getSubscribeCycleForCatalog();
|
||||||
|
// 设置最小值为30
|
||||||
|
subscribeCycleForCatalog = Math.max(subscribeCycleForCatalog, 30);
|
||||||
|
dynamicTask.startCron(device.getDeviceId(), catalogSubscribeTask, subscribeCycleForCatalog - 5);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,21 +47,10 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
if (device == null || device.getSubscribeCycleForCatalog() < 0) {
|
if (device == null || device.getSubscribeCycleForCatalog() < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
logger.info("移除目录订阅: {}", device.getDeviceId());
|
||||||
dynamicTask.stopCron(device.getDeviceId());
|
dynamicTask.stopCron(device.getDeviceId());
|
||||||
|
device.setSubscribeCycleForCatalog(0);
|
||||||
|
sipCommander.catalogSubscribe(device, null, null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCron(int time) {
|
|
||||||
if (time <= 59) {
|
|
||||||
return "0/" + time +" * * * * ?";
|
|
||||||
}else if (time <= 60* 59) {
|
|
||||||
int minute = time/(60);
|
|
||||||
return "0 0/" + minute +" * * * ?";
|
|
||||||
}else if (time <= 60* 60* 59) {
|
|
||||||
int hour = time/(60*60);
|
|
||||||
return "0 0 0/" + hour +" * * ?";
|
|
||||||
}else {
|
|
||||||
return "0 0/10 * * * ?";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,7 +47,7 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR
|
|||||||
private boolean sslEnabled;
|
private boolean sslEnabled;
|
||||||
|
|
||||||
@Value("${server.port}")
|
@Value("${server.port}")
|
||||||
private String serverPort;
|
private Integer serverPort;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MediaConfig mediaConfig;
|
private MediaConfig mediaConfig;
|
||||||
@ -241,6 +241,11 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR
|
|||||||
return mediaServerMapper.queryOneByHostAndPort(host, port);
|
return mediaServerMapper.queryOneByHostAndPort(host, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaServerItem getDefaultMediaServer() {
|
||||||
|
return mediaServerMapper.queryDefault();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearMediaServerForOnline() {
|
public void clearMediaServerForOnline() {
|
||||||
String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
|
String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONArray;
|
|||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
||||||
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||||
@ -31,14 +32,20 @@ public class MediaServiceImpl implements IMediaService {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, JSONArray tracks) {
|
public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks) {
|
||||||
return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null);
|
return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr) {
|
public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr) {
|
||||||
StreamInfo streamInfo = null;
|
StreamInfo streamInfo = null;
|
||||||
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
|
|
||||||
|
MediaServerItem mediaInfo;
|
||||||
|
if (mediaServerId == null) {
|
||||||
|
mediaInfo = mediaServerService.getDefaultMediaServer();
|
||||||
|
}else {
|
||||||
|
mediaInfo = mediaServerService.getOne(mediaServerId);
|
||||||
|
}
|
||||||
if (mediaInfo == null) {
|
if (mediaInfo == null) {
|
||||||
return streamInfo;
|
return streamInfo;
|
||||||
}
|
}
|
||||||
@ -55,13 +62,15 @@ public class MediaServiceImpl implements IMediaService {
|
|||||||
return streamInfo;
|
return streamInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId) {
|
public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId) {
|
||||||
return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null);
|
return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr) {
|
public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr) {
|
||||||
StreamInfo streamInfoResult = new StreamInfo();
|
StreamInfo streamInfoResult = new StreamInfo();
|
||||||
streamInfoResult.setStreamId(stream);
|
streamInfoResult.setStreamId(stream);
|
||||||
streamInfoResult.setApp(app);
|
streamInfoResult.setApp(app);
|
||||||
|
|||||||
@ -34,12 +34,8 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.util.ResourceUtils;
|
import org.springframework.util.ResourceUtils;
|
||||||
import org.springframework.web.context.request.async.DeferredResult;
|
import org.springframework.web.context.request.async.DeferredResult;
|
||||||
|
|
||||||
import javax.sip.DialogTerminatedEvent;
|
|
||||||
import javax.sip.ResponseEvent;
|
|
||||||
import javax.sip.TimeoutEvent;
|
|
||||||
import javax.sip.TransactionTerminatedEvent;
|
|
||||||
import javax.sip.message.Response;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@SuppressWarnings(value = {"rawtypes", "unchecked"})
|
@SuppressWarnings(value = {"rawtypes", "unchecked"})
|
||||||
@ -85,7 +81,13 @@ public class PlayServiceImpl implements IPlayService {
|
|||||||
RequestMessage msg = new RequestMessage();
|
RequestMessage msg = new RequestMessage();
|
||||||
String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
|
String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
|
||||||
msg.setKey(key);
|
msg.setKey(key);
|
||||||
msg.setId(playResult.getUuid());
|
String uuid = UUID.randomUUID().toString();
|
||||||
|
msg.setId(uuid);
|
||||||
|
playResult.setUuid(uuid);
|
||||||
|
DeferredResult<ResponseEntity<String>> result = new DeferredResult<>(userSetup.getPlayTimeout());
|
||||||
|
playResult.setResult(result);
|
||||||
|
// 录像查询以channelId作为deviceId查询
|
||||||
|
resultHolder.put(key, uuid, result);
|
||||||
if (mediaServerItem == null) {
|
if (mediaServerItem == null) {
|
||||||
WVPResult wvpResult = new WVPResult();
|
WVPResult wvpResult = new WVPResult();
|
||||||
wvpResult.setCode(-1);
|
wvpResult.setCode(-1);
|
||||||
@ -94,16 +96,9 @@ public class PlayServiceImpl implements IPlayService {
|
|||||||
resultHolder.invokeResult(msg);
|
resultHolder.invokeResult(msg);
|
||||||
return playResult;
|
return playResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
Device device = storager.queryVideoDevice(deviceId);
|
Device device = storager.queryVideoDevice(deviceId);
|
||||||
StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
|
StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
|
||||||
playResult.setDevice(device);
|
playResult.setDevice(device);
|
||||||
String uuid = UUID.randomUUID().toString();
|
|
||||||
playResult.setUuid(uuid);
|
|
||||||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(userSetup.getPlayTimeout());
|
|
||||||
playResult.setResult(result);
|
|
||||||
// 录像查询以channelId作为deviceId查询
|
|
||||||
resultHolder.put(key, uuid, result);
|
|
||||||
// 超时处理
|
// 超时处理
|
||||||
result.onTimeout(()->{
|
result.onTimeout(()->{
|
||||||
logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId));
|
logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId));
|
||||||
@ -134,18 +129,18 @@ public class PlayServiceImpl implements IPlayService {
|
|||||||
classPath = classPath.substring(0, classPath.lastIndexOf("/") + 1);
|
classPath = classPath.substring(0, classPath.lastIndexOf("/") + 1);
|
||||||
}
|
}
|
||||||
if (classPath.startsWith("file:")) {
|
if (classPath.startsWith("file:")) {
|
||||||
classPath = classPath.substring(classPath.indexOf(":") + 1, classPath.length());
|
classPath = classPath.substring(classPath.indexOf(":") + 1);
|
||||||
}
|
}
|
||||||
String path = classPath + "static/static/snap/";
|
String path = classPath + "static/static/snap/";
|
||||||
// 兼容Windows系统路径(去除前面的“/”)
|
// 兼容Windows系统路径(去除前面的“/”)
|
||||||
if(System.getProperty("os.name").contains("indows")) {
|
if(System.getProperty("os.name").contains("indows")) {
|
||||||
path = path.substring(1, path.length());
|
path = path.substring(1);
|
||||||
}
|
}
|
||||||
String fileName = deviceId + "_" + channelId + ".jpg";
|
String fileName = deviceId + "_" + channelId + ".jpg";
|
||||||
ResponseEntity responseEntity = (ResponseEntity)result.getResult();
|
ResponseEntity responseEntity = (ResponseEntity)result.getResult();
|
||||||
if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
|
if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
|
||||||
WVPResult wvpResult = (WVPResult)responseEntity.getBody();
|
WVPResult wvpResult = (WVPResult)responseEntity.getBody();
|
||||||
if (wvpResult.getCode() == 0) {
|
if (Objects.requireNonNull(wvpResult).getCode() == 0) {
|
||||||
StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
|
StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
|
||||||
MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
|
MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
|
||||||
String streamUrl = streamInfoForSuccess.getFmp4();
|
String streamUrl = streamInfoForSuccess.getFmp4();
|
||||||
@ -169,7 +164,7 @@ public class PlayServiceImpl implements IPlayService {
|
|||||||
// 发送点播消息
|
// 发送点播消息
|
||||||
cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
|
cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
|
||||||
logger.info("收到订阅消息: " + response.toJSONString());
|
logger.info("收到订阅消息: " + response.toJSONString());
|
||||||
onPublishHandlerForPlay(mediaServerItemInUse, response, deviceId, channelId, uuid.toString());
|
onPublishHandlerForPlay(mediaServerItemInUse, response, deviceId, channelId, uuid);
|
||||||
if (hookEvent != null) {
|
if (hookEvent != null) {
|
||||||
hookEvent.response(mediaServerItem, response);
|
hookEvent.response(mediaServerItem, response);
|
||||||
}
|
}
|
||||||
@ -192,7 +187,7 @@ public class PlayServiceImpl implements IPlayService {
|
|||||||
if (streamId == null) {
|
if (streamId == null) {
|
||||||
WVPResult wvpResult = new WVPResult();
|
WVPResult wvpResult = new WVPResult();
|
||||||
wvpResult.setCode(-1);
|
wvpResult.setCode(-1);
|
||||||
wvpResult.setMsg(String.format("点播失败, redis缓存streamId等于null"));
|
wvpResult.setMsg("点播失败, redis缓存streamId等于null");
|
||||||
msg.setData(wvpResult);
|
msg.setData(wvpResult);
|
||||||
resultHolder.invokeAllResult(msg);
|
resultHolder.invokeAllResult(msg);
|
||||||
return playResult;
|
return playResult;
|
||||||
@ -226,7 +221,7 @@ public class PlayServiceImpl implements IPlayService {
|
|||||||
|
|
||||||
cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
|
cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
|
||||||
logger.info("收到订阅消息: " + response.toJSONString());
|
logger.info("收到订阅消息: " + response.toJSONString());
|
||||||
onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid.toString());
|
onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid);
|
||||||
}, (event) -> {
|
}, (event) -> {
|
||||||
mediaServerService.closeRTPServer(playResult.getDevice(), channelId);
|
mediaServerService.closeRTPServer(playResult.getDevice(), channelId);
|
||||||
WVPResult wvpResult = new WVPResult();
|
WVPResult wvpResult = new WVPResult();
|
||||||
@ -274,7 +269,7 @@ public class PlayServiceImpl implements IPlayService {
|
|||||||
public MediaServerItem getNewMediaServerItem(Device device) {
|
public MediaServerItem getNewMediaServerItem(Device device) {
|
||||||
if (device == null) return null;
|
if (device == null) return null;
|
||||||
String mediaServerId = device.getMediaServerId();
|
String mediaServerId = device.getMediaServerId();
|
||||||
MediaServerItem mediaServerItem = null;
|
MediaServerItem mediaServerItem;
|
||||||
if (mediaServerId == null) {
|
if (mediaServerId == null) {
|
||||||
mediaServerItem = mediaServerService.getMediaServerForMinimumLoad();
|
mediaServerItem = mediaServerService.getMediaServerForMinimumLoad();
|
||||||
}else {
|
}else {
|
||||||
@ -286,16 +281,35 @@ public class PlayServiceImpl implements IPlayService {
|
|||||||
return mediaServerItem;
|
return mediaServerItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPublishHandlerForPlayBack(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
|
public void onPublishHandlerForPlayBack(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
|
||||||
RequestMessage msg = new RequestMessage();
|
RequestMessage msg = new RequestMessage();
|
||||||
msg.setKey(DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId);
|
msg.setKey(DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId);
|
||||||
msg.setId(uuid);
|
msg.setId(uuid);
|
||||||
StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId, uuid);
|
StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId, uuid);
|
||||||
if (streamInfo != null) {
|
if (streamInfo != null) {
|
||||||
redisCatchStorage.startPlayback(streamInfo);
|
redisCatchStorage.startPlayback(streamInfo);
|
||||||
msg.setData(JSON.toJSONString(streamInfo));
|
msg.setData(JSON.toJSONString(streamInfo));
|
||||||
resultHolder.invokeResult(msg);
|
resultHolder.invokeResult(msg);
|
||||||
|
} else {
|
||||||
|
logger.warn("设备回放API调用失败!");
|
||||||
|
msg.setData("设备回放API调用失败!");
|
||||||
|
resultHolder.invokeResult(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPublishHandlerForDownload(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId, String uuid) {
|
||||||
|
RequestMessage msg = new RequestMessage();
|
||||||
|
msg.setKey(DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId);
|
||||||
|
msg.setId(uuid);
|
||||||
|
StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId, uuid);
|
||||||
|
if (streamInfo != null) {
|
||||||
|
redisCatchStorage.startDownload(streamInfo);
|
||||||
|
msg.setData(JSON.toJSONString(streamInfo));
|
||||||
|
resultHolder.invokeResult(msg);
|
||||||
} else {
|
} else {
|
||||||
logger.warn("设备预览API调用失败!");
|
logger.warn("设备预览API调用失败!");
|
||||||
msg.setData("设备预览API调用失败!");
|
msg.setData("设备预览API调用失败!");
|
||||||
@ -303,6 +317,7 @@ public class PlayServiceImpl implements IPlayService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
|
public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
|
||||||
String streamId = resonse.getString("stream");
|
String streamId = resonse.getString("stream");
|
||||||
JSONArray tracks = resonse.getJSONArray("tracks");
|
JSONArray tracks = resonse.getJSONArray("tracks");
|
||||||
|
|||||||
@ -1,18 +1,21 @@
|
|||||||
package com.genersoft.iot.vmp.service.impl;
|
package com.genersoft.iot.vmp.service.impl;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
|
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
|
||||||
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
||||||
import com.genersoft.iot.vmp.service.IGbStreamService;
|
import com.genersoft.iot.vmp.service.IGbStreamService;
|
||||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||||
|
import com.genersoft.iot.vmp.service.IMediaService;
|
||||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||||
import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
|
import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
|
||||||
import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper;
|
import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper;
|
||||||
import com.genersoft.iot.vmp.storager.dao.StreamProxyMapper;
|
import com.genersoft.iot.vmp.storager.dao.StreamProxyMapper;
|
||||||
import com.genersoft.iot.vmp.service.IStreamProxyService;
|
import com.genersoft.iot.vmp.service.IStreamProxyService;
|
||||||
|
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||||
import com.github.pagehelper.PageInfo;
|
import com.github.pagehelper.PageInfo;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -34,7 +37,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
|||||||
private IVideoManagerStorager videoManagerStorager;
|
private IVideoManagerStorager videoManagerStorager;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IRedisCatchStorage redisCatchStorage;
|
private IMediaService mediaService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ZLMRESTfulUtils zlmresTfulUtils;;
|
private ZLMRESTfulUtils zlmresTfulUtils;;
|
||||||
@ -56,8 +59,10 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String save(StreamProxyItem param) {
|
public WVPResult<StreamInfo> save(StreamProxyItem param) {
|
||||||
MediaServerItem mediaInfo;
|
MediaServerItem mediaInfo;
|
||||||
|
WVPResult<StreamInfo> wvpResult = new WVPResult<>();
|
||||||
|
wvpResult.setCode(0);
|
||||||
if ("auto".equals(param.getMediaServerId())){
|
if ("auto".equals(param.getMediaServerId())){
|
||||||
mediaInfo = mediaServerService.getMediaServerForMinimumLoad();
|
mediaInfo = mediaServerService.getMediaServerForMinimumLoad();
|
||||||
}else {
|
}else {
|
||||||
@ -65,7 +70,8 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
|||||||
}
|
}
|
||||||
if (mediaInfo == null) {
|
if (mediaInfo == null) {
|
||||||
logger.warn("保存代理未找到在线的ZLM...");
|
logger.warn("保存代理未找到在线的ZLM...");
|
||||||
return "保存失败";
|
wvpResult.setMsg("保存失败");
|
||||||
|
return wvpResult;
|
||||||
}
|
}
|
||||||
String dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(),
|
String dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(),
|
||||||
param.getStream() );
|
param.getStream() );
|
||||||
@ -83,6 +89,10 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
|||||||
result.append(", 但是启用失败,请检查流地址是否可用");
|
result.append(", 但是启用失败,请检查流地址是否可用");
|
||||||
param.setEnable(false);
|
param.setEnable(false);
|
||||||
videoManagerStorager.updateStreamProxy(param);
|
videoManagerStorager.updateStreamProxy(param);
|
||||||
|
}else {
|
||||||
|
StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
|
||||||
|
mediaInfo, param.getApp(), param.getStream(), null);
|
||||||
|
wvpResult.setData(streamInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,6 +107,10 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
|||||||
result.append(", 但是启用失败,请检查流地址是否可用");
|
result.append(", 但是启用失败,请检查流地址是否可用");
|
||||||
param.setEnable(false);
|
param.setEnable(false);
|
||||||
videoManagerStorager.updateStreamProxy(param);
|
videoManagerStorager.updateStreamProxy(param);
|
||||||
|
}else {
|
||||||
|
StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
|
||||||
|
mediaInfo, param.getApp(), param.getStream(), null);
|
||||||
|
wvpResult.setData(streamInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else {
|
}else {
|
||||||
@ -113,7 +127,8 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
|||||||
result.append(", 关联国标平台[ " + param.getPlatformGbId() + " ]失败");
|
result.append(", 关联国标平台[ " + param.getPlatformGbId() + " ]失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result.toString();
|
wvpResult.setMsg(result.toString());
|
||||||
|
return wvpResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -213,4 +228,10 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId) {
|
||||||
|
return videoManagerStorager.getStreamProxyByAppAndStream(app, streamId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,33 +51,38 @@ public class StreamPushServiceImpl implements IStreamPushService {
|
|||||||
for (MediaItem item : mediaItems) {
|
for (MediaItem item : mediaItems) {
|
||||||
|
|
||||||
// 不保存国标推理以及拉流代理的流
|
// 不保存国标推理以及拉流代理的流
|
||||||
if (item.getOriginType() == 3 || item.getOriginType() == 4 || item.getOriginType() == 5) {
|
if (item.getOriginType() == 1 || item.getOriginType() == 2 || item.getOriginType() == 8) {
|
||||||
continue;
|
String key = item.getApp() + "_" + item.getStream();
|
||||||
}
|
StreamPushItem streamPushItem = result.get(key);
|
||||||
String key = item.getApp() + "_" + item.getStream();
|
if (streamPushItem == null) {
|
||||||
StreamPushItem streamPushItem = result.get(key);
|
streamPushItem = transform(item);
|
||||||
if (streamPushItem == null) {
|
result.put(key, streamPushItem);
|
||||||
streamPushItem = new StreamPushItem();
|
}
|
||||||
streamPushItem.setApp(item.getApp());
|
|
||||||
streamPushItem.setMediaServerId(mediaServerItem.getId());
|
|
||||||
streamPushItem.setStream(item.getStream());
|
|
||||||
streamPushItem.setAliveSecond(item.getAliveSecond());
|
|
||||||
streamPushItem.setCreateStamp(item.getCreateStamp());
|
|
||||||
streamPushItem.setOriginSock(item.getOriginSock());
|
|
||||||
streamPushItem.setTotalReaderCount(item.getTotalReaderCount());
|
|
||||||
streamPushItem.setOriginType(item.getOriginType());
|
|
||||||
streamPushItem.setOriginTypeStr(item.getOriginTypeStr());
|
|
||||||
streamPushItem.setOriginUrl(item.getOriginUrl());
|
|
||||||
streamPushItem.setCreateStamp(item.getCreateStamp());
|
|
||||||
streamPushItem.setAliveSecond(item.getAliveSecond());
|
|
||||||
streamPushItem.setStatus(true);
|
|
||||||
streamPushItem.setVhost(item.getVhost());
|
|
||||||
result.put(key, streamPushItem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ArrayList<>(result.values());
|
return new ArrayList<>(result.values());
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public StreamPushItem transform(MediaItem item) {
|
||||||
|
StreamPushItem streamPushItem = new StreamPushItem();
|
||||||
|
streamPushItem.setApp(item.getApp());
|
||||||
|
streamPushItem.setMediaServerId(item.getMediaServerId());
|
||||||
|
streamPushItem.setStream(item.getStream());
|
||||||
|
streamPushItem.setAliveSecond(item.getAliveSecond());
|
||||||
|
streamPushItem.setCreateStamp(item.getCreateStamp());
|
||||||
|
streamPushItem.setOriginSock(item.getOriginSock());
|
||||||
|
streamPushItem.setTotalReaderCount(item.getTotalReaderCount());
|
||||||
|
streamPushItem.setOriginType(item.getOriginType());
|
||||||
|
streamPushItem.setOriginTypeStr(item.getOriginTypeStr());
|
||||||
|
streamPushItem.setOriginUrl(item.getOriginUrl());
|
||||||
|
streamPushItem.setCreateStamp(item.getCreateStamp());
|
||||||
|
streamPushItem.setAliveSecond(item.getAliveSecond());
|
||||||
|
streamPushItem.setStatus(true);
|
||||||
|
streamPushItem.setVhost(item.getVhost());
|
||||||
|
return streamPushItem;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageInfo<StreamPushItem> getPushList(Integer page, Integer count) {
|
public PageInfo<StreamPushItem> getPushList(Integer page, Integer count) {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.common.StreamInfo;
|
|||||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
|
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
|
||||||
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -120,5 +121,35 @@ public interface IRedisCatchStorage {
|
|||||||
/**
|
/**
|
||||||
* 在redis添加wvp的信息
|
* 在redis添加wvp的信息
|
||||||
*/
|
*/
|
||||||
void updateWVPInfo(JSONObject jsonObject);
|
void updateWVPInfo(String id, JSONObject jsonObject, int time);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送推流生成与推流消失消息
|
||||||
|
* @param jsonObject 消息内容
|
||||||
|
*/
|
||||||
|
void sendStreamChangeMsg(JSONObject jsonObject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加流信息到redis
|
||||||
|
* @param mediaServerItem
|
||||||
|
* @param app
|
||||||
|
* @param streamId
|
||||||
|
*/
|
||||||
|
void addPushStream(MediaServerItem mediaServerItem, String app, String streamId, StreamInfo streamInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除流信息从redis
|
||||||
|
* @param mediaServerItem
|
||||||
|
* @param app
|
||||||
|
* @param streamId
|
||||||
|
*/
|
||||||
|
void removePushStream(MediaServerItem mediaServerItem, String app, String streamId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始下载录像时存入
|
||||||
|
* @param streamInfo
|
||||||
|
*/
|
||||||
|
boolean startDownload(StreamInfo streamInfo);
|
||||||
|
|
||||||
|
StreamInfo queryDownloadByStreamId(String streamId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -353,7 +353,7 @@ public interface IVideoManagerStorager {
|
|||||||
* @param app
|
* @param app
|
||||||
* @param stream
|
* @param stream
|
||||||
*/
|
*/
|
||||||
void removeMedia(String app, String stream);
|
int removeMedia(String app, String stream);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -366,7 +366,7 @@ public interface IVideoManagerStorager {
|
|||||||
* @param app
|
* @param app
|
||||||
* @param streamId
|
* @param streamId
|
||||||
*/
|
*/
|
||||||
void mediaOutline(String app, String streamId);
|
int mediaOutline(String app, String streamId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置平台在线/离线
|
* 设置平台在线/离线
|
||||||
@ -406,4 +406,12 @@ public interface IVideoManagerStorager {
|
|||||||
* @param channelId 通道ID
|
* @param channelId 通道ID
|
||||||
*/
|
*/
|
||||||
void deviceChannelOffline(String deviceId, String channelId);
|
void deviceChannelOffline(String deviceId, String channelId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过app与stream获取StreamProxy
|
||||||
|
* @param app
|
||||||
|
* @param streamId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ public interface GbStreamMapper {
|
|||||||
"latitude=#{latitude}," +
|
"latitude=#{latitude}," +
|
||||||
"mediaServerId=#{mediaServerId}," +
|
"mediaServerId=#{mediaServerId}," +
|
||||||
"status=${status} " +
|
"status=${status} " +
|
||||||
"WHERE app=#{app} AND stream=#{stream} AND gbId=#{gbId}")
|
"WHERE app=#{app} AND stream=#{stream}")
|
||||||
int update(GbStream gbStream);
|
int update(GbStream gbStream);
|
||||||
|
|
||||||
@Delete("DELETE FROM gb_stream WHERE app=#{app} AND stream=#{stream}")
|
@Delete("DELETE FROM gb_stream WHERE app=#{app} AND stream=#{stream}")
|
||||||
@ -53,7 +53,7 @@ public interface GbStreamMapper {
|
|||||||
@Update("UPDATE gb_stream " +
|
@Update("UPDATE gb_stream " +
|
||||||
"SET status=${status} " +
|
"SET status=${status} " +
|
||||||
"WHERE app=#{app} AND stream=#{stream}")
|
"WHERE app=#{app} AND stream=#{stream}")
|
||||||
void setStatus(String app, String stream, boolean status);
|
int setStatus(String app, String stream, boolean status);
|
||||||
|
|
||||||
@Select("SELECT gs.*, pgs.platformId FROM gb_stream gs LEFT JOIN platform_gb_stream pgs ON gs.app = pgs.app AND gs.stream = pgs.stream WHERE mediaServerId=#{mediaServerId} ")
|
@Select("SELECT gs.*, pgs.platformId FROM gb_stream gs LEFT JOIN platform_gb_stream pgs ON gs.app = pgs.app AND gs.stream = pgs.stream WHERE mediaServerId=#{mediaServerId} ")
|
||||||
List<GbStream> selectAllByMediaServerId(String mediaServerId);
|
List<GbStream> selectAllByMediaServerId(String mediaServerId);
|
||||||
|
|||||||
@ -105,4 +105,7 @@ public interface MediaServerMapper {
|
|||||||
|
|
||||||
@Select("SELECT * FROM media_server WHERE ip='${host}' and httpPort=${port}")
|
@Select("SELECT * FROM media_server WHERE ip='${host}' and httpPort=${port}")
|
||||||
MediaServerItem queryOneByHostAndPort(String host, int port);
|
MediaServerItem queryOneByHostAndPort(String host, int port);
|
||||||
|
|
||||||
|
@Select("SELECT * FROM media_server WHERE defaultServer=1")
|
||||||
|
MediaServerItem queryDefault();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,9 +11,10 @@ import java.util.List;
|
|||||||
public interface StreamProxyMapper {
|
public interface StreamProxyMapper {
|
||||||
|
|
||||||
@Insert("INSERT INTO stream_proxy (type, app, stream,mediaServerId, url, src_url, dst_url, " +
|
@Insert("INSERT INTO stream_proxy (type, app, stream,mediaServerId, url, src_url, dst_url, " +
|
||||||
"timeout_ms, ffmpeg_cmd_key, rtp_type, enable_hls, enable_mp4, enable, createTime) VALUES" +
|
"timeout_ms, ffmpeg_cmd_key, rtp_type, enable_hls, enable_mp4, enable, enable_remove_none_reader, createTime) VALUES" +
|
||||||
"('${type}','${app}', '${stream}', '${mediaServerId}','${url}', '${src_url}', '${dst_url}', " +
|
"('${type}','${app}', '${stream}', '${mediaServerId}','${url}', '${src_url}', '${dst_url}', " +
|
||||||
"'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_hls}, ${enable_mp4}, ${enable}, '${createTime}' )")
|
"'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_hls}, ${enable_mp4}, ${enable}, " +
|
||||||
|
"${enable_remove_none_reader}, '${createTime}' )")
|
||||||
int add(StreamProxyItem streamProxyDto);
|
int add(StreamProxyItem streamProxyDto);
|
||||||
|
|
||||||
@Update("UPDATE stream_proxy " +
|
@Update("UPDATE stream_proxy " +
|
||||||
@ -29,6 +30,7 @@ public interface StreamProxyMapper {
|
|||||||
"rtp_type=#{rtp_type}, " +
|
"rtp_type=#{rtp_type}, " +
|
||||||
"enable_hls=#{enable_hls}, " +
|
"enable_hls=#{enable_hls}, " +
|
||||||
"enable=#{enable}, " +
|
"enable=#{enable}, " +
|
||||||
|
"enable_remove_none_reader=#{enable_remove_none_reader}, " +
|
||||||
"enable_mp4=#{enable_mp4} " +
|
"enable_mp4=#{enable_mp4} " +
|
||||||
"WHERE app=#{app} AND stream=#{stream}")
|
"WHERE app=#{app} AND stream=#{stream}")
|
||||||
int update(StreamProxyItem streamProxyDto);
|
int update(StreamProxyItem streamProxyDto);
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
|
|||||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
import com.genersoft.iot.vmp.common.VideoManagerConstants;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.*;
|
import com.genersoft.iot.vmp.gb28181.bean.*;
|
||||||
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||||
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
|
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
|
||||||
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
|
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
|
||||||
@ -63,15 +64,15 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
|||||||
streamInfo.getChannelId()));
|
streamInfo.getChannelId()));
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public StreamInfo queryPlayByStreamId(String steamId) {
|
public StreamInfo queryPlayByStreamId(String streamId) {
|
||||||
List<Object> playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, steamId));
|
List<Object> playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAYER_PREFIX, streamId));
|
||||||
if (playLeys == null || playLeys.size() == 0) return null;
|
if (playLeys == null || playLeys.size() == 0) return null;
|
||||||
return (StreamInfo)redis.get(playLeys.get(0).toString());
|
return (StreamInfo)redis.get(playLeys.get(0).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamInfo queryPlaybackByStreamId(String steamId) {
|
public StreamInfo queryPlaybackByStreamId(String streamId) {
|
||||||
List<Object> playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAY_BLACK_PREFIX, steamId));
|
List<Object> playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.PLAY_BLACK_PREFIX, streamId));
|
||||||
if (playLeys == null || playLeys.size() == 0) return null;
|
if (playLeys == null || playLeys.size() == 0) return null;
|
||||||
return (StreamInfo)redis.get(playLeys.get(0).toString());
|
return (StreamInfo)redis.get(playLeys.get(0).toString());
|
||||||
}
|
}
|
||||||
@ -103,10 +104,15 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean startPlayback(StreamInfo stream) {
|
public boolean startPlayback(StreamInfo stream) {
|
||||||
return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, stream.getStreamId(),stream.getDeviceID(), stream.getChannelId()),
|
return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, stream.getStreamId(),
|
||||||
stream);
|
stream.getDeviceID(), stream.getChannelId()), stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean startDownload(StreamInfo streamInfo) {
|
||||||
|
return redis.set(String.format("%S_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX, streamInfo.getStreamId(),
|
||||||
|
streamInfo.getDeviceID(), streamInfo.getChannelId()), streamInfo);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean stopPlayback(StreamInfo streamInfo) {
|
public boolean stopPlayback(StreamInfo streamInfo) {
|
||||||
@ -295,8 +301,33 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateWVPInfo(JSONObject jsonObject) {
|
public void updateWVPInfo(String id, JSONObject jsonObject, int time) {
|
||||||
|
String key = VideoManagerConstants.WVP_SERVER_PREFIX + id;
|
||||||
|
redis.set(key, jsonObject, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendStreamChangeMsg(JSONObject jsonObject) {
|
||||||
|
String key = VideoManagerConstants.WVP_MSG_STREAM_PUSH_CHANGE_PREFIX;
|
||||||
|
redis.convertAndSend(key, jsonObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addPushStream(MediaServerItem mediaServerItem, String app, String streamId, StreamInfo streamInfo) {
|
||||||
|
String key = VideoManagerConstants.WVP_SERVER_STREAM_PUSH_PREFIX + app + "_" + streamId + "_" + mediaServerItem.getId();
|
||||||
|
redis.set(key, streamInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removePushStream(MediaServerItem mediaServerItem, String app, String streamId) {
|
||||||
|
String key = VideoManagerConstants.WVP_SERVER_STREAM_PUSH_PREFIX + app + "_" + streamId + "_" + mediaServerItem.getId();
|
||||||
|
redis.del(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamInfo queryDownloadByStreamId(String streamId) {
|
||||||
|
List<Object> playLeys = redis.scan(String.format("%S_%s_*", VideoManagerConstants.DOWNLOAD_PREFIX, streamId));
|
||||||
|
if (playLeys == null || playLeys.size() == 0) return null;
|
||||||
|
return (StreamInfo)redis.get(playLeys.get(0).toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -605,8 +605,8 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeMedia(String app, String stream) {
|
public int removeMedia(String app, String stream) {
|
||||||
streamPushMapper.del(app, stream);
|
return streamPushMapper.del(app, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -615,8 +615,8 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mediaOutline(String app, String streamId) {
|
public int mediaOutline(String app, String streamId) {
|
||||||
gbStreamMapper.setStatus(app, streamId, false);
|
return gbStreamMapper.setStatus(app, streamId, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -651,4 +651,9 @@ public class VideoManagerStoragerImpl implements IVideoManagerStorager {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamProxyItem getStreamProxyByAppAndStream(String app, String streamId) {
|
||||||
|
return streamProxyMapper.selectOne(app, streamId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.utils.redis;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.*;
|
import org.springframework.data.redis.core.*;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -729,4 +730,11 @@ public class RedisUtil {
|
|||||||
return new ArrayList<>(keys);
|
return new ArrayList<>(keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================== 消息发送与订阅 ==============================
|
||||||
|
public void convertAndSend(String channel, JSONObject msg) {
|
||||||
|
// redisTemplate.convertAndSend(channel, msg);
|
||||||
|
redisTemplate.convertAndSend(channel, msg);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -108,10 +108,10 @@ public class DeviceControl {
|
|||||||
msg.setData("Timeout. Device did not response to this command.");
|
msg.setData("Timeout. Device did not response to this command.");
|
||||||
resultHolder.invokeAllResult(msg);
|
resultHolder.invokeAllResult(msg);
|
||||||
});
|
});
|
||||||
resultHolder.put(key, uuid, result);
|
|
||||||
if (resultHolder.exist(key, null)){
|
if (resultHolder.exist(key, null)){
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
resultHolder.put(key, uuid, result);
|
||||||
cmder.recordCmd(device, channelId, recordCmdStr, event -> {
|
cmder.recordCmd(device, channelId, recordCmdStr, event -> {
|
||||||
RequestMessage msg = new RequestMessage();
|
RequestMessage msg = new RequestMessage();
|
||||||
msg.setId(uuid);
|
msg.setId(uuid);
|
||||||
|
|||||||
@ -44,11 +44,11 @@ public class MediaController {
|
|||||||
@ApiImplicitParams({
|
@ApiImplicitParams({
|
||||||
@ApiImplicitParam(name = "app", value = "应用名", dataTypeClass = String.class),
|
@ApiImplicitParam(name = "app", value = "应用名", dataTypeClass = String.class),
|
||||||
@ApiImplicitParam(name = "stream", value = "流id", dataTypeClass = String.class),
|
@ApiImplicitParam(name = "stream", value = "流id", dataTypeClass = String.class),
|
||||||
@ApiImplicitParam(name = "mediaServerId", value = "媒体服务器id", dataTypeClass = String.class),
|
@ApiImplicitParam(name = "mediaServerId", value = "媒体服务器id", dataTypeClass = String.class, required = false),
|
||||||
})
|
})
|
||||||
@GetMapping(value = "/stream_info_by_app_and_stream")
|
@GetMapping(value = "/stream_info_by_app_and_stream")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public WVPResult<StreamInfo> getStreamInfoByAppAndStream(@RequestParam String app, @RequestParam String stream, @RequestParam String mediaServerId){
|
public WVPResult<StreamInfo> getStreamInfoByAppAndStream(@RequestParam String app, @RequestParam String stream, @RequestParam(required = false) String mediaServerId){
|
||||||
StreamInfo streamInfoByAppAndStreamWithCheck = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId);
|
StreamInfo streamInfoByAppAndStreamWithCheck = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId);
|
||||||
WVPResult<StreamInfo> result = new WVPResult<>();
|
WVPResult<StreamInfo> result = new WVPResult<>();
|
||||||
if (streamInfoByAppAndStreamWithCheck != null){
|
if (streamInfoByAppAndStreamWithCheck != null){
|
||||||
|
|||||||
@ -76,7 +76,7 @@ public class DownloadController {
|
|||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("历史媒体下载 API调用,deviceId:%s,channelId:%s,downloadSpeed:%s", deviceId, channelId, downloadSpeed));
|
logger.debug(String.format("历史媒体下载 API调用,deviceId:%s,channelId:%s,downloadSpeed:%s", deviceId, channelId, downloadSpeed));
|
||||||
}
|
}
|
||||||
String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
|
String key = DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId;
|
||||||
String uuid = UUID.randomUUID().toString();
|
String uuid = UUID.randomUUID().toString();
|
||||||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(30000L);
|
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(30000L);
|
||||||
// 超时处理
|
// 超时处理
|
||||||
@ -88,10 +88,10 @@ public class DownloadController {
|
|||||||
msg.setData("Timeout");
|
msg.setData("Timeout");
|
||||||
resultHolder.invokeAllResult(msg);
|
resultHolder.invokeAllResult(msg);
|
||||||
});
|
});
|
||||||
resultHolder.put(key, uuid, result);
|
|
||||||
if(resultHolder.exist(key, null)) {
|
if(resultHolder.exist(key, null)) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
resultHolder.put(key, uuid, result);
|
||||||
Device device = storager.queryVideoDevice(deviceId);
|
Device device = storager.queryVideoDevice(deviceId);
|
||||||
StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId);
|
StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId);
|
||||||
if (streamInfo != null) {
|
if (streamInfo != null) {
|
||||||
@ -114,7 +114,7 @@ public class DownloadController {
|
|||||||
|
|
||||||
cmder.downloadStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, (MediaServerItem mediaServerItem, JSONObject response) -> {
|
cmder.downloadStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, (MediaServerItem mediaServerItem, JSONObject response) -> {
|
||||||
logger.info("收到订阅消息: " + response.toJSONString());
|
logger.info("收到订阅消息: " + response.toJSONString());
|
||||||
playService.onPublishHandlerForPlayBack(mediaServerItem, response, deviceId, channelId, uuid.toString());
|
playService.onPublishHandlerForDownload(mediaServerItem, response, deviceId, channelId, uuid.toString());
|
||||||
}, event -> {
|
}, event -> {
|
||||||
RequestMessage msg = new RequestMessage();
|
RequestMessage msg = new RequestMessage();
|
||||||
msg.setId(uuid);
|
msg.setId(uuid);
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import com.genersoft.iot.vmp.service.IMediaServerService;
|
|||||||
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
|
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
|
||||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||||
import com.genersoft.iot.vmp.service.IPlayService;
|
import com.genersoft.iot.vmp.service.IPlayService;
|
||||||
|
import com.genersoft.iot.vmp.vmanager.gb28181.session.InfoCseqCache;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiImplicitParam;
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
import io.swagger.annotations.ApiImplicitParams;
|
import io.swagger.annotations.ApiImplicitParams;
|
||||||
@ -77,7 +78,7 @@ public class PlaybackController {
|
|||||||
logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId));
|
logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId));
|
||||||
}
|
}
|
||||||
String uuid = UUID.randomUUID().toString();
|
String uuid = UUID.randomUUID().toString();
|
||||||
String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
|
String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId;
|
||||||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(30000L);
|
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(30000L);
|
||||||
Device device = storager.queryVideoDevice(deviceId);
|
Device device = storager.queryVideoDevice(deviceId);
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
@ -152,4 +153,103 @@ public class PlaybackController {
|
|||||||
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
|
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation("回放暂停")
|
||||||
|
@ApiImplicitParams({
|
||||||
|
@ApiImplicitParam(name = "streamId", value = "回放流ID", dataTypeClass = String.class),
|
||||||
|
})
|
||||||
|
@GetMapping("/pause/{streamId}")
|
||||||
|
public ResponseEntity<String> playPause(@PathVariable String streamId) {
|
||||||
|
logger.info("playPause: "+streamId);
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId);
|
||||||
|
if (null == streamInfo) {
|
||||||
|
json.put("msg", "streamId不存在");
|
||||||
|
logger.warn("streamId不存在!");
|
||||||
|
return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
setCseq(streamId);
|
||||||
|
Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
|
||||||
|
cmder.playPauseCmd(device, streamInfo);
|
||||||
|
json.put("msg", "ok");
|
||||||
|
return new ResponseEntity<String>(json.toString(), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation("回放恢复")
|
||||||
|
@ApiImplicitParams({
|
||||||
|
@ApiImplicitParam(name = "streamId", value = "回放流ID", dataTypeClass = String.class),
|
||||||
|
})
|
||||||
|
@GetMapping("/resume/{streamId}")
|
||||||
|
public ResponseEntity<String> playResume(@PathVariable String streamId) {
|
||||||
|
logger.info("playResume: "+streamId);
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId);
|
||||||
|
if (null == streamInfo) {
|
||||||
|
json.put("msg", "streamId不存在");
|
||||||
|
logger.warn("streamId不存在!");
|
||||||
|
return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
setCseq(streamId);
|
||||||
|
Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
|
||||||
|
cmder.playResumeCmd(device, streamInfo);
|
||||||
|
json.put("msg", "ok");
|
||||||
|
return new ResponseEntity<String>(json.toString(), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation("回放拖动播放")
|
||||||
|
@ApiImplicitParams({
|
||||||
|
@ApiImplicitParam(name = "streamId", value = "回放流ID", dataTypeClass = String.class),
|
||||||
|
@ApiImplicitParam(name = "seekTime", value = "拖动偏移量,单位s", dataTypeClass = Long.class),
|
||||||
|
})
|
||||||
|
@GetMapping("/seek/{streamId}/{seekTime}")
|
||||||
|
public ResponseEntity<String> playSeek(@PathVariable String streamId, @PathVariable long seekTime) {
|
||||||
|
logger.info("playSeek: "+streamId+", "+seekTime);
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId);
|
||||||
|
if (null == streamInfo) {
|
||||||
|
json.put("msg", "streamId不存在");
|
||||||
|
logger.warn("streamId不存在!");
|
||||||
|
return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
setCseq(streamId);
|
||||||
|
Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
|
||||||
|
cmder.playSeekCmd(device, streamInfo, seekTime);
|
||||||
|
json.put("msg", "ok");
|
||||||
|
return new ResponseEntity<String>(json.toString(), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation("回放倍速播放")
|
||||||
|
@ApiImplicitParams({
|
||||||
|
@ApiImplicitParam(name = "streamId", value = "回放流ID", dataTypeClass = String.class),
|
||||||
|
@ApiImplicitParam(name = "speed", value = "倍速0.25 0.5 1、2、4", dataTypeClass = Double.class),
|
||||||
|
})
|
||||||
|
@GetMapping("/speed/{streamId}/{speed}")
|
||||||
|
public ResponseEntity<String> playSpeed(@PathVariable String streamId, @PathVariable Double speed) {
|
||||||
|
logger.info("playSpeed: "+streamId+", "+speed);
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId);
|
||||||
|
if (null == streamInfo) {
|
||||||
|
json.put("msg", "streamId不存在");
|
||||||
|
logger.warn("streamId不存在!");
|
||||||
|
return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
if(speed != 0.25 && speed != 0.5 && speed != 1 && speed != 2.0 && speed != 4.0) {
|
||||||
|
json.put("msg", "不支持的speed(0.25 0.5 1、2、4)");
|
||||||
|
logger.warn("不支持的speed: " + speed);
|
||||||
|
return new ResponseEntity<String>(json.toString(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
setCseq(streamId);
|
||||||
|
Device device = storager.queryVideoDevice(streamInfo.getDeviceID());
|
||||||
|
cmder.playSpeedCmd(device, streamInfo, speed);
|
||||||
|
json.put("msg", "ok");
|
||||||
|
return new ResponseEntity<String>(json.toString(), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCseq(String streamId) {
|
||||||
|
if (InfoCseqCache.CSEQCACHE.containsKey(streamId)) {
|
||||||
|
InfoCseqCache.CSEQCACHE.put(streamId, InfoCseqCache.CSEQCACHE.get(streamId) + 1);
|
||||||
|
} else {
|
||||||
|
InfoCseqCache.CSEQCACHE.put(streamId, 2L);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,17 +56,22 @@ public class GBRecordController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Device device = storager.queryVideoDevice(deviceId);
|
Device device = storager.queryVideoDevice(deviceId);
|
||||||
cmder.recordInfoQuery(device, channelId, startTime, endTime);
|
|
||||||
// 指定超时时间 1分钟30秒
|
// 指定超时时间 1分钟30秒
|
||||||
DeferredResult<ResponseEntity<RecordInfo>> result = new DeferredResult<>(90*1000L);
|
DeferredResult<ResponseEntity<RecordInfo>> result = new DeferredResult<>(90*1000L);
|
||||||
String uuid = UUID.randomUUID().toString();
|
String uuid = UUID.randomUUID().toString();
|
||||||
String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + channelId;
|
int sn = (int)((Math.random()*9+1)*100000);
|
||||||
|
String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn;
|
||||||
|
RequestMessage msg = new RequestMessage();
|
||||||
|
msg.setId(uuid);
|
||||||
|
msg.setKey(key);
|
||||||
|
cmder.recordInfoQuery(device, channelId, startTime, endTime, sn, (eventResult -> {
|
||||||
|
msg.setData("查询录像失败, status: " + eventResult.statusCode + ", message: " + eventResult.msg );
|
||||||
|
resultHolder.invokeResult(msg);
|
||||||
|
}));
|
||||||
|
|
||||||
// 录像查询以channelId作为deviceId查询
|
// 录像查询以channelId作为deviceId查询
|
||||||
resultHolder.put(key, uuid, result);
|
resultHolder.put(key, uuid, result);
|
||||||
result.onTimeout(()->{
|
result.onTimeout(()->{
|
||||||
RequestMessage msg = new RequestMessage();
|
|
||||||
msg.setId(uuid);
|
|
||||||
msg.setKey(key);
|
|
||||||
msg.setData("timeout");
|
msg.setData("timeout");
|
||||||
resultHolder.invokeResult(msg);
|
resultHolder.invokeResult(msg);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.genersoft.iot.vmp.vmanager.gb28181.session;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ClassName: InfoCseqCache
|
||||||
|
* @Description: INFO类型的Sip中cseq的缓存
|
||||||
|
*/
|
||||||
|
public class InfoCseqCache {
|
||||||
|
|
||||||
|
public static Map<String, Long> CSEQCACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,123 +0,0 @@
|
|||||||
package com.genersoft.iot.vmp.vmanager.onvif;
|
|
||||||
|
|
||||||
import be.teletask.onvif.models.OnvifDevice;
|
|
||||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
|
||||||
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
|
|
||||||
import com.genersoft.iot.vmp.onvif.IONVIFServer;
|
|
||||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
|
||||||
import io.swagger.annotations.Api;
|
|
||||||
import io.swagger.annotations.ApiImplicitParam;
|
|
||||||
import io.swagger.annotations.ApiImplicitParams;
|
|
||||||
import io.swagger.annotations.ApiOperation;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import org.springframework.web.context.request.async.DeferredResult;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@SuppressWarnings(value = {"rawtypes", "unchecked"})
|
|
||||||
@Api(tags = "onvif设备")
|
|
||||||
@CrossOrigin
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/onvif")
|
|
||||||
public class ONVIFController {
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DeferredResultHolder resultHolder;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private IONVIFServer onvifServer;
|
|
||||||
|
|
||||||
|
|
||||||
@ApiOperation("搜索")
|
|
||||||
@ApiImplicitParams({
|
|
||||||
@ApiImplicitParam(name="timeout", value = "超时时间", required = true, dataTypeClass = Integer.class),
|
|
||||||
})
|
|
||||||
@GetMapping(value = "/search")
|
|
||||||
@ResponseBody
|
|
||||||
public DeferredResult<ResponseEntity<WVPResult>> search(@RequestParam(required = false)Integer timeout){
|
|
||||||
DeferredResult<ResponseEntity<WVPResult>> result = new DeferredResult<>(timeout + 10L);
|
|
||||||
String uuid = UUID.randomUUID().toString();
|
|
||||||
result.onTimeout(()->{
|
|
||||||
RequestMessage msg = new RequestMessage();
|
|
||||||
msg.setKey(DeferredResultHolder.CALLBACK_ONVIF );
|
|
||||||
msg.setId(uuid);
|
|
||||||
WVPResult<String> wvpResult = new WVPResult();
|
|
||||||
wvpResult.setCode(0);
|
|
||||||
wvpResult.setMsg("搜索超时");
|
|
||||||
msg.setData(wvpResult);
|
|
||||||
resultHolder.invokeResult(msg);
|
|
||||||
});
|
|
||||||
resultHolder.put(DeferredResultHolder.CALLBACK_ONVIF, uuid, result);
|
|
||||||
|
|
||||||
onvifServer.search(timeout, (errorCode, onvifDevices) ->{
|
|
||||||
RequestMessage msg = new RequestMessage();
|
|
||||||
msg.setId(DeferredResultHolder.CALLBACK_ONVIF + uuid);
|
|
||||||
WVPResult<List<String>> resultData = new WVPResult();
|
|
||||||
resultData.setCode(errorCode);
|
|
||||||
if (errorCode == 0) {
|
|
||||||
resultData.setMsg("success");
|
|
||||||
resultData.setData(onvifDevices);
|
|
||||||
}else {
|
|
||||||
resultData.setMsg("fail");
|
|
||||||
}
|
|
||||||
msg.setData(resultData);
|
|
||||||
msg.setData(resultData);
|
|
||||||
resultHolder.invokeResult(msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation("获取onvif的rtsp地址")
|
|
||||||
@ApiImplicitParams({
|
|
||||||
@ApiImplicitParam(name="timeout", value = "超时时间", required = true, dataTypeClass = Integer.class),
|
|
||||||
@ApiImplicitParam(name="hostname", value = "onvif地址", required = true, dataTypeClass = String.class),
|
|
||||||
@ApiImplicitParam(name="username", value = "用户名", required = true, dataTypeClass = String.class),
|
|
||||||
@ApiImplicitParam(name="password", value = "密码", required = true, dataTypeClass = String.class),
|
|
||||||
})
|
|
||||||
@GetMapping(value = "/rtsp")
|
|
||||||
@ResponseBody
|
|
||||||
public DeferredResult<ResponseEntity<WVPResult>> getRTSPUrl(@RequestParam(value="timeout", required=false, defaultValue="3000") Integer timeout,
|
|
||||||
@RequestParam(required = true) String hostname,
|
|
||||||
@RequestParam(required = false) String username,
|
|
||||||
@RequestParam(required = false) String password
|
|
||||||
){
|
|
||||||
|
|
||||||
DeferredResult<ResponseEntity<WVPResult>> result = new DeferredResult<>(timeout + 10L);
|
|
||||||
String uuid = UUID.randomUUID().toString();
|
|
||||||
result.onTimeout(()->{
|
|
||||||
RequestMessage msg = new RequestMessage();
|
|
||||||
msg.setId(uuid);
|
|
||||||
msg.setKey(DeferredResultHolder.CALLBACK_ONVIF);
|
|
||||||
WVPResult<String> wvpResult = new WVPResult();
|
|
||||||
wvpResult.setCode(0);
|
|
||||||
wvpResult.setMsg("获取onvif的rtsp地址超时");
|
|
||||||
msg.setData(wvpResult);
|
|
||||||
resultHolder.invokeResult(msg);
|
|
||||||
});
|
|
||||||
resultHolder.put(DeferredResultHolder.CALLBACK_ONVIF, uuid, result);
|
|
||||||
OnvifDevice onvifDevice = new OnvifDevice(hostname, username, password);
|
|
||||||
onvifServer.getRTSPUrl(timeout, onvifDevice, (errorCode, url) ->{
|
|
||||||
RequestMessage msg = new RequestMessage();
|
|
||||||
msg.setId(DeferredResultHolder.CALLBACK_ONVIF + uuid);
|
|
||||||
WVPResult<String> resultData = new WVPResult();
|
|
||||||
resultData.setCode(errorCode);
|
|
||||||
if (errorCode == 0) {
|
|
||||||
resultData.setMsg("success");
|
|
||||||
resultData.setData(url);
|
|
||||||
}else {
|
|
||||||
resultData.setMsg(url);
|
|
||||||
}
|
|
||||||
msg.setData(resultData);
|
|
||||||
|
|
||||||
resultHolder.invokeResult(msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,9 +1,11 @@
|
|||||||
package com.genersoft.iot.vmp.vmanager.streamProxy;
|
package com.genersoft.iot.vmp.vmanager.streamProxy;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||||
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
|
||||||
import com.genersoft.iot.vmp.service.IMediaServerService;
|
import com.genersoft.iot.vmp.service.IMediaServerService;
|
||||||
|
import com.genersoft.iot.vmp.service.IMediaService;
|
||||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||||
import com.genersoft.iot.vmp.service.IStreamProxyService;
|
import com.genersoft.iot.vmp.service.IStreamProxyService;
|
||||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
||||||
@ -68,10 +70,8 @@ public class StreamProxyController {
|
|||||||
public WVPResult save(@RequestBody StreamProxyItem param){
|
public WVPResult save(@RequestBody StreamProxyItem param){
|
||||||
logger.info("添加代理: " + JSONObject.toJSONString(param));
|
logger.info("添加代理: " + JSONObject.toJSONString(param));
|
||||||
if (StringUtils.isEmpty(param.getMediaServerId())) param.setMediaServerId("auto");
|
if (StringUtils.isEmpty(param.getMediaServerId())) param.setMediaServerId("auto");
|
||||||
String msg = streamProxyService.save(param);
|
if (StringUtils.isEmpty(param.getType())) param.setType("default");
|
||||||
WVPResult<Object> result = new WVPResult<>();
|
WVPResult<StreamInfo> result = streamProxyService.save(param);
|
||||||
result.setCode(0);
|
|
||||||
result.setMsg(msg);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
package com.genersoft.iot.vmp.web;
|
|
||||||
|
|
||||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
|
||||||
import com.genersoft.iot.vmp.service.IMediaService;
|
|
||||||
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
@CrossOrigin
|
|
||||||
@RestController
|
|
||||||
public class ApiCompatibleController {
|
|
||||||
|
|
||||||
private final static Logger logger = LoggerFactory.getLogger(ApiCompatibleController.class);
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private IMediaService mediaService;
|
|
||||||
|
|
||||||
@GetMapping(value = "/api/v1/stream_info_by_app_and_stream")
|
|
||||||
@ResponseBody
|
|
||||||
public WVPResult<StreamInfo> getStreamInfoByAppAndStream(HttpServletRequest request, @RequestParam String app, @RequestParam String stream){
|
|
||||||
String localAddr = request.getLocalAddr();
|
|
||||||
StreamInfo streamINfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, localAddr);
|
|
||||||
WVPResult<StreamInfo> wvpResult = new WVPResult<>();
|
|
||||||
wvpResult.setCode(0);
|
|
||||||
wvpResult.setMsg("success");
|
|
||||||
wvpResult.setData(streamINfo);
|
|
||||||
return wvpResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.genersoft.iot.vmp.web;
|
package com.genersoft.iot.vmp.web.gb28181;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||||
@ -45,49 +45,46 @@ public class ApiControlController {
|
|||||||
serial, code, command, speed));
|
serial, code, command, speed));
|
||||||
}
|
}
|
||||||
Device device = storager.queryVideoDevice(serial);
|
Device device = storager.queryVideoDevice(serial);
|
||||||
int leftRight = 0;
|
int cmdCode = 0;
|
||||||
int upDown = 0;
|
switch (command){
|
||||||
int inOut = 0;
|
|
||||||
switch (command) {
|
|
||||||
case "left":
|
case "left":
|
||||||
leftRight = 1;
|
cmdCode = 2;
|
||||||
break;
|
break;
|
||||||
case "right":
|
case "right":
|
||||||
leftRight = 2;
|
cmdCode = 1;
|
||||||
break;
|
break;
|
||||||
case "up":
|
case "up":
|
||||||
upDown = 1;
|
cmdCode = 8;
|
||||||
break;
|
break;
|
||||||
case "down":
|
case "down":
|
||||||
upDown = 2;
|
cmdCode = 4;
|
||||||
break;
|
break;
|
||||||
case "upleft":
|
case "upleft":
|
||||||
upDown = 1;
|
cmdCode = 10;
|
||||||
leftRight = 1;
|
break;
|
||||||
case "upright":
|
case "upright":
|
||||||
upDown = 1;
|
cmdCode = 9;
|
||||||
leftRight = 2;
|
|
||||||
break;
|
break;
|
||||||
case "downleft":
|
case "downleft":
|
||||||
upDown = 2;
|
cmdCode = 6;
|
||||||
leftRight = 1;
|
|
||||||
break;
|
break;
|
||||||
case "downright":
|
case "downright":
|
||||||
upDown = 2;
|
cmdCode = 5;
|
||||||
leftRight = 2;
|
|
||||||
break;
|
break;
|
||||||
case "zoomin":
|
case "zoomin":
|
||||||
inOut = 2;
|
cmdCode = 16;
|
||||||
break;
|
break;
|
||||||
case "zoomout":
|
case "zoomout":
|
||||||
inOut = 1;
|
cmdCode = 32;
|
||||||
break;
|
break;
|
||||||
case "stop":
|
case "stop":
|
||||||
|
cmdCode = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
// 默认值 50
|
// 默认值 50
|
||||||
cmder.ptzCmd(device, code, leftRight, upDown, inOut, speed==0 ? 129 : speed, 50);
|
cmder.frontEndCmd(device, code, cmdCode, speed, speed, speed);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.genersoft.iot.vmp.web;
|
package com.genersoft.iot.vmp.web.gb28181;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.genersoft.iot.vmp.conf.SipConfig;
|
import com.genersoft.iot.vmp.conf.SipConfig;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.genersoft.iot.vmp.web;
|
package com.genersoft.iot.vmp.web.gb28181;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
@ -73,7 +73,7 @@ public class ApiDeviceController {
|
|||||||
deviceJsonObject.put("ChannelCount", device.getChannelCount());
|
deviceJsonObject.put("ChannelCount", device.getChannelCount());
|
||||||
deviceJsonObject.put("RecvStreamIP", "");
|
deviceJsonObject.put("RecvStreamIP", "");
|
||||||
deviceJsonObject.put("CatalogInterval", 3600); // 通道目录抓取周期
|
deviceJsonObject.put("CatalogInterval", 3600); // 通道目录抓取周期
|
||||||
deviceJsonObject.put("SubscribeInterval", 0); // 订阅周期(秒), 0 表示后台不周期订阅
|
deviceJsonObject.put("SubscribeInterval", device.getSubscribeCycleForCatalog()); // 订阅周期(秒), 0 表示后台不周期订阅
|
||||||
deviceJsonObject.put("Online", device.getOnline() == 1);
|
deviceJsonObject.put("Online", device.getOnline() == 1);
|
||||||
deviceJsonObject.put("Password", "");
|
deviceJsonObject.put("Password", "");
|
||||||
deviceJsonObject.put("MediaTransport", device.getTransport());
|
deviceJsonObject.put("MediaTransport", device.getTransport());
|
||||||
@ -1,18 +1,20 @@
|
|||||||
package com.genersoft.iot.vmp.web;
|
package com.genersoft.iot.vmp.web.gb28181;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.genersoft.iot.vmp.common.StreamInfo;
|
import com.genersoft.iot.vmp.common.StreamInfo;
|
||||||
|
import com.genersoft.iot.vmp.conf.UserSetup;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
import com.genersoft.iot.vmp.gb28181.bean.Device;
|
||||||
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
|
||||||
|
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
|
||||||
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
|
||||||
|
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
|
||||||
|
import com.genersoft.iot.vmp.service.IPlayService;
|
||||||
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
|
||||||
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
|
||||||
import com.genersoft.iot.vmp.vmanager.gb28181.play.PlayController;
|
import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.context.request.async.DeferredResult;
|
import org.springframework.web.context.request.async.DeferredResult;
|
||||||
|
|
||||||
@ -34,15 +36,13 @@ public class ApiStreamController {
|
|||||||
private IVideoManagerStorager storager;
|
private IVideoManagerStorager storager;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IRedisCatchStorage redisCatchStorage;
|
private UserSetup userSetup;
|
||||||
|
|
||||||
|
|
||||||
// @Autowired
|
|
||||||
// private ZLMRESTfulUtils zlmresTfulUtils;
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PlayController playController;
|
private IRedisCatchStorage redisCatchStorage;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IPlayService playService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实时直播 - 开始直播
|
* 实时直播 - 开始直播
|
||||||
@ -69,7 +69,7 @@ public class ApiStreamController {
|
|||||||
@RequestParam(required = false)String timeout
|
@RequestParam(required = false)String timeout
|
||||||
|
|
||||||
){
|
){
|
||||||
DeferredResult<JSONObject> resultDeferredResult = new DeferredResult<JSONObject>();
|
DeferredResult<JSONObject> resultDeferredResult = new DeferredResult<>(userSetup.getPlayTimeout() + 10);
|
||||||
Device device = storager.queryVideoDevice(serial);
|
Device device = storager.queryVideoDevice(serial);
|
||||||
if (device == null ) {
|
if (device == null ) {
|
||||||
JSONObject result = new JSONObject();
|
JSONObject result = new JSONObject();
|
||||||
@ -99,11 +99,9 @@ public class ApiStreamController {
|
|||||||
result.put("error","channel[ " + code + " ]offline");
|
result.put("error","channel[ " + code + " ]offline");
|
||||||
resultDeferredResult.setResult(result);
|
resultDeferredResult.setResult(result);
|
||||||
}
|
}
|
||||||
DeferredResult<ResponseEntity<String>> play = playController.play(serial, code);
|
MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
|
||||||
|
PlayResult play = playService.play(newMediaServerItem, serial, code, (mediaServerItem, response)->{
|
||||||
play.setResultHandler((Object o)->{
|
StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(serial, code);
|
||||||
ResponseEntity<String> responseEntity = (ResponseEntity)o;
|
|
||||||
StreamInfo streamInfo = JSON.parseObject(responseEntity.getBody(), StreamInfo.class);
|
|
||||||
JSONObject result = new JSONObject();
|
JSONObject result = new JSONObject();
|
||||||
result.put("StreamID", streamInfo.getStreamId());
|
result.put("StreamID", streamInfo.getStreamId());
|
||||||
result.put("DeviceID", device.getDeviceId());
|
result.put("DeviceID", device.getDeviceId());
|
||||||
@ -134,7 +132,23 @@ public class ApiStreamController {
|
|||||||
result.put("NumOutputs", "");
|
result.put("NumOutputs", "");
|
||||||
result.put("CascadeSize", "");
|
result.put("CascadeSize", "");
|
||||||
result.put("RelaySize", "");
|
result.put("RelaySize", "");
|
||||||
result.put("ChannelPTZType", 0);
|
result.put("ChannelPTZType", "0");
|
||||||
|
resultDeferredResult.setResult(result);
|
||||||
|
// Class<?> aClass = responseEntity.getClass().getSuperclass();
|
||||||
|
// Field body = null;
|
||||||
|
// try {
|
||||||
|
// // 使用反射动态修改返回的body
|
||||||
|
// body = aClass.getDeclaredField("body");
|
||||||
|
// body.setAccessible(true);
|
||||||
|
// body.set(responseEntity, result);
|
||||||
|
// } catch (NoSuchFieldException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// } catch (IllegalAccessException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
}, (eventResult) -> {
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
result.put("error", "channel[ " + code + " ] " + eventResult.msg);
|
||||||
resultDeferredResult.setResult(result);
|
resultDeferredResult.setResult(result);
|
||||||
});
|
});
|
||||||
return resultDeferredResult;
|
return resultDeferredResult;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.genersoft.iot.vmp.web;
|
package com.genersoft.iot.vmp.web.gb28181;
|
||||||
|
|
||||||
import com.genersoft.iot.vmp.service.IUserService;
|
import com.genersoft.iot.vmp.service.IUserService;
|
||||||
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
import com.genersoft.iot.vmp.storager.dao.dto.User;
|
||||||
@ -137,6 +137,8 @@ logging:
|
|||||||
com.genersoft.iot.vmp.gb28181: info
|
com.genersoft.iot.vmp.gb28181: info
|
||||||
# [根据业务需求配置]
|
# [根据业务需求配置]
|
||||||
user-settings:
|
user-settings:
|
||||||
|
# [可选] 服务ID,不写则为000000
|
||||||
|
server-id:
|
||||||
# [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true
|
# [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true
|
||||||
auto-apply-play: false
|
auto-apply-play: false
|
||||||
# [可选] 部分设备需要扩展SDP,需要打开此设置
|
# [可选] 部分设备需要扩展SDP,需要打开此设置
|
||||||
|
|||||||
@ -2,11 +2,11 @@ spring:
|
|||||||
# REDIS数据库配置
|
# REDIS数据库配置
|
||||||
redis:
|
redis:
|
||||||
# [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
|
# [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
|
||||||
host: ${REDIS_HOST:127.0.0.1}
|
host: 127.0.0.1
|
||||||
# [必须修改] 端口号
|
# [必须修改] 端口号
|
||||||
port: ${REDIS_PORT:6379}
|
port: 6379
|
||||||
# [可选] 数据库 DB
|
# [可选] 数据库 DB
|
||||||
database: ${REDIS_DB:6}
|
database: 6
|
||||||
# [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
|
# [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
|
||||||
password: ${REDIS_PWD:}
|
password: ${REDIS_PWD:}
|
||||||
# [可选] 超时时间
|
# [可选] 超时时间
|
||||||
@ -43,11 +43,11 @@ sip:
|
|||||||
# 后两位为行业编码,定义参照附录D.3
|
# 后两位为行业编码,定义参照附录D.3
|
||||||
# 3701020049标识山东济南历下区 信息行业接入
|
# 3701020049标识山东济南历下区 信息行业接入
|
||||||
# [可选]
|
# [可选]
|
||||||
domain: ${WVP_DOMAIN:4401020049}
|
domain: 4401020049
|
||||||
# [可选]
|
# [可选]
|
||||||
id: ${WVP_ID:44010200492000000001}
|
id: 44010200492000000001
|
||||||
# [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验
|
# [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验
|
||||||
password: ${WVP_PWD:admin123}
|
password: admin123
|
||||||
|
|
||||||
#zlm 默认服务器配置
|
#zlm 默认服务器配置
|
||||||
media:
|
media:
|
||||||
|
|||||||
98
src/main/resources/application-docker.yml
Normal file
98
src/main/resources/application-docker.yml
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
spring:
|
||||||
|
# REDIS数据库配置
|
||||||
|
redis:
|
||||||
|
# [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
|
||||||
|
host: ${REDIS_HOST:127.0.0.1}
|
||||||
|
# [必须修改] 端口号
|
||||||
|
port: ${REDIS_PORT:6379}
|
||||||
|
# [可选] 数据库 DB
|
||||||
|
database: ${REDIS_DB:6}
|
||||||
|
# [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接
|
||||||
|
password: ${REDIS_PWD:}
|
||||||
|
# [可选] 超时时间
|
||||||
|
timeout: 10000
|
||||||
|
# [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置
|
||||||
|
datasource:
|
||||||
|
# 使用mysql 打开23-28行注释, 删除29-36行
|
||||||
|
# name: wvp
|
||||||
|
# url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
|
||||||
|
# username:
|
||||||
|
# password:
|
||||||
|
# type: com.alibaba.druid.pool.DruidDataSource
|
||||||
|
# driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
name: eiot
|
||||||
|
url: jdbc:sqlite::resource:wvp.sqlite
|
||||||
|
username:
|
||||||
|
password:
|
||||||
|
type: com.alibaba.druid.pool.DruidDataSource
|
||||||
|
driver-class-name: org.sqlite.JDBC
|
||||||
|
max-active: 1
|
||||||
|
min-idle: 1
|
||||||
|
|
||||||
|
# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口
|
||||||
|
server:
|
||||||
|
port: 18080
|
||||||
|
|
||||||
|
# 作为28181服务器的配置
|
||||||
|
sip:
|
||||||
|
# [必须修改] 本机的IP
|
||||||
|
ip: ${WVP_HOST}
|
||||||
|
# [可选] 28181服务监听的端口
|
||||||
|
port: 5060
|
||||||
|
# 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007)
|
||||||
|
# 后两位为行业编码,定义参照附录D.3
|
||||||
|
# 3701020049标识山东济南历下区 信息行业接入
|
||||||
|
# [可选]
|
||||||
|
domain: ${WVP_DOMAIN:4401020049}
|
||||||
|
# [可选]
|
||||||
|
id: ${WVP_ID:44010200492000000001}
|
||||||
|
# [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验
|
||||||
|
password: ${WVP_PWD:admin123}
|
||||||
|
|
||||||
|
#zlm 默认服务器配置
|
||||||
|
media:
|
||||||
|
# [必须修改] zlm服务器的内网IP
|
||||||
|
ip: 127.0.0.1
|
||||||
|
# [必须修改] zlm服务器的http.port
|
||||||
|
http-port: 80
|
||||||
|
# [可选] zlm服务器的hook.admin_params=secret
|
||||||
|
secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc
|
||||||
|
# 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试
|
||||||
|
rtp:
|
||||||
|
# [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
|
||||||
|
enable: true
|
||||||
|
# [可选] 在此范围内选择端口用于媒体流传输,
|
||||||
|
port-range: 30000,30500 # 端口范围
|
||||||
|
# [可选] 国标级联在此范围内选择端口发送媒体流,
|
||||||
|
send-port-range: 30000,30500 # 端口范围
|
||||||
|
# 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用
|
||||||
|
record-assist-port: 18081
|
||||||
|
sdp-ip: ${WVP_HOST}
|
||||||
|
stream-ip: ${WVP_HOST}
|
||||||
|
# [可选] 日志配置, 一般不需要改
|
||||||
|
logging:
|
||||||
|
file:
|
||||||
|
name: logs/wvp.log
|
||||||
|
max-history: 30
|
||||||
|
max-size: 10MB
|
||||||
|
total-size-cap: 300MB
|
||||||
|
level:
|
||||||
|
com.genersoft.iot: debug
|
||||||
|
com.genersoft.iot.vmp.storager.dao: info
|
||||||
|
com.genersoft.iot.vmp.gb28181: info
|
||||||
|
|
||||||
|
# [根据业务需求配置]
|
||||||
|
user-settings:
|
||||||
|
# 推流直播是否录制
|
||||||
|
record-push-live: true
|
||||||
|
auto-apply-play: false
|
||||||
|
|
||||||
|
# 在线文档: swagger-ui(生产环境建议关闭)
|
||||||
|
swagger-ui:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# 版本信息, 不需修改
|
||||||
|
version:
|
||||||
|
version: "@project.version@"
|
||||||
|
description: "@project.description@"
|
||||||
|
artifact-id: "@project.artifactId@"
|
||||||
Binary file not shown.
@ -6,7 +6,9 @@
|
|||||||
</el-header>
|
</el-header>
|
||||||
<el-main>
|
<el-main>
|
||||||
<div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;">
|
<div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;">
|
||||||
<span style="font-size: 1rem; font-weight: bold;">云端录像</span>
|
<span v-if="!recordDetail" >云端录像</span>
|
||||||
|
<el-page-header v-if="recordDetail" @back="backToList" content="云端录像">
|
||||||
|
</el-page-header>
|
||||||
<div style="position: absolute; right: 5rem; top: 0.3rem;">
|
<div style="position: absolute; right: 5rem; top: 0.3rem;">
|
||||||
节点选择:
|
节点选择:
|
||||||
<el-select size="mini" @change="chooseMediaChange" style="width: 16rem; margin-right: 1rem;" v-model="mediaServerId" placeholder="请选择" :disabled="recordDetail">
|
<el-select size="mini" @change="chooseMediaChange" style="width: 16rem; margin-right: 1rem;" v-model="mediaServerId" placeholder="请选择" :disabled="recordDetail">
|
||||||
@ -20,7 +22,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="position: absolute; right: 1rem; top: 0.3rem;">
|
<div style="position: absolute; right: 1rem; top: 0.3rem;">
|
||||||
<el-button v-if="!recordDetail" icon="el-icon-refresh-right" circle size="mini" :loading="loading" @click="getRecordList()"></el-button>
|
<el-button v-if="!recordDetail" icon="el-icon-refresh-right" circle size="mini" :loading="loading" @click="getRecordList()"></el-button>
|
||||||
<el-button v-if="recordDetail" icon="el-icon-arrow-left" circle size="mini" @click="backToList()"></el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!recordDetail">
|
<div v-if="!recordDetail">
|
||||||
@ -53,7 +54,7 @@
|
|||||||
:total="total">
|
:total="total">
|
||||||
</el-pagination>
|
</el-pagination>
|
||||||
</div>
|
</div>
|
||||||
<cloud-record-detail ref="cloudRecordDetail" v-if="recordDetail" :recordFile="chooseRecord" :mediaServerId="mediaServerId" ></cloud-record-detail>
|
<cloud-record-detail ref="cloudRecordDetail" v-if="recordDetail" :recordFile="chooseRecord" :mediaServerId="mediaServerId" :mediaServerPath="mediaServerPath" ></cloud-record-detail>
|
||||||
</el-main>
|
</el-main>
|
||||||
</el-container>
|
</el-container>
|
||||||
</div>
|
</div>
|
||||||
@ -72,6 +73,7 @@
|
|||||||
return {
|
return {
|
||||||
mediaServerList: [], // 滅体节点列表
|
mediaServerList: [], // 滅体节点列表
|
||||||
mediaServerId: null, // 媒体服务
|
mediaServerId: null, // 媒体服务
|
||||||
|
mediaServerPath: null, // 媒体服务地址
|
||||||
recordList: [], // 设备列表
|
recordList: [], // 设备列表
|
||||||
chooseRecord: null, // 媒体服务
|
chooseRecord: null, // 媒体服务
|
||||||
|
|
||||||
@ -115,6 +117,11 @@
|
|||||||
that.mediaServerList = data.data;
|
that.mediaServerList = data.data;
|
||||||
if (that.mediaServerList.length > 0) {
|
if (that.mediaServerList.length > 0) {
|
||||||
that.mediaServerId = that.mediaServerList[0].id
|
that.mediaServerId = that.mediaServerList[0].id
|
||||||
|
let port = that.mediaServerList[0].httpPort;
|
||||||
|
if (location.protocol === "https:" && that.mediaServerList[0].httpSSlPort) {
|
||||||
|
port = that.mediaServerList[0].httpSSlPort
|
||||||
|
}
|
||||||
|
that.mediaServerPath = location.protocol + "//" + that.mediaServerList[0].streamIp + ":" + port
|
||||||
that.getRecordList();
|
that.getRecordList();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -15,7 +15,8 @@
|
|||||||
<i class="el-icon-video-camera" ></i>
|
<i class="el-icon-video-camera" ></i>
|
||||||
{{ item.substring(0,17)}}
|
{{ item.substring(0,17)}}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<a class="el-icon-download" style="color: #409EFF;font-weight: 600;margin-left: 10px;" :href="`${basePath}/${mediaServerId}/record/${recordFile.app}/${recordFile.stream}/${chooseDate}/${item}`" download />
|
<!-- <a class="el-icon-download" style="color: #409EFF;font-weight: 600;margin-left: 10px;" :href="`${basePath}/${mediaServerId}/record/${recordFile.app}/${recordFile.stream}/${chooseDate}/${item}`" download />-->
|
||||||
|
<a class="el-icon-download" style="color: #409EFF;font-weight: 600;margin-left: 10px;" :href="`${basePath}/download.html?url=${recordFile.app}/${recordFile.stream}/${chooseDate}/${item}`" target="_blank" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -75,7 +76,7 @@
|
|||||||
<li class="task-list-item" v-for="item in taskListEnded">
|
<li class="task-list-item" v-for="item in taskListEnded">
|
||||||
<div class="task-list-item-box" style="height: 2rem;line-height: 2rem;">
|
<div class="task-list-item-box" style="height: 2rem;line-height: 2rem;">
|
||||||
<span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span>
|
<span>{{ item.startTime.substr(10) }}-{{item.endTime.substr(10)}}</span>
|
||||||
<a class="el-icon-download download-btn" :href="basePath + '/' + item.recordFile" download >
|
<a class="el-icon-download download-btn" :href="basePath + '/download.html?url=../' + item.recordFile" target="_blank">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@ -111,10 +112,10 @@
|
|||||||
components: {
|
components: {
|
||||||
uiHeader, player
|
uiHeader, player
|
||||||
},
|
},
|
||||||
props: ['recordFile', 'mediaServerId', 'dateFiles'],
|
props: ['recordFile', 'mediaServerId', 'dateFiles', 'mediaServerPath'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
basePath: process.env.NODE_ENV === 'development'?`${location.origin}/debug/zlm/${this.mediaServerId}`:`${location.origin}/zlm/${this.mediaServerId}`,
|
basePath: `${this.mediaServerPath}/record`,
|
||||||
dateFilesObj: [],
|
dateFilesObj: [],
|
||||||
detailFiles: [],
|
detailFiles: [],
|
||||||
chooseDate: null,
|
chooseDate: null,
|
||||||
@ -264,7 +265,7 @@
|
|||||||
this.videoUrl = "";
|
this.videoUrl = "";
|
||||||
}else {
|
}else {
|
||||||
// TODO 控制列表滚动条
|
// TODO 控制列表滚动条
|
||||||
this.videoUrl = `${this.basePath}/${this.mediaServerId}/record/${this.recordFile.app}/${this.recordFile.stream}/${this.chooseDate}/${this.choosedFile}`
|
this.videoUrl = `${this.basePath}/${this.recordFile.app}/${this.recordFile.stream}/${this.chooseDate}/${this.choosedFile}`
|
||||||
console.log(this.videoUrl)
|
console.log(this.videoUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -66,6 +66,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="无人观看自动删除" width="160" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div slot="reference" class="name-wrapper">
|
||||||
|
<el-tag size="medium" v-if="scope.row.enable_remove_none_reader">已启用</el-tag>
|
||||||
|
<el-tag size="medium" type="info" v-if="!scope.row.enable_remove_none_reader">未启用</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
|
||||||
<el-table-column label="操作" width="360" align="center" fixed="right">
|
<el-table-column label="操作" width="360" align="center" fixed="right">
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
<!-- <el-menu-item index="/setting/sip">国标服务</el-menu-item>-->
|
<!-- <el-menu-item index="/setting/sip">国标服务</el-menu-item>-->
|
||||||
<!-- <el-menu-item index="/setting/media">媒体服务</el-menu-item>-->
|
<!-- <el-menu-item index="/setting/media">媒体服务</el-menu-item>-->
|
||||||
<!-- </el-submenu>-->
|
<!-- </el-submenu>-->
|
||||||
<el-switch v-model="alarmNotify" active-text="报警信息推送" style="display: block float: right" @change="sseControl"></el-switch>
|
<el-switch v-model="alarmNotify" active-text="报警信息推送" style="display: block float: right" @change="alarmNotifyChannge"></el-switch>
|
||||||
<!-- <el-menu-item style="float: right;" @click="loginout">退出</el-menu-item>-->
|
<!-- <el-menu-item style="float: right;" @click="loginout">退出</el-menu-item>-->
|
||||||
<el-submenu index="" style="float: right;" >
|
<el-submenu index="" style="float: right;" >
|
||||||
<template slot="title">欢迎,{{this.$cookies.get("session").username}}</template>
|
<template slot="title">欢迎,{{this.$cookies.get("session").username}}</template>
|
||||||
@ -35,11 +35,23 @@ export default {
|
|||||||
components: { Notification, changePasswordDialog },
|
components: { Notification, changePasswordDialog },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
alarmNotify: true,
|
alarmNotify: false,
|
||||||
sseSource: null,
|
sseSource: null,
|
||||||
activeIndex: this.$route.path,
|
activeIndex: this.$route.path,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
created(){
|
||||||
|
if (this.$route.path.startsWith("/channelList")){
|
||||||
|
this.activeIndex = "/deviceList"
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
window.addEventListener('beforeunload', e => this.beforeunloadHandler(e))
|
||||||
|
// window.addEventListener('unload', e => this.unloadHandler(e))
|
||||||
|
this.alarmNotify = this.getAlarmSwitchStatus() === "true";
|
||||||
|
this.sseControl();
|
||||||
|
},
|
||||||
methods:{
|
methods:{
|
||||||
loginout(){
|
loginout(){
|
||||||
this.$axios({
|
this.$axios({
|
||||||
@ -65,6 +77,10 @@ export default {
|
|||||||
beforeunloadHandler() {
|
beforeunloadHandler() {
|
||||||
this.sseSource.close();
|
this.sseSource.close();
|
||||||
},
|
},
|
||||||
|
alarmNotifyChannge(){
|
||||||
|
this.setAlarmSwitchStatus()
|
||||||
|
this.sseControl()
|
||||||
|
},
|
||||||
sseControl() {
|
sseControl() {
|
||||||
let that = this;
|
let that = this;
|
||||||
if (this.alarmNotify) {
|
if (this.alarmNotify) {
|
||||||
@ -90,31 +106,35 @@ export default {
|
|||||||
}
|
}
|
||||||
}, false);
|
}, false);
|
||||||
} else {
|
} else {
|
||||||
this.sseSource.removeEventListener('open', null);
|
if (this.sseSource != null) {
|
||||||
this.sseSource.removeEventListener('message', null);
|
this.sseSource.removeEventListener('open', null);
|
||||||
this.sseSource.removeEventListener('error', null);
|
this.sseSource.removeEventListener('message', null);
|
||||||
this.sseSource.close();
|
this.sseSource.removeEventListener('error', null);
|
||||||
|
this.sseSource.close();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
getAlarmSwitchStatus(){
|
||||||
|
if (localStorage.getItem("alarmSwitchStatus") == null) {
|
||||||
|
localStorage.setItem("alarmSwitchStatus", false);
|
||||||
|
}
|
||||||
|
return localStorage.getItem("alarmSwitchStatus");
|
||||||
|
},
|
||||||
|
setAlarmSwitchStatus(){
|
||||||
|
localStorage.setItem("alarmSwitchStatus", this.alarmNotify);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created(){
|
|
||||||
if (this.$route.path.startsWith("/channelList")){
|
|
||||||
this.activeIndex = "/deviceList"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
window.addEventListener('beforeunload', e => this.beforeunloadHandler(e))
|
|
||||||
// window.addEventListener('unload', e => this.unloadHandler(e))
|
|
||||||
this.sseControl();
|
|
||||||
},
|
|
||||||
destroyed() {
|
destroyed() {
|
||||||
window.removeEventListener('beforeunload', e => this.beforeunloadHandler(e))
|
window.removeEventListener('beforeunload', e => this.beforeunloadHandler(e))
|
||||||
this.sseSource.removeEventListener('open', null);
|
if (this.sseSource != null) {
|
||||||
this.sseSource.removeEventListener('message', null);
|
this.sseSource.removeEventListener('open', null);
|
||||||
this.sseSource.removeEventListener('error', null);
|
this.sseSource.removeEventListener('message', null);
|
||||||
this.sseSource.close();
|
this.sseSource.removeEventListener('error', null);
|
||||||
// window.removeEventListener('unload', e => this.unloadHandler(e))
|
this.sseSource.close();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -106,6 +106,7 @@
|
|||||||
<el-checkbox label="启用" v-model="proxyParam.enable" ></el-checkbox>
|
<el-checkbox label="启用" v-model="proxyParam.enable" ></el-checkbox>
|
||||||
<el-checkbox label="转HLS" v-model="proxyParam.enable_hls" ></el-checkbox>
|
<el-checkbox label="转HLS" v-model="proxyParam.enable_hls" ></el-checkbox>
|
||||||
<el-checkbox label="MP4录制" v-model="proxyParam.enable_mp4" ></el-checkbox>
|
<el-checkbox label="MP4录制" v-model="proxyParam.enable_mp4" ></el-checkbox>
|
||||||
|
<el-checkbox label="无人观看自动删除" v-model="proxyParam.enable_remove_none_reader" ></el-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -160,7 +161,7 @@ export default {
|
|||||||
type: "default",
|
type: "default",
|
||||||
app: null,
|
app: null,
|
||||||
stream: null,
|
stream: null,
|
||||||
url: "rtmp://58.200.131.2/livetv/cctv5hd",
|
url: "",
|
||||||
src_url: null,
|
src_url: null,
|
||||||
timeout_ms: null,
|
timeout_ms: null,
|
||||||
ffmpeg_cmd_key: null,
|
ffmpeg_cmd_key: null,
|
||||||
@ -169,6 +170,7 @@ export default {
|
|||||||
enable: true,
|
enable: true,
|
||||||
enable_hls: true,
|
enable_hls: true,
|
||||||
enable_mp4: false,
|
enable_mp4: false,
|
||||||
|
enable_remove_none_reader: false,
|
||||||
platformGbId: null,
|
platformGbId: null,
|
||||||
mediaServerId: "auto",
|
mediaServerId: "auto",
|
||||||
},
|
},
|
||||||
|
|||||||
@ -39,7 +39,34 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}-->
|
<!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}-->
|
||||||
<el-tab-pane label="录像查询" name="record" v-if="showRrecord">
|
<el-tab-pane label="录像查询" name="record" v-if="showRrecord">
|
||||||
<el-date-picker size="mini" v-model="videoHistory.date" type="date" value-format="yyyy-MM-dd" placeholder="日期" @change="queryRecords()"></el-date-picker>
|
<div style="width: 100%;">
|
||||||
|
<div style="width: 100%; text-align: left">
|
||||||
|
<span>录像控制</span>
|
||||||
|
<el-button-group style="margin-left: 1rem;">
|
||||||
|
<el-button size="mini" class="iconfont icon-zanting" title="开始" @click="gbPause()"></el-button>
|
||||||
|
<el-button size="mini" class="iconfont icon-kaishi" title="暂停" @click="gbPlay()"></el-button>
|
||||||
|
<el-dropdown size="mini" title="播放倍速" style="margin-left: 1px;" @command="gbScale">
|
||||||
|
<el-button size="mini">
|
||||||
|
倍速 <i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</el-button>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item command="0.25">0.25倍速</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="0.5">0.5倍速</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="1.0">1倍速</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="2.0">2倍速</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="4.0">4倍速</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
</el-button-group>
|
||||||
|
<el-date-picker style="float: right;" size="mini" v-model="videoHistory.date" type="date" value-format="yyyy-MM-dd" placeholder="日期" @change="queryRecords()"></el-date-picker>
|
||||||
|
</div>
|
||||||
|
<div style="width: 100%; text-align: left">
|
||||||
|
<span class="demonstration" style="padding: 12px 36px 12px 0;float: left;">{{showTimeText}}</span>
|
||||||
|
<el-slider style="width: 80%; float:left;" v-model="sliderTime" @change="gbSeek" :show-tooltip="false"></el-slider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<el-table :data="videoHistory.searchHistoryResult" height="150" v-loading="recordsLoading">
|
<el-table :data="videoHistory.searchHistoryResult" height="150" v-loading="recordsLoading">
|
||||||
<el-table-column label="名称" prop="name"></el-table-column>
|
<el-table-column label="名称" prop="name"></el-table-column>
|
||||||
<el-table-column label="文件" prop="filePath"></el-table-column>
|
<el-table-column label="文件" prop="filePath"></el-table-column>
|
||||||
@ -210,6 +237,10 @@ export default {
|
|||||||
showPtz: true,
|
showPtz: true,
|
||||||
showRrecord: true,
|
showRrecord: true,
|
||||||
tracksNotLoaded: false,
|
tracksNotLoaded: false,
|
||||||
|
sliderTime: 0,
|
||||||
|
seekTime: 0,
|
||||||
|
recordStartTime: 0,
|
||||||
|
showTimeText: "00:00:00",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -287,11 +318,13 @@ export default {
|
|||||||
// return `http://${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`;
|
// return `http://${baseZlmApi}/${streamInfo.app}/${streamInfo.streamId}.flv`;
|
||||||
if (location.protocol === "https:") {
|
if (location.protocol === "https:") {
|
||||||
if (streamInfo.wss_flv === null) {
|
if (streamInfo.wss_flv === null) {
|
||||||
this.$message({
|
console.error("媒体服务器未配置ssl端口, 使用http端口")
|
||||||
showClose: true,
|
// this.$message({
|
||||||
message: '媒体服务器未配置ssl端口',
|
// showClose: true,
|
||||||
type: 'error'
|
// message: '媒体服务器未配置ssl端口, ',
|
||||||
});
|
// type: 'error'
|
||||||
|
// });
|
||||||
|
return streamInfo.ws_flv
|
||||||
}else {
|
}else {
|
||||||
return streamInfo.wss_flv;
|
return streamInfo.wss_flv;
|
||||||
}
|
}
|
||||||
@ -431,6 +464,14 @@ export default {
|
|||||||
},
|
},
|
||||||
playRecord: function (row) {
|
playRecord: function (row) {
|
||||||
let that = this;
|
let that = this;
|
||||||
|
|
||||||
|
let startTime = row.startTime
|
||||||
|
this.recordStartTime = row.startTime
|
||||||
|
this.showTimeText = row.startTime.split(" ")[1]
|
||||||
|
let endtime = row.endTime
|
||||||
|
this.sliderTime = 0;
|
||||||
|
this.seekTime = new Date(endtime).getTime() - new Date(startTime).getTime();
|
||||||
|
console.log(this.seekTime)
|
||||||
if (that.streamId != "") {
|
if (that.streamId != "") {
|
||||||
that.stopPlayRecord(function () {
|
that.stopPlayRecord(function () {
|
||||||
that.streamId = "",
|
that.streamId = "",
|
||||||
@ -578,7 +619,45 @@ export default {
|
|||||||
}
|
}
|
||||||
console.log(resultArray)
|
console.log(resultArray)
|
||||||
return resultArray;
|
return resultArray;
|
||||||
|
},
|
||||||
|
gbPlay(){
|
||||||
|
console.log('前端控制:播放');
|
||||||
|
this.$axios({
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/playback/resume/' + this.streamId
|
||||||
|
}).then((res)=> {
|
||||||
|
this.$refs.videoPlayer.play(this.videoUrl)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
gbPause(){
|
||||||
|
console.log('前端控制:暂停');
|
||||||
|
this.$axios({
|
||||||
|
method: 'get',
|
||||||
|
url: '/api/playback/pause/' + this.streamId
|
||||||
|
}).then(function (res) {});
|
||||||
|
},
|
||||||
|
gbScale(command){
|
||||||
|
console.log('前端控制:倍速 ' + command);
|
||||||
|
this.$axios({
|
||||||
|
method: 'get',
|
||||||
|
url: `/api/playback/speed/${this.streamId }/${command}`
|
||||||
|
}).then(function (res) {});
|
||||||
|
},
|
||||||
|
gbSeek(val){
|
||||||
|
console.log('前端控制:seek ');
|
||||||
|
console.log(this.seekTime);
|
||||||
|
console.log(this.sliderTime);
|
||||||
|
let showTime = new Date(new Date(this.recordStartTime).getTime() + this.seekTime * val / 100)
|
||||||
|
let hour = showTime.getHours();
|
||||||
|
let minutes = showTime.getMinutes();
|
||||||
|
let seconds = showTime.getSeconds();
|
||||||
|
this.showTimeText = (hour < 10?("0" + hour):hour) + ":" + (minutes<10?("0" + minutes):minutes) + ":" + (seconds<10?("0" + seconds):seconds)
|
||||||
|
this.$axios({
|
||||||
|
method: 'get',
|
||||||
|
url: `/api/playback/seek/${this.streamId }/` + Math.floor(this.seekTime * val / 100000)
|
||||||
|
}).then(function (res) {});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
<el-input v-model="platform.serverIP" clearable></el-input>
|
<el-input v-model="platform.serverIP" clearable></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="SIP服务端口" prop="serverPort">
|
<el-form-item label="SIP服务端口" prop="serverPort">
|
||||||
<el-input v-model="platform.serverPort" clearable></el-input>
|
<el-input v-model="platform.serverPort" clearable type="number"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="设备国标编号" prop="deviceGBId">
|
<el-form-item label="设备国标编号" prop="deviceGBId">
|
||||||
<el-input v-model="platform.deviceGBId" clearable></el-input>
|
<el-input v-model="platform.deviceGBId" clearable></el-input>
|
||||||
@ -35,7 +35,7 @@
|
|||||||
<el-input v-model="platform.deviceIp" :disabled="true"></el-input>
|
<el-input v-model="platform.deviceIp" :disabled="true"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="本地端口" prop="devicePort">
|
<el-form-item label="本地端口" prop="devicePort">
|
||||||
<el-input v-model="platform.devicePort" :disabled="true"></el-input>
|
<el-input v-model="platform.devicePort" :disabled="true" type="number"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -236,6 +236,17 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
/* 谷歌 */
|
||||||
|
input::-webkit-outer-spin-button,
|
||||||
|
input::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
/* 火狐 */
|
||||||
|
input{
|
||||||
|
-moz-appearance:textfield;
|
||||||
|
}
|
||||||
.control-wrapper-not-used {
|
.control-wrapper-not-used {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 6.25rem;
|
width: 6.25rem;
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 1291092 */
|
font-family: "iconfont"; /* Project id 1291092 */
|
||||||
src: url('iconfont.woff2?t=1631767887536') format('woff2'),
|
src: url('iconfont.woff2?t=1637741914969') format('woff2'),
|
||||||
url('iconfont.woff?t=1631767887536') format('woff'),
|
url('iconfont.woff?t=1637741914969') format('woff'),
|
||||||
url('iconfont.ttf?t=1631767887536') format('truetype');
|
url('iconfont.ttf?t=1637741914969') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
@ -13,6 +13,226 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-suoxiao:before {
|
||||||
|
content: "\e79a";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shanchu3:before {
|
||||||
|
content: "\e79b";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-chehui:before {
|
||||||
|
content: "\e79c";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wenben:before {
|
||||||
|
content: "\e79d";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zhongzuo:before {
|
||||||
|
content: "\e79e";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-jianqie:before {
|
||||||
|
content: "\e79f";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-fangda:before {
|
||||||
|
content: "\e7a0";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-fangdazhanshi:before {
|
||||||
|
content: "\e7a1";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-qianjin:before {
|
||||||
|
content: "\e7a2";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-kuaijin:before {
|
||||||
|
content: "\e7a3";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-diyigeshipin:before {
|
||||||
|
content: "\e7a4";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-kuaitui:before {
|
||||||
|
content: "\e7a5";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-kaishi:before {
|
||||||
|
content: "\e7a7";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zuihouyigeshipin:before {
|
||||||
|
content: "\e7a8";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zanting:before {
|
||||||
|
content: "\e7a9";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zhankai:before {
|
||||||
|
content: "\e7aa";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-bendisucai:before {
|
||||||
|
content: "\e7ab";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-luzhi:before {
|
||||||
|
content: "\e7ac";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-ossziyuan:before {
|
||||||
|
content: "\e7ad";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-chuangjianzhinengfenxirenwu:before {
|
||||||
|
content: "\e7ae";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-sousuo3:before {
|
||||||
|
content: "\e7af";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-gengduo:before {
|
||||||
|
content: "\e7b0";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tianjia:before {
|
||||||
|
content: "\e7b1";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xiazai:before {
|
||||||
|
content: "\e7b2";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-biaojibeifen:before {
|
||||||
|
content: "\e7b3";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-bendisucaibeifen:before {
|
||||||
|
content: "\e7b4";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-luzhibeifen:before {
|
||||||
|
content: "\e7b5";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-ossziyuanbeifen:before {
|
||||||
|
content: "\e7b6";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-bianji3:before {
|
||||||
|
content: "\e7b7";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-cuti:before {
|
||||||
|
content: "\e7b8";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xieti:before {
|
||||||
|
content: "\e7b9";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xiahuaxian:before {
|
||||||
|
content: "\e7ba";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wuxiaoguo:before {
|
||||||
|
content: "\e7bb";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-sousuo4:before {
|
||||||
|
content: "\e7bc";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-gouwuche:before {
|
||||||
|
content: "\e7bd";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shuaxin2:before {
|
||||||
|
content: "\e7be";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xiaoxi:before {
|
||||||
|
content: "\e7bf";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wushouquan:before {
|
||||||
|
content: "\e7c0";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tishi2:before {
|
||||||
|
content: "\e7c1";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tishi1:before {
|
||||||
|
content: "\e7c2";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shouquanchenggong:before {
|
||||||
|
content: "\e7c3";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-sousuo5:before {
|
||||||
|
content: "\e7c4";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shuaxin3:before {
|
||||||
|
content: "\e7c5";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xiazai1:before {
|
||||||
|
content: "\e7c6";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-shangchuan:before {
|
||||||
|
content: "\e7c7";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-guanbi:before {
|
||||||
|
content: "\e7c8";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wangye-loading:before {
|
||||||
|
content: "\e7c9";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-bianzubeifen3:before {
|
||||||
|
content: "\e7ca";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-xingzhuangbeifen:before {
|
||||||
|
content: "\e7cb";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-bianzubeifen:before {
|
||||||
|
content: "\e7cc";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-zhuanchang:before {
|
||||||
|
content: "\e7cd";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-meizi:before {
|
||||||
|
content: "\e7ce";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-daimabeifen:before {
|
||||||
|
content: "\e7cf";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-suoxiao1:before {
|
||||||
|
content: "\e7d0";
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-ai19:before {
|
||||||
|
content: "\e799";
|
||||||
|
}
|
||||||
|
|
||||||
.icon-online:before {
|
.icon-online:before {
|
||||||
content: "\e600";
|
content: "\e600";
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user