Browse Source

优化集群方案, 每个zlm一套ssrc;

优化集群下的docker接入逻辑;
更正sql脚本;
支持重启不设置设备离线。重启SIP事务不丢失
pull/136/head
64850858 3 years ago
parent
commit
3469271ec2
  1. 7
      pom.xml
  2. 204
      sql/mysql.sql
  3. 6
      src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
  4. 188
      src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
  5. 20
      src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
  6. 37
      src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java
  7. 31
      src/main/java/com/genersoft/iot/vmp/conf/SipDeviceRunner.java
  8. 28
      src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
  9. 70
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java
  10. 3
      src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java
  11. 140
      src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java
  12. 101
      src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java
  13. 111
      src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java
  14. 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
  15. 10
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
  16. 211
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
  17. 3
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java
  18. 3
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java
  19. 5
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
  20. 14
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java
  21. 24
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
  22. 8
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
  23. 9
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
  24. 45
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
  25. 80
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
  26. 34
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
  27. 92
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IMediaServerItem.java
  28. 54
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
  29. 30
      src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
  30. 5
      src/main/java/com/genersoft/iot/vmp/service/IMediaService.java
  31. 9
      src/main/java/com/genersoft/iot/vmp/service/IPlayService.java
  32. 3
      src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java
  33. 4
      src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java
  34. 38
      src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java
  35. 385
      src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
  36. 8
      src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
  37. 65
      src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
  38. 9
      src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
  39. 5
      src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java
  40. 2
      src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java
  41. 1
      src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java
  42. 10
      src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java
  43. 3
      src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
  44. 8
      src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java
  45. 31
      src/main/java/com/genersoft/iot/vmp/utils/SerializeUtils.java
  46. 97
      src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java
  47. 6
      src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java
  48. 25
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
  49. 14
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java
  50. 22
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
  51. 23
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/session/PlayTypeEnum.java
  52. 24
      src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java
  53. 4
      src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java
  54. 7
      src/main/resources/all-application.yml
  55. 2
      src/main/resources/application-dev.yml
  56. BIN
      src/main/resources/wvp.sqlite
  57. 2
      web_src/src/components/service/MediaServer.js

7
pom.xml

@ -46,6 +46,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.build.timestamp.format>MMddHHmm</maven.build.timestamp.format>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<jedis-version>3.1.0</jedis-version>
<!-- 依赖版本 -->
<pagehelper.version>5.2.0</pagehelper.version>
@ -80,6 +81,12 @@
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis-version}</version>
</dependency>
<!-- druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>

204
sql/mysql.sql

@ -1,112 +1,145 @@
-- auto-generated definition
create schema wvp collate utf8_bin;
CREATE DATABASE `wvp` /*!40100 DEFAULT CHARACTER SET utf8mb3 COLLATE utf8mb3_bin */;
use wvp;
create table device
(
deviceId varchar(50) not null
deviceId varchar(50) not null
primary key,
name varchar(255) null,
manufacturer varchar(255) null,
model varchar(255) null,
firmware varchar(255) null,
transport varchar(50) null,
streamMode varchar(50) null,
online varchar(50) null,
registerTime varchar(50) null,
keepaliveTime varchar(50) null,
ip varchar(50) not null,
createTime varchar(50) not null,
updateTime varchar(50) not null,
port int not null,
expires int not null,
hostAddress varchar(50) not null
name varchar(255) null,
manufacturer varchar(255) null,
model varchar(255) null,
firmware varchar(255) null,
transport varchar(50) null,
streamMode varchar(50) null,
online varchar(50) null,
registerTime varchar(50) null,
keepaliveTime varchar(50) null,
ip varchar(50) not null,
createTime varchar(50) not null,
updateTime varchar(50) not null,
port int not null,
expires int not null,
hostAddress varchar(50) not null
);
create table device_channel
(
channelId varchar(50) not null,
channelId varchar(50) not null,
name varchar(255) null,
manufacture varchar(50) null,
model varchar(50) null,
owner varchar(50) null,
civilCode varchar(50) null,
block varchar(50) null,
address varchar(50) null,
parentId varchar(50) null,
manufacture varchar(50) null,
model varchar(50) null,
owner varchar(50) null,
civilCode varchar(50) null,
block varchar(50) null,
address varchar(50) null,
parentId varchar(50) null,
safetyWay int null,
registerWay int null,
certNum varchar(50) null,
certNum varchar(50) null,
certifiable int null,
errCode int null,
endTime varchar(50) null,
secrecy varchar(50) null,
ipAddress varchar(50) null,
endTime varchar(50) null,
secrecy varchar(50) null,
ipAddress varchar(50) null,
port int null,
password varchar(255) null,
PTZType int null,
status int null,
longitude double null,
latitude double null,
streamId varchar(50) null,
deviceId varchar(50) not null,
parental varchar(50) null,
hasAudio bit(1) null,
createTime varchar(50) not null,
updateTime varchar(50) not null,
streamId varchar(50) null,
deviceId varchar(50) not null,
parental varchar(50) null,
hasAudio bit null,
createTime varchar(50) not null,
updateTime varchar(50) not null,
primary key (channelId, deviceId)
);
create table device_mobile_position
(
deviceId varchar(50) not null,
deviceId varchar(50) not null,
deviceName varchar(255) null,
time varchar(50) not null,
time varchar(50) not null,
longitude double not null,
latitude double not null,
altitude double null,
speed double null,
direction double null,
reportSource varchar(50) null,
geodeticSystem varchar(50) null,
cnLng varchar(50) null,
cnLat varchar(50) null,
reportSource varchar(50) null,
geodeticSystem varchar(50) null,
cnLng varchar(50) null,
cnLat varchar(50) null,
primary key (deviceId, time)
);
create table gb_stream
(
app varchar(255) not null,
stream varchar(255) not null,
gbId varchar(50) not null,
name varchar(255) null,
longitude double null,
latitude double null,
streamType varchar(50) null,
status int null,
app varchar(255) not null,
stream varchar(255) not null,
gbId varchar(50) not null,
name varchar(255) null,
longitude double null,
latitude double null,
streamType varchar(50) null,
mediaServerId varchar(50) null,
status int null,
primary key (app, stream, gbId)
);
create table media_server
(
id varchar(255) not null
primary key,
ip varchar(50) not null,
hookIp varchar(50) not null,
sdpIp varchar(50) not null,
streamIp varchar(50) not null,
httpPort int not null,
httpSSlPort int not null,
rtmpPort int not null,
rtmpSSlPort int not null,
rtpProxyPort int not null,
rtspPort int not null,
rtspSSLPort int not null,
autoConfig int not null,
secret varchar(50) not null,
streamNoneReaderDelayMS int not null,
rtpEnable int not null,
rtpPortRange varchar(50) not null,
recordAssistPort int not null,
defaultServer int not null,
createTime varchar(50) not null,
updateTime varchar(50) not null,
constraint media_server_i
unique (ip, httpPort)
);
create table parent_platform
(
id int auto_increment,
id int auto_increment,
enable int null,
name varchar(255) null,
serverGBId varchar(50) not null,
serverGBDomain varchar(50) null,
serverIP varchar(50) null,
serverGBId varchar(50) not null,
serverGBDomain varchar(50) null,
serverIP varchar(50) null,
serverPort int null,
deviceGBId varchar(50) not null,
deviceIp varchar(50) null,
devicePort varchar(50) null,
deviceGBId varchar(50) not null,
deviceIp varchar(50) null,
devicePort varchar(50) null,
username varchar(255) null,
password varchar(50) null,
expires varchar(50) null,
keepTimeout varchar(50) null,
transport varchar(50) null,
characterSet varchar(50) null,
password varchar(50) null,
expires varchar(50) null,
keepTimeout varchar(50) null,
transport varchar(50) null,
characterSet varchar(50) null,
ptz int null,
rtcp int null,
status bit(1) null,
status bit null,
primary key (id, serverGBId)
);
@ -121,7 +154,7 @@ create table platform_gb_channel
create table platform_gb_stream
(
platformId varchar(50) not null,
platformId varchar(50) not null,
app varchar(255) not null,
stream varchar(255) not null,
primary key (platformId, app, stream)
@ -129,7 +162,7 @@ create table platform_gb_stream
create table stream_proxy
(
type varchar(50) not null,
type varchar(50) not null,
app varchar(255) not null,
stream varchar(255) not null,
url varchar(255) null,
@ -137,11 +170,12 @@ create table stream_proxy
dst_url varchar(255) null,
timeout_ms int null,
ffmpeg_cmd_key varchar(255) null,
rtp_type varchar(50) null,
mediaServerId varchar(50) null,
enable_hls bit(1) null,
enable_mp4 bit(1) null,
enable bit(1) not null,
rtp_type varchar(50) null,
mediaServerId varchar(50) null,
enable_hls bit null,
enable_mp4 bit null,
enable bit not null,
createTime varchar(50) not null,
primary key (app, stream)
);
@ -149,11 +183,12 @@ create table stream_push
(
app varchar(255) not null,
stream varchar(255) not null,
totalReaderCount varchar(50) null,
totalReaderCount varchar(50) null,
originType int null,
originTypeStr varchar(50) null,
originTypeStr varchar(50) null,
createStamp int null,
aliveSecond int null,
mediaServerId varchar(50) null,
primary key (app, stream)
);
@ -164,31 +199,6 @@ create table user
username varchar(255) not null,
password varchar(255) not null,
roleId int not null,
create_time varchar(50) not null
create_time varchar(50) not null
);
insert into user (username, password, roleId, create_time) values ('admin', '21232f297a57a5a743894a0e4a801fc3', '0', '2021-04-13 14:14:57');
create table media_server (
id varchar(255)
primary key,
ip varchar(50) NOT NULL,
hookIp varchar(50) NOT NULL,
sdpIp varchar(50) NOT NULL,
streamIp varchar(50) NOT NULL,
httpPort int NOT NULL,
httpSSlPort int NOT NULL,
rtmpPort int NOT NULL,
rtmpSSlPort int NOT NULL,
rtpProxyPort int NOT NULL,
rtspPort int NOT NULL,
rtspSSLPort int NOT NULL,
autoConfig int NOT NULL,
secret varchar(50) NOT NULL,
streamNoneReaderDelayMS int NOT NULL,
rtpEnable int NOT NULL,
rtpPortRange varchar(50) NOT NULL,
recordAssistPort int NOT NULL,
createTime varchar(50) not null,
updateTime varchar(50) not null
);

6
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java

@ -12,6 +12,8 @@ public class VideoManagerConstants {
public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_";
public static final String MEDIA_SERVERS_ONLINE_PREFIX = "VMP_MEDIA_ONLINE_SERVERS";
public static final String MEDIA_STREAM_PREFIX = "VMP_MEDIA_STREAM";
public static final String DEVICE_PREFIX = "VMP_DEVICE_";
@ -45,4 +47,8 @@ public class VideoManagerConstants {
public static final String EVENT_OUTLINE_UNREGISTER = "1";
public static final String EVENT_OUTLINE_TIMEOUT = "2";
public static final String MEDIA_SSRC_USED_PREFIX = "VMP_media_used_ssrc_";
public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_media_transaction_";
}

188
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java

@ -1,13 +1,16 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
@Configuration("mediaConfig")
public class MediaConfig implements IMediaServerItem {
public class MediaConfig{
@Value("${media.id:}")
private String id;
@ -21,6 +24,9 @@ public class MediaConfig implements IMediaServerItem {
@Value("${sip.ip}")
private String sipIp;
@Value("${sip.domain}")
private String sipDomain;
@Value("${media.sdp-ip:${media.ip}}")
private String sdpIp;
@ -66,31 +72,14 @@ public class MediaConfig implements IMediaServerItem {
@Value("${media.record-assist-port:0}")
private Integer recordAssistPort = 0;
private String updateTime;
private String createTime;
private boolean docker = false;
private int count;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getHookIp() {
if (StringUtils.isEmpty(hookIp)){
return sipIp;
@ -100,78 +89,26 @@ public class MediaConfig implements IMediaServerItem {
}
public void setHookIp(String hookIp) {
this.hookIp = hookIp;
}
public String getSipIp() {
return sipIp;
}
public void setSipIp(String sipIp) {
this.sipIp = sipIp;
}
public void setSdpIp(String sdpIp) {
this.sdpIp = sdpIp;
}
public void setStreamIp(String streamIp) {
this.streamIp = streamIp;
}
public int getHttpPort() {
return httpPort;
}
@Override
public void setHttpPort(int httpPort) {
}
public void setHttpPort(Integer httpPort) {
this.httpPort = httpPort;
}
public int getHttpSSlPort() {
return httpSSlPort;
}
@Override
public void setHttpSSlPort(int httpSSlPort) {
}
public void setHttpSSlPort(Integer httpSSlPort) {
this.httpSSlPort = httpSSlPort;
}
public int getRtmpPort() {
return rtmpPort;
}
@Override
public void setRtmpPort(int rtmpPort) {
}
public void setRtmpPort(Integer rtmpPort) {
this.rtmpPort = rtmpPort;
}
public int getRtmpSSlPort() {
return rtmpSSlPort;
}
@Override
public void setRtmpSSlPort(int rtmpSSlPort) {
}
public void setRtmpSSlPort(Integer rtmpSSlPort) {
this.rtmpSSlPort = rtmpSSlPort;
}
public int getRtpProxyPort() {
if (rtpProxyPort == null) {
return 0;
@ -181,104 +118,38 @@ public class MediaConfig implements IMediaServerItem {
}
@Override
public void setRtpProxyPort(int rtpProxyPort) {
}
public void setRtpProxyPort(Integer rtpProxyPort) {
this.rtpProxyPort = rtpProxyPort;
}
public int getRtspPort() {
return rtspPort;
}
@Override
public void setRtspPort(int rtspPort) {
}
public void setRtspPort(Integer rtspPort) {
this.rtspPort = rtspPort;
}
public int getRtspSSLPort() {
return rtspSSLPort;
}
@Override
public void setRtspSSLPort(int rtspSSLPort) {
}
public void setRtspSSLPort(Integer rtspSSLPort) {
this.rtspSSLPort = rtspSSLPort;
}
public boolean isAutoConfig() {
return autoConfig;
}
public void setAutoConfig(boolean autoConfig) {
this.autoConfig = autoConfig;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getStreamNoneReaderDelayMS() {
return streamNoneReaderDelayMS;
}
public void setStreamNoneReaderDelayMS(String streamNoneReaderDelayMS) {
this.streamNoneReaderDelayMS = streamNoneReaderDelayMS;
}
public boolean isRtpEnable() {
return rtpEnable;
}
public void setRtpEnable(boolean rtpEnable) {
this.rtpEnable = rtpEnable;
}
public String getRtpPortRange() {
return rtpPortRange;
}
public void setRtpPortRange(String rtpPortRange) {
this.rtpPortRange = rtpPortRange;
}
public int getRecordAssistPort() {
return recordAssistPort;
}
@Override
public void setRecordAssistPort(int recordAssistPort) {
}
public void setRecordAssistPort(Integer recordAssistPort) {
this.recordAssistPort = recordAssistPort;
}
@Override
public boolean isDocker() {
return docker;
}
@Override
public void setDocker(boolean docker) {
this.docker = docker;
}
public String getSdpIp() {
if (StringUtils.isEmpty(sdpIp)){
return ip;
@ -295,13 +166,11 @@ public class MediaConfig implements IMediaServerItem {
}
}
public MediaServerItem getMediaSerItem(){
MediaServerItem mediaServerItem = new MediaServerItem();
mediaServerItem.setId(id);
mediaServerItem.setIp(ip);
mediaServerItem.setDocker(true);
mediaServerItem.setDefaultServer(true);
mediaServerItem.setHookIp(hookIp);
mediaServerItem.setSdpIp(sdpIp);
mediaServerItem.setStreamIp(streamIp);
@ -318,39 +187,12 @@ public class MediaConfig implements IMediaServerItem {
mediaServerItem.setRtpEnable(rtpEnable);
mediaServerItem.setRtpPortRange(rtpPortRange);
mediaServerItem.setRecordAssistPort(recordAssistPort);
mediaServerItem.setCreateTime(createTime);
mediaServerItem.setUpdateTime(updateTime);
mediaServerItem.setCount(count);
return mediaServerItem;
}
@Override
public String getUpdateTime() {
return updateTime;
}
@Override
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mediaServerItem.setCreateTime(format.format(new Date(System.currentTimeMillis())));
mediaServerItem.setUpdateTime(format.format(new Date(System.currentTimeMillis())));
@Override
public String getCreateTime() {
return createTime;
}
@Override
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
@Override
public int getCount() {
return count;
return mediaServerItem;
}
@Override
public void setCount(int count) {
this.count = count;
}
}

20
src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java

@ -1,6 +1,6 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.http.HttpHost;
@ -49,7 +49,7 @@ public class ProxyServletConfig {
@Override
protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
IMediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
if (mediaInfo != null) {
if (!StringUtils.isEmpty(queryStr)) {
queryStr += "&secret=" + mediaInfo.getSecret();
@ -88,7 +88,7 @@ public class ProxyServletConfig {
@Override
protected String getTargetUri(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
String uri = null;
if (mediaInfo != null) {
@ -106,7 +106,7 @@ public class ProxyServletConfig {
@Override
protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
HttpHost host;
if (mediaInfo != null) {
host = new HttpHost(mediaInfo.getIp(), mediaInfo.getHttpPort());
@ -120,7 +120,7 @@ public class ProxyServletConfig {
/**
* 根据uri获取流媒体信息
*/
IMediaServerItem getMediaInfoByUri(String uri){
MediaServerItem getMediaInfoByUri(String uri){
String[] split = uri.split("/");
String mediaServerId = split[2];
return mediaServerService.getOne(mediaServerId);
@ -132,7 +132,7 @@ public class ProxyServletConfig {
@Override
protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
String url = super.rewriteUrlFromRequest(servletRequest);
if (mediaInfo == null) {
return url;
@ -186,7 +186,7 @@ public class ProxyServletConfig {
@Override
protected String getTargetUri(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
String uri = null;
if (mediaInfo != null) {
@ -204,7 +204,7 @@ public class ProxyServletConfig {
@Override
protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
HttpHost host;
if (mediaInfo != null) {
host = new HttpHost(mediaInfo.getIp(), mediaInfo.getRecordAssistPort());
@ -218,7 +218,7 @@ public class ProxyServletConfig {
/**
* 根据uri获取流媒体信息
*/
IMediaServerItem getMediaInfoByUri(String uri){
MediaServerItem getMediaInfoByUri(String uri){
String[] split = uri.split("/");
String mediaServerId = split[2];
return mediaServerService.getOne(mediaServerId);
@ -230,7 +230,7 @@ public class ProxyServletConfig {
@Override
protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
String requestURI = servletRequest.getRequestURI();
IMediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
String url = super.rewriteUrlFromRequest(servletRequest);
if (mediaInfo == null) {
return url;

37
src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java

@ -1,5 +1,7 @@
package com.genersoft.iot.vmp.conf;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -10,6 +12,8 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.alibaba.fastjson.parser.ParserConfig;
import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* @Description:Redis中间件配置类使用spring-data-redis集成自动从application.yml中加载redis配置
@ -20,6 +24,37 @@ import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer;
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.database}")
private int database;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.poolMaxTotal:1000}")
private int poolMaxTotal;
@Value("${spring.redis.poolMaxIdle:500}")
private int poolMaxIdle;
@Value("${spring.redis.poolMaxWait:5}")
private int poolMaxWait;
@Bean
public JedisPool jedisPool() {
if (StringUtils.isBlank(password)) {
password = null;
}
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(poolMaxIdle);
poolConfig.setMaxTotal(poolMaxTotal);
// 秒转毫秒
poolConfig.setMaxWaitMillis(poolMaxWait * 1000L);
JedisPool jp = new JedisPool(poolConfig, host, port, timeout * 1000, password, database);
return jp;
}
@Bean("redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
@ -34,7 +69,7 @@ public class RedisConfig extends CachingConfigurerSupport {
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
// 使用fastjson时需设置此项,否则会报异常not support type
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
return template;
}

31
src/main/java/com/genersoft/iot/vmp/conf/SipDeviceRunner.java

@ -1,31 +0,0 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 系统启动时控制设备离线
*/
@Component
@Order(value=4)
public class SipDeviceRunner implements CommandLineRunner {
@Autowired
private IVideoManagerStorager storager;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Override
public void run(String... args) throws Exception {
// 设置所有设备离线
storager.outlineForAll();
// 设置所有设备离线
redisCatchStorage.outlineForAll();
}
}

28
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java

@ -12,6 +12,7 @@ import javax.sip.header.CallIdHeader;
import javax.sip.message.Response;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import gov.nist.javax.sip.SipProviderImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -39,7 +40,7 @@ public class SipLayer implements SipListener {
@Autowired
private SipSubscribe sipSubscribe;
private SipStack sipStack;
private SipStackImpl sipStack;
private SipFactory sipFactory;
@ -52,7 +53,7 @@ public class SipLayer implements SipListener {
private ThreadPoolExecutor initSipServer() {
int processThreadNum = Runtime.getRuntime().availableProcessors() * 10;
LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<Runnable>(10000);
LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<>(10000);
processThreadPool = new ThreadPoolExecutor(processThreadNum,processThreadNum,
0L,TimeUnit.MILLISECONDS,processQueue,
new ThreadPoolExecutor.CallerRunsPolicy());
@ -88,17 +89,14 @@ public class SipLayer implements SipListener {
@Bean("tcpSipProvider")
@DependsOn("sipStack")
private SipProvider startTcpListener() {
private SipProviderImpl startTcpListener() {
ListeningPoint tcpListeningPoint = null;
SipProvider tcpSipProvider = null;
SipProviderImpl tcpSipProvider = null;
try {
tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "TCP");
tcpSipProvider = sipStack.createSipProvider(tcpListeningPoint);
tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
tcpSipProvider.addSipListener(this);
logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getSipPort() + "}");
// } catch (TransportNotSupportedException | InvalidArgumentException | TooManyListenersException | ObjectInUseException e) {
// logger.error(String.format("创建SIP服务失败: %s", e.getMessage()));
// }
} catch (TransportNotSupportedException e) {
e.printStackTrace();
} catch (InvalidArgumentException e) {
@ -114,13 +112,14 @@ public class SipLayer implements SipListener {
@Bean("udpSipProvider")
@DependsOn("sipStack")
private SipProvider startUdpListener() {
private SipProviderImpl startUdpListener() {
ListeningPoint udpListeningPoint = null;
SipProvider udpSipProvider = null;
SipProviderImpl udpSipProvider = null;
try {
udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getSipPort(), "UDP");
udpSipProvider = sipStack.createSipProvider(udpListeningPoint);
udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
udpSipProvider.addSipListener(this);
// udpSipProvider.setAutomaticDialogSupportEnabled(false);
} catch (TransportNotSupportedException e) {
e.printStackTrace();
} catch (InvalidArgumentException e) {
@ -141,7 +140,7 @@ public class SipLayer implements SipListener {
*/
@Override
public void processRequest(RequestEvent evt) {
// logger.debug(evt.getRequest().toString());
logger.debug(evt.getRequest().toString());
// 由于jainsip是单线程程序,为提高性能并发处理
processThreadPool.execute(() -> {
if (processorFactory != null) {
@ -153,7 +152,7 @@ public class SipLayer implements SipListener {
@Override
public void processResponse(ResponseEvent evt) {
Response response = evt.getResponse();
// logger.debug(evt.getResponse().toString());
logger.debug(evt.getResponse().toString());
int status = response.getStatusCode();
if (((status >= 200) && (status < 300)) || status == 401) { // Success!
ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);
@ -163,6 +162,7 @@ public class SipLayer implements SipListener {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) {
CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME);
if (callIdHeader != null) {
@ -220,7 +220,6 @@ public class SipLayer implements SipListener {
@Override
public void processIOException(IOExceptionEvent exceptionEvent) {
// TODO Auto-generated method stub
}
/**
@ -236,7 +235,6 @@ public class SipLayer implements SipListener {
@Override
public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
// TODO Auto-generated method stub
}
/**

70
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SsrcTransaction.java

@ -0,0 +1,70 @@
package com.genersoft.iot.vmp.gb28181.bean;
import javax.sip.message.Request;
public class SsrcTransaction {
private String deviceId;
private String channelId;
private String ssrc;
private String streamId;
private byte[] transaction;
private byte[] dialog;
private String mediaServerId;
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public String getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public String getSsrc() {
return ssrc;
}
public void setSsrc(String ssrc) {
this.ssrc = ssrc;
}
public String getStreamId() {
return streamId;
}
public void setStreamId(String streamId) {
this.streamId = streamId;
}
public byte[] getTransaction() {
return transaction;
}
public void setTransaction(byte[] transaction) {
this.transaction = transaction;
}
public byte[] getDialog() {
return dialog;
}
public void setDialog(byte[] dialog) {
this.dialog = dialog;
}
public String getMediaServerId() {
return mediaServerId;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
}

3
src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java

@ -6,7 +6,6 @@ import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@ -77,7 +76,7 @@ public class PlatformNotRegisterEventLister implements ApplicationListener<Platf
}
stream.append(sendRtpItem.getStreamId());
redisCatchStorage.deleteSendRTPServer(event.getPlatformGbID(), sendRtpItem.getChannelId());
IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
Map<String, Object> param = new HashMap<>();
param.put("vhost", "__defaultVhost__");
param.put("app", app.toString());

140
src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java

@ -0,0 +1,140 @@
package com.genersoft.iot.vmp.gb28181.session;
import com.genersoft.iot.vmp.utils.ConfigConst;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Set;
public class SsrcConfig {
/**
* zlm流媒体服务器Id
*/
private String mediaServerId;
private String ssrcPrefix;
/**
* zlm流媒体服务器已用会话句柄
*/
private List<String> isUsed;
/**
* zlm流媒体服务器可用会话句柄
*/
private List<String> notUsed;
public SsrcConfig() {
}
public SsrcConfig(String mediaServerId, Set<String> usedSet, String sipDomain) {
this.mediaServerId = mediaServerId;
this.isUsed = new ArrayList<>();
this.ssrcPrefix = sipDomain.substring(3, 8);
this.notUsed = new ArrayList<>();
for (int i = 1; i < ConfigConst.MAX_STRTEAM_COUNT; i++) {
String ssrc;
if (i < 10) {
ssrc = "000" + i;
} else if (i < 100) {
ssrc = "00" + i;
} else if (i < 1000) {
ssrc = "0" + i;
} else {
ssrc = String.valueOf(i);
}
if (null == usedSet || !usedSet.contains(ssrc)) {
this.notUsed.add(ssrc);
} else {
this.isUsed.add(ssrc);
}
}
}
/**
* 获取视频预览的SSRC值,第一位固定为0
* @return ssrc
*/
public String getPlaySsrc() {
return "0" + getSsrcPrefix() + getSN();
}
/**
* 获取录像回放的SSRC值,第一位固定为1
*
*/
public String getPlayBackSsrc() {
return "1" + getSsrcPrefix() + getSN();
}
/**
* 释放ssrc主要用完的ssrc一定要释放否则会耗尽
* @param ssrc 需要重置的ssrc
*/
public void releaseSsrc(String ssrc) {
if (ssrc == null) {
return;
}
String sn = ssrc.substring(6);
try {
isUsed.remove(sn);
notUsed.add(sn);
}catch (NullPointerException e){
System.out.printf("11111");
}
}
/**
* 获取后四位数SN,随机数
*
*/
private String getSN() {
String sn = null;
int index = 0;
if (notUsed.size() == 0) {
throw new RuntimeException("ssrc已经用完");
} else if (notUsed.size() == 1) {
sn = notUsed.get(0);
} else {
index = new Random().nextInt(notUsed.size() - 1);
sn = notUsed.get(index);
}
notUsed.remove(index);
isUsed.add(sn);
return sn;
}
public String getSsrcPrefix() {
return ssrcPrefix;
}
public String getMediaServerId() {
return mediaServerId;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
public void setSsrcPrefix(String ssrcPrefix) {
this.ssrcPrefix = ssrcPrefix;
}
public List<String> getIsUsed() {
return isUsed;
}
public void setIsUsed(List<String> isUsed) {
this.isUsed = isUsed;
}
public List<String> getNotUsed() {
return notUsed;
}
public void setNotUsed(List<String> notUsed) {
this.notUsed = notUsed;
}
}

101
src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcUtil.java

@ -1,101 +0,0 @@
package com.genersoft.iot.vmp.gb28181.session;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Description:SIP信令中的SSRC工具类SSRC值由10位十进制整数组成的字符串第一位为0代表实况为1则代表回放第二位至第六位由监控域ID的第4位到第8位组成最后4位为不重复的4个整数
* @author: swwheihei
* @date: 2020年5月10日 上午11:57:57
*/
public class SsrcUtil {
private final static Logger logger = LoggerFactory.getLogger(SsrcUtil.class);
private static String ssrcPrefix;
private static List<String> isUsed;
private static List<String> notUsed;
private static void init() {
SipConfig sipConfig = (SipConfig) SpringBeanFactory.getBean("sipConfig");
ssrcPrefix = sipConfig.getSipDomain().substring(3, 8);
isUsed = new ArrayList<String>();
notUsed = new ArrayList<String>();
for (int i = 1; i < 10000; i++) {
if (i < 10) {
notUsed.add("000" + i);
} else if (i < 100) {
notUsed.add("00" + i);
} else if (i < 1000) {
notUsed.add("0" + i);
} else {
notUsed.add(String.valueOf(i));
}
}
}
/**
* 获取视频预览的SSRC值,第一位固定为0
*
*/
public static String getPlaySsrc() {
return "0" + getSsrcPrefix() + getSN();
}
/**
* 获取录像回放的SSRC值,第一位固定为1
*
*/
public static String getPlayBackSsrc() {
return "1" + getSsrcPrefix() + getSN();
}
/**
* 释放ssrc主要用完的ssrc一定要释放否则会耗尽
*
*/
public static void releaseSsrc(String ssrc) {
if (ssrc == null) {
logger.error("要释放ssrc为null");
return;
}
String sn = ssrc.substring(6);
isUsed.remove(sn);
notUsed.add(sn);
}
/**
* 获取后四位数SN,随机数
*
*/
private static String getSN() {
String sn = null;
int index = 0;
if (notUsed.size() == 0) {
throw new RuntimeException("ssrc已经用完");
} else if (notUsed.size() == 1) {
sn = notUsed.get(0);
} else {
index = new Random().nextInt(notUsed.size() - 1);
sn = notUsed.get(index);
}
notUsed.remove(index);
isUsed.add(sn);
return sn;
}
private static String getSsrcPrefix() {
if (ssrcPrefix == null) {
init();
}
return ssrcPrefix;
}
}

111
src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java

@ -1,9 +1,23 @@
package com.genersoft.iot.vmp.gb28181.session;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.message.Request;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.utils.SerializeUtils;
import com.genersoft.iot.vmp.utils.redis.JedisUtil;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import gov.nist.javax.sip.stack.SIPDialog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
/**
@ -14,50 +28,85 @@ import org.springframework.stereotype.Component;
@Component
public class VideoStreamSessionManager {
private ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, String> ssrcMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, String> streamIdMap = new ConcurrentHashMap<>();
@Autowired
private RedisUtil redisUtil;
public String createPlaySsrc(){
return SsrcUtil.getPlaySsrc();
}
public String createPlayBackSsrc(){
return SsrcUtil.getPlayBackSsrc();
public void put(String deviceId, String channelId ,String ssrc, String streamId, String mediaServerId, ClientTransaction transaction){
SsrcTransaction ssrcTransaction = new SsrcTransaction();
ssrcTransaction.setDeviceId(deviceId);
ssrcTransaction.setChannelId(channelId);
ssrcTransaction.setStreamId(streamId);
byte[] transactionByteArray = SerializeUtils.serialize(transaction);
ssrcTransaction.setTransaction(transactionByteArray);
ssrcTransaction.setSsrc(ssrc);
ssrcTransaction.setMediaServerId(mediaServerId);
redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + deviceId + "_" + channelId, ssrcTransaction);
}
public void put(String deviceId, String channelId ,String ssrc, String streamId, ClientTransaction transaction){
sessionMap.put(deviceId + "_" + channelId, transaction);
ssrcMap.put(deviceId + "_" + channelId, ssrc);
streamIdMap.put(deviceId + "_" + channelId, streamId);
public void put(String deviceId, String channelId , Dialog dialog){
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
if (ssrcTransaction != null) {
byte[] dialogByteArray = SerializeUtils.serialize(dialog);
ssrcTransaction.setDialog(dialogByteArray);
}
redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + deviceId + "_" + channelId, ssrcTransaction);
}
public ClientTransaction getTransaction(String deviceId, String channelId){
return sessionMap.get(deviceId + "_" + channelId);
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
if (ssrcTransaction == null) return null;
byte[] transactionByteArray = ssrcTransaction.getTransaction();
ClientTransaction clientTransaction = (ClientTransaction)SerializeUtils.deSerialize(transactionByteArray);
return clientTransaction;
}
public String getStreamId(String deviceId, String channelId){
return streamIdMap.get(deviceId + "_" + channelId);
public SIPDialog getDialog(String deviceId, String channelId){
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
if (ssrcTransaction == null) return null;
byte[] dialogByteArray = ssrcTransaction.getDialog();
if (dialogByteArray == null) return null;
SIPDialog dialog = (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
return dialog;
}
public void remove(String deviceId, String channelId) {
sessionMap.remove(deviceId + "_" + channelId);
if (ssrcMap.get(deviceId + "_" + channelId) != null) {
SsrcUtil.releaseSsrc(ssrcMap.get(deviceId + "_" + channelId));
}
ssrcMap.remove(deviceId + "_" + channelId);
streamIdMap.remove(deviceId + "_" + channelId);
public SsrcTransaction getSsrcTransaction(String deviceId, String channelId){
SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + deviceId + "_" + channelId);
return ssrcTransaction;
}
public ConcurrentHashMap<String, ClientTransaction> getSessionMap() {
return sessionMap;
public String getStreamId(String deviceId, String channelId){
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
if (ssrcTransaction == null) return null;
return ssrcTransaction.getStreamId();
}
public String getMediaServerId(String deviceId, String channelId){
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
if (ssrcTransaction == null) return null;
return ssrcTransaction.getMediaServerId();
}
public ConcurrentHashMap<String, String> getSsrcMap() {
return ssrcMap;
public String getSSRC(String deviceId, String channelId){
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
if (ssrcTransaction == null) return null;
return ssrcTransaction.getSsrc();
}
public void remove(String deviceId, String channelId) {
SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
if (ssrcTransaction == null) return;
redisUtil.del(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + deviceId + "_" + channelId);
}
public ConcurrentHashMap<String, String> getStreamIdMap() {
return streamIdMap;
public List<SsrcTransaction> getAllSsrc() {
List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX));
List<SsrcTransaction> result= new ArrayList<>();
for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
String key = (String)ssrcTransactionKeys.get(i);
SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(key);
result.add(ssrcTransaction);
}
return result;
}
}

2
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorFactory.java

@ -7,6 +7,7 @@ import javax.sip.header.CSeqHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.service.IMediaServerService;
@ -108,7 +109,6 @@ public class SIPProcessorFactory {
@Autowired
private IMediaServerService mediaServerService;
// 注:这里使用注解会导致循环依赖注入,暂用springBean
private SipProvider tcpSipProvider;

10
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java

@ -3,8 +3,8 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
/**
* @Description:设备能力接口用于定义设备的控制查询能力
@ -92,7 +92,7 @@ public interface ISIPCommander {
* @param device 视频设备
* @param channelId 预览通道
*/
void playStreamCmd(IMediaServerItem mediaServerItem, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
/**
* 请求回放视频流
@ -102,7 +102,7 @@ public interface ISIPCommander {
* @param startTime 开始时间,格式要求yyyy-MM-dd HH:mm:ss
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss
*/
void playbackStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
/**
* 请求历史媒体下载
@ -113,12 +113,10 @@ public interface ISIPCommander {
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss
* @param downloadSpeed 下载倍速参数
*/
void downloadStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
/**
* 视频流停止
*
* @param ssrc ssrc
*/
void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent);
void streamByeCmd(String deviceId, String channelId);

211
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java

@ -1,6 +1,8 @@
package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.util.HashSet;
import javax.sip.*;
import javax.sip.address.SipURI;
@ -8,18 +10,21 @@ import javax.sip.header.CallIdHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.conf.UserSetup;
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
import com.genersoft.iot.vmp.media.zlm.*;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.SipStackImpl;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.stack.SIPDialog;
import gov.nist.javax.sip.stack.SIPTransaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -35,7 +40,6 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
import org.springframework.util.StringUtils;
/**
@ -55,12 +59,12 @@ public class SIPCommander implements ISIPCommander {
@Lazy
@Autowired
@Qualifier(value="tcpSipProvider")
private SipProvider tcpSipProvider;
private SipProviderImpl tcpSipProvider;
@Lazy
@Autowired
@Qualifier(value="udpSipProvider")
private SipProvider udpSipProvider;
private SipProviderImpl udpSipProvider;
@Autowired
private SIPRequestHeaderProvider headerProvider;
@ -74,9 +78,6 @@ public class SIPCommander implements ISIPCommander {
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private ZLMRTPServerFactory zlmrtpServerFactory;
@Autowired
private UserSetup userSetup;
@ -86,6 +87,11 @@ public class SIPCommander implements ISIPCommander {
@Autowired
private SipSubscribe sipSubscribe;
@Autowired
private IMediaServerService mediaServerService;
private SIPDialog dialog;
public SipConfig getSipConfig() {
return sipConfig;
}
@ -334,26 +340,13 @@ public class SIPCommander implements ISIPCommander {
* @param errorEvent sip错误订阅
*/
@Override
public void playStreamCmd(IMediaServerItem mediaServerItem, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) {
String streamId = null;
public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) {
String streamId = ssrcInfo.getStreamId();
try {
if (device == null) return;
String streamMode = device.getStreamMode().toUpperCase();
String ssrc = streamSession.createPlaySsrc();
if (mediaServerItem.isRtpEnable()) {
streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
}else {
streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
}
Integer mediaPort = null;
// 使用动态udp端口
if (mediaServerItem.isRtpEnable()) {
mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
}else {
mediaPort = mediaServerItem.getRtpProxyPort();
}
logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), mediaPort);
logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
// 添加订阅
JSONObject subscribeKey = new JSONObject();
subscribeKey.put("app", "rtp");
@ -361,7 +354,7 @@ public class SIPCommander implements ISIPCommander {
subscribeKey.put("regist", true);
subscribeKey.put("mediaServerId", mediaServerItem.getId());
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
(IMediaServerItem mediaServerItemInUse, JSONObject json)->{
(MediaServerItem mediaServerItemInUse, JSONObject json)->{
if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
event.response(mediaServerItemInUse, json);
subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
@ -369,7 +362,6 @@ public class SIPCommander implements ISIPCommander {
//
StringBuffer content = new StringBuffer(200);
content.append("v=0\r\n");
// content.append("o=" + sipConfig.getSipId() + " 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n");
content.append("o="+"00000"+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
content.append("s=Play\r\n");
content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n");
@ -377,11 +369,11 @@ public class SIPCommander implements ISIPCommander {
if (userSetup.isSeniorSdp()) {
if("TCP-PASSIVE".equals(streamMode)) {
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
}else if ("TCP-ACTIVE".equals(streamMode)) {
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
}else if("UDP".equals(streamMode)) {
content.append("m=video "+ mediaPort +" RTP/AVP 96 126 125 99 34 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
}
content.append("a=recvonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
@ -402,11 +394,11 @@ public class SIPCommander implements ISIPCommander {
}
}else {
if("TCP-PASSIVE".equals(streamMode)) {
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
}else if ("TCP-ACTIVE".equals(streamMode)) {
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
}else if("UDP".equals(streamMode)) {
content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
}
content.append("a=recvonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
@ -421,20 +413,25 @@ public class SIPCommander implements ISIPCommander {
}
}
content.append("y="+ssrc+"\r\n");//ssrc
content.append("y="+ssrcInfo.getSsrc()+"\r\n");//ssrc
String tm = Long.toString(System.currentTimeMillis());
CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
: udpSipProvider.getNewCallId();
Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrc, callIdHeader);
Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrcInfo.getSsrc(), callIdHeader);
ClientTransaction transaction = transmitRequest(device, request, (e -> {
String finalStreamId = streamId;
transmitRequest(device, request, (e -> {
streamSession.remove(device.getDeviceId(), channelId);
mediaServerService.releaseSsrc(mediaServerItem, ssrcInfo.getSsrc());
errorEvent.response(e);
}));
streamSession.put(device.getDeviceId(), channelId ,ssrc,streamId, transaction);
}), e ->{
streamSession.put(device.getDeviceId(), channelId ,ssrcInfo.getSsrc(), finalStreamId, mediaServerItem.getId(),e.getClientTransaction());
streamSession.put(device.getDeviceId(), channelId , e.getDialog());
});
} catch ( SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
@ -450,30 +447,21 @@ public class SIPCommander implements ISIPCommander {
* @param endTime 结束时间,格式要求yyyy-MM-dd HH:mm:ss
*/
@Override
public void playbackStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event
public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event
, SipSubscribe.Event errorEvent) {
try {
String ssrc = streamSession.createPlayBackSsrc();
String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
Integer mediaPort = null;
// 使用动态udp端口
if (mediaServerItem.isRtpEnable()) {
mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
}else {
mediaPort = mediaServerItem.getRtpProxyPort();
}
logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), mediaPort);
logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStreamId(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
// 添加订阅
JSONObject subscribeKey = new JSONObject();
subscribeKey.put("app", "rtp");
subscribeKey.put("stream", streamId);
subscribeKey.put("stream", ssrcInfo.getStreamId());
subscribeKey.put("regist", true);
subscribeKey.put("mediaServerId", mediaServerItem.getId());
logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString());
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
(IMediaServerItem mediaServerItemInUse, JSONObject json)->{
(MediaServerItem mediaServerItemInUse, JSONObject json)->{
if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
event.response(mediaServerItemInUse, json);
subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
@ -494,11 +482,11 @@ public class SIPCommander implements ISIPCommander {
if (userSetup.isSeniorSdp()) {
if("TCP-PASSIVE".equals(streamMode)) {
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
}else if ("TCP-ACTIVE".equals(streamMode)) {
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
}else if("UDP".equals(streamMode)) {
content.append("m=video "+ mediaPort +" RTP/AVP 96 126 125 99 34 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
}
content.append("a=recvonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
@ -519,11 +507,11 @@ public class SIPCommander implements ISIPCommander {
}
}else {
if("TCP-PASSIVE".equals(streamMode)) {
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
}else if ("TCP-ACTIVE".equals(streamMode)) {
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
}else if("UDP".equals(streamMode)) {
content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
}
content.append("a=recvonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
@ -538,7 +526,7 @@ public class SIPCommander implements ISIPCommander {
}
}
content.append("y="+ssrc+"\r\n");//ssrc
content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
String tm = Long.toString(System.currentTimeMillis());
@ -547,9 +535,11 @@ public class SIPCommander implements ISIPCommander {
Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader);
ClientTransaction transaction = transmitRequest(device, request, errorEvent);
streamSession.put(device.getDeviceId(), channelId, ssrc, streamId, transaction);
transmitRequest(device, request, errorEvent, okEvent -> {
Dialog dialog = okEvent.getClientTransaction().getDialog();
streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), okEvent.getClientTransaction());
streamSession.put(device.getDeviceId(), channelId, dialog);
});
} catch ( SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
}
@ -565,30 +555,20 @@ public class SIPCommander implements ISIPCommander {
* @param downloadSpeed 下载倍速参数
*/
@Override
public void downloadStreamCmd(IMediaServerItem mediaServerItem,Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event
public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event
, SipSubscribe.Event errorEvent) {
try {
String ssrc = streamSession.createPlayBackSsrc();
String streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
Integer mediaPort = null;
// 使用动态udp端口
if (mediaServerItem.isRtpEnable()) {
mediaPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
}else {
mediaPort = mediaServerItem.getRtpProxyPort();
}
logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), mediaPort);
logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStreamId(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
// 添加订阅
JSONObject subscribeKey = new JSONObject();
subscribeKey.put("app", "rtp");
subscribeKey.put("stream", streamId);
subscribeKey.put("stream", ssrcInfo.getStreamId());
subscribeKey.put("regist", true);
subscribeKey.put("mediaServerId", mediaServerItem.getId());
logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString());
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey,
(IMediaServerItem mediaServerItemInUse, JSONObject json)->{
(MediaServerItem mediaServerItemInUse, JSONObject json)->{
if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return;
event.response(mediaServerItemInUse, json);
subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey);
@ -609,11 +589,11 @@ public class SIPCommander implements ISIPCommander {
if (userSetup.isSeniorSdp()) {
if("TCP-PASSIVE".equals(streamMode)) {
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
}else if ("TCP-ACTIVE".equals(streamMode)) {
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
}else if("UDP".equals(streamMode)) {
content.append("m=video "+ mediaPort +" RTP/AVP 96 126 125 99 34 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n");
}
content.append("a=recvonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
@ -634,11 +614,11 @@ public class SIPCommander implements ISIPCommander {
}
}else {
if("TCP-PASSIVE".equals(streamMode)) {
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
}else if ("TCP-ACTIVE".equals(streamMode)) {
content.append("m=video "+ mediaPort +" TCP/RTP/AVP 96 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n");
}else if("UDP".equals(streamMode)) {
content.append("m=video "+ mediaPort +" RTP/AVP 96 98 97\r\n");
content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n");
}
content.append("a=recvonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
@ -654,7 +634,7 @@ public class SIPCommander implements ISIPCommander {
}
content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
content.append("y="+ssrc+"\r\n");//ssrc
content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
String tm = Long.toString(System.currentTimeMillis());
@ -664,7 +644,7 @@ public class SIPCommander implements ISIPCommander {
Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader);
ClientTransaction transaction = transmitRequest(device, request, errorEvent);
streamSession.put(device.getDeviceId(), channelId, ssrc, streamId, transaction);
streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), transaction);
} catch ( SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
@ -684,53 +664,35 @@ public class SIPCommander implements ISIPCommander {
*/
@Override
public void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent) {
StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
try {
ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId);
// 服务重启后, 无法直接发送bye, 通过手动构建发送
// if (transaction == null) {
//
// if (streamInfo != null) {
// MediaServerItem mediaServerItem = redisCatchStorage.getMediaInfo(streamInfo.getMediaServerId());
// JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaServerItem,streamInfo.getApp(), streamInfo.getStreamId());
// if (mediaList != null) { // 仍在推流才发送
// if (mediaList.getInteger("code") == 0) {
// JSONArray data = mediaList.getJSONArray("data");
// if (data != null && data.size() > 0) {
// Device device = storager.queryVideoDevice(deviceId);
// if (device != null) {
// StreamInfo.TransactionInfo transactionInfo = streamInfo.getTransactionInfo();
// try {
// Request byteRequest = headerProvider.createByteRequest(device, channelId,
// transactionInfo.branch,
// transactionInfo.localTag,
// transactionInfo.remoteTag,
// transactionInfo.callId);
// transmitRequest(device, byteRequest);
// } catch (InvalidArgumentException e) {
// e.printStackTrace();
// }
// }
// }
// }
// }
// redisCatchStorage.stopPlay(streamInfo);
// }
//
// if (okEvent != null) {
// okEvent.response(null);
// }
// return;
// }
if (transaction == null) {
logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId);
return;
}
Dialog dialog = transaction.getDialog();
SIPDialog dialog = streamSession.getDialog(deviceId, channelId);
if (dialog == null) {
logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", deviceId, channelId);
return;
}
SipStack sipStack = udpSipProvider.getSipStack();
SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog);
if (dialog != sipDialog) {
dialog = sipDialog;
}else {
dialog.setSipProvider(udpSipProvider);
try {
Field sipStackField = SIPDialog.class.getDeclaredField("sipStack");
sipStackField.setAccessible(true);
sipStackField.set(dialog, sipStack);
Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners");
eventListenersField.setAccessible(true);
eventListenersField.set(dialog, new HashSet<>());
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
Request byeRequest = dialog.createRequest(Request.BYE);
SipURI byeURI = (SipURI) byeRequest.getRequestURI();
SIPRequest request = (SIPRequest)transaction.getRequest();
@ -752,7 +714,12 @@ public class SIPCommander implements ISIPCommander {
dialog.sendRequest(clientTransaction);
streamSession.remove(deviceId, channelId);
SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId);
if (ssrcTransaction != null) {
MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId());
mediaServerService.releaseSsrc(mediaServerItem, ssrcTransaction.getSsrc());
streamSession.remove(deviceId, channelId);
}
} catch (SipException | ParseException e) {
e.printStackTrace();
}

3
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java

@ -13,7 +13,6 @@ import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@ -81,7 +80,7 @@ public class AckRequestProcessor extends SIPRequestAbstractProcessor {
while (!rtpPushed) {
try {
if (System.currentTimeMillis() - startTime < 30 * 1000) {
IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStreamId())) {
rtpPushed = true;
logger.info("已获取设备推流[{}/{}],开始向上级推流[{}:{}]",

3
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java

@ -15,7 +15,6 @@ import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@ -65,7 +64,7 @@ public class ByeRequestProcessor extends SIPRequestAbstractProcessor {
param.put("stream",streamId);
param.put("ssrc",sendRtpItem.getSsrc());
logger.info("停止向上级推流:" + streamId);
IMediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
redisCatchStorage.deleteSendRTPServer(platformGbId, channelId);
if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) {

5
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java

@ -11,14 +11,11 @@ import javax.sip.header.*;
import javax.sip.message.Request;
import javax.sip.message.Response;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.gb28181.bean.*;
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.request.SIPRequestAbstractProcessor;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@ -97,7 +94,7 @@ public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
// 查询平台下是否有该通道
DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
IMediaServerItem mediaServerItem = null;
MediaServerItem mediaServerItem = null;
// 不是通道可能是直播流
if (channel != null && gbStream == null ) {
if (channel.getStatus() == 0) {

14
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java

@ -3,14 +3,18 @@ package com.genersoft.iot.vmp.gb28181.transmit.response.impl;
import java.text.ParseException;
import javax.sip.*;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.CSeqHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import gov.nist.javax.sip.ResponseEventExt;
import gov.nist.javax.sip.stack.SIPDialog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.genersoft.iot.vmp.conf.SipConfig;
@ -28,6 +32,9 @@ public class InviteResponseProcessor implements ISIPResponseProcessor {
private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
@Autowired
private VideoStreamSessionManager streamSession;
/**
* 处理invite响应
*
@ -46,7 +53,7 @@ public class InviteResponseProcessor implements ISIPResponseProcessor {
// 下发ack
if (statusCode == Response.OK) {
ResponseEventExt event = (ResponseEventExt)evt;
Dialog dialog = evt.getDialog();
SIPDialog dialog = (SIPDialog)evt.getDialog();
CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
Request reqAck = dialog.createAck(cseq.getSeqNumber());
SipURI requestURI = (SipURI) reqAck.getRequestURI();
@ -54,7 +61,12 @@ public class InviteResponseProcessor implements ISIPResponseProcessor {
requestURI.setPort(event.getRemotePort());
reqAck.setRequestURI(requestURI);
logger.info("向 " + event.getRemoteIpAddress() + ":" + event.getRemotePort() + "回复ack");
SipURI sipURI = (SipURI)dialog.getRemoteParty().getURI();
String deviceId = requestURI.getUser();
String channelId = sipURI.getUser();
dialog.sendAck(reqAck);
}
} catch (InvalidArgumentException | SipException e) {
e.printStackTrace();

24
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java

@ -3,15 +3,14 @@ package com.genersoft.iot.vmp.media.zlm;
import java.util.List;
import java.util.UUID;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.conf.UserSetup;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.service.IPlayService;
@ -42,7 +41,6 @@ public class ZLMHttpHookListener {
private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class);
@Autowired
private SIPCommander cmder;
@ -125,7 +123,7 @@ public class ZLMHttpHookListener {
String mediaServerId = json.getString("mediaServerId");
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_play, json);
if (subscribe != null ) {
IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (mediaInfo != null) {
subscribe.response(mediaInfo, json);
}
@ -150,7 +148,7 @@ public class ZLMHttpHookListener {
String mediaServerId = json.getString("mediaServerId");
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
if (subscribe != null) {
IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (mediaInfo != null) {
subscribe.response(mediaInfo, json);
}
@ -237,7 +235,7 @@ public class ZLMHttpHookListener {
String mediaServerId = json.getString("mediaServerId");
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_shell_login, json);
if (subscribe != null ) {
IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (mediaInfo != null) {
subscribe.response(mediaInfo, json);
}
@ -264,7 +262,7 @@ public class ZLMHttpHookListener {
String mediaServerId = json.getString("mediaServerId");
ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, json);
if (subscribe != null ) {
IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (mediaInfo != null) {
subscribe.response(mediaInfo, json);
}
@ -297,7 +295,7 @@ public class ZLMHttpHookListener {
}
}else {
if (!"rtp".equals(app) ){
IMediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
if (regist) {
zlmMediaListManager.addMedia(mediaServerItem, app, streamId);
}else {
@ -369,7 +367,7 @@ public class ZLMHttpHookListener {
logger.debug("ZLM HOOK on_stream_not_found API调用,参数:" + json.toString());
}
String mediaServerId = json.getString("mediaServerId");
IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (userSetup.isAutoApplyPlay() && mediaInfo != null) {
String app = json.getString("app");
String streamId = json.getString("stream");
@ -381,7 +379,13 @@ public class ZLMHttpHookListener {
Device device = storager.queryVideoDevice(deviceId);
if (device != null) {
UUID uuid = UUID.randomUUID();
cmder.playStreamCmd(mediaInfo, device, channelId, (IMediaServerItem mediaServerItemInuse, JSONObject response) -> {
SSRCInfo ssrcInfo;
String streamId2 = null;
if (mediaInfo.isRtpEnable()) {
streamId2 = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
}
ssrcInfo = mediaServerService.openRTPServer(mediaInfo, streamId2);
cmder.playStreamCmd(mediaInfo, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString());
playService.onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid.toString());
}, null);

8
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java

@ -1,7 +1,6 @@
package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import org.springframework.stereotype.Component;
@ -32,7 +31,7 @@ public class ZLMHttpHookSubscribe {
}
public interface Event{
void response(IMediaServerItem mediaServerItem, JSONObject response);
void response(MediaServerItem mediaServerItem, JSONObject response);
}
private Map<HookType, Map<JSONObject, ZLMHttpHookSubscribe.Event>> allSubscribes = new ConcurrentHashMap<>();
@ -58,6 +57,9 @@ public class ZLMHttpHookSubscribe {
if (result == null) {
result = key.getString(s).equals(hookResponse.getString(s));
}else {
if (key.getString(s) == null) {
continue;
}
result = result && key.getString(s).equals(hookResponse.getString(s));
}
@ -83,9 +85,9 @@ public class ZLMHttpHookSubscribe {
if (result == null) {
result = key.getString(s).equals(hookResponse.getString(s));
}else {
if (key.getString(s) == null) continue;
result = result && key.getString(s).equals(hookResponse.getString(s));
}
}
if (null != result && result){
iterator.remove();

9
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java

@ -1,12 +1,9 @@
package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
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.StreamPushItem;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.service.IStreamPushService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
@ -76,7 +73,7 @@ public class ZLMMediaListManager {
jsonObject.put("stream", streamPushItem.getStream());
jsonObject.put("mediaServerId", mediaServerItem.getId());
subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_play,jsonObject,
(IMediaServerItem mediaServerItemInuse, JSONObject response)->{
(MediaServerItem mediaServerItemInuse, JSONObject response)->{
updateMedia(mediaServerItem, response.getString("app"), response.getString("stream"));
}
);
@ -86,13 +83,13 @@ public class ZLMMediaListManager {
}
public void addMedia(IMediaServerItem mediaServerItem, String app, String streamId) {
public void addMedia(MediaServerItem mediaServerItem, String app, String streamId) {
//使用异步更新推流
updateMedia(mediaServerItem, app, streamId);
}
public void updateMedia(IMediaServerItem mediaServerItem, String app, String streamId) {
public void updateMedia(MediaServerItem mediaServerItem, String app, String streamId) {
//使用异步更新推流
zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId, "rtmp", json->{

45
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java

@ -2,14 +2,11 @@ package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.*;
@ -27,7 +24,7 @@ public class ZLMRESTfulUtils {
void run(JSONObject response);
}
public JSONObject sendPost(IMediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
OkHttpClient client = new OkHttpClient();
String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
JSONObject responseJSON = null;
@ -93,7 +90,7 @@ public class ZLMRESTfulUtils {
}
public void sendPostForImg(IMediaServerItem mediaServerItem, String api, Map<String, Object> param, String targetPath, String fileName) {
public void sendPostForImg(MediaServerItem mediaServerItem, String api, Map<String, Object> param, String targetPath, String fileName) {
OkHttpClient client = new OkHttpClient();
String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
JSONObject responseJSON = null;
@ -139,7 +136,7 @@ public class ZLMRESTfulUtils {
}
public JSONObject getMediaList(IMediaServerItem mediaServerItem,String app, String stream, String schema, RequestCallback callback){
public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream, String schema, RequestCallback callback){
Map<String, Object> param = new HashMap<>();
if (app != null) param.put("app",app);
if (stream != null) param.put("stream",stream);
@ -148,15 +145,15 @@ public class ZLMRESTfulUtils {
return sendPost(mediaServerItem, "getMediaList",param, callback);
}
public JSONObject getMediaList(IMediaServerItem mediaServerItem,String app, String stream){
public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream){
return getMediaList(mediaServerItem, app, stream,null, null);
}
public JSONObject getMediaList(IMediaServerItem mediaServerItem,RequestCallback callback){
public JSONObject getMediaList(MediaServerItem mediaServerItem, RequestCallback callback){
return sendPost(mediaServerItem, "getMediaList",null, callback);
}
public JSONObject getMediaInfo(IMediaServerItem mediaServerItem,String app, String schema, String stream){
public JSONObject getMediaInfo(MediaServerItem mediaServerItem, String app, String schema, String stream){
Map<String, Object> param = new HashMap<>();
param.put("app",app);
param.put("schema",schema);
@ -165,13 +162,13 @@ public class ZLMRESTfulUtils {
return sendPost(mediaServerItem, "getMediaInfo",param, null);
}
public JSONObject getRtpInfo(IMediaServerItem mediaServerItem,String stream_id){
public JSONObject getRtpInfo(MediaServerItem mediaServerItem, String stream_id){
Map<String, Object> param = new HashMap<>();
param.put("stream_id",stream_id);
return sendPost(mediaServerItem, "getRtpInfo",param, null);
}
public JSONObject addFFmpegSource(IMediaServerItem mediaServerItem,String src_url, String dst_url, String timeout_ms,
public JSONObject addFFmpegSource(MediaServerItem mediaServerItem, String src_url, String dst_url, String timeout_ms,
boolean enable_hls, boolean enable_mp4, String ffmpeg_cmd_key){
logger.info(src_url);
logger.info(dst_url);
@ -185,41 +182,41 @@ public class ZLMRESTfulUtils {
return sendPost(mediaServerItem, "addFFmpegSource",param, null);
}
public JSONObject delFFmpegSource(IMediaServerItem mediaServerItem,String key){
public JSONObject delFFmpegSource(MediaServerItem mediaServerItem, String key){
Map<String, Object> param = new HashMap<>();
param.put("key", key);
return sendPost(mediaServerItem, "delFFmpegSource",param, null);
}
public JSONObject getMediaServerConfig(IMediaServerItem mediaServerItem){
public JSONObject getMediaServerConfig(MediaServerItem mediaServerItem){
return sendPost(mediaServerItem, "getServerConfig",null, null);
}
public JSONObject setServerConfig(IMediaServerItem mediaServerItem, Map<String, Object> param){
public JSONObject setServerConfig(MediaServerItem mediaServerItem, Map<String, Object> param){
return sendPost(mediaServerItem,"setServerConfig",param, null);
}
public JSONObject openRtpServer(IMediaServerItem mediaServerItem,Map<String, Object> param){
public JSONObject openRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param){
return sendPost(mediaServerItem, "openRtpServer",param, null);
}
public JSONObject closeRtpServer(IMediaServerItem mediaServerItem,Map<String, Object> param) {
public JSONObject closeRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param) {
return sendPost(mediaServerItem, "closeRtpServer",param, null);
}
public JSONObject listRtpServer(IMediaServerItem mediaServerItem) {
public JSONObject listRtpServer(MediaServerItem mediaServerItem) {
return sendPost(mediaServerItem, "listRtpServer",null, null);
}
public JSONObject startSendRtp(IMediaServerItem mediaServerItem,Map<String, Object> param) {
public JSONObject startSendRtp(MediaServerItem mediaServerItem, Map<String, Object> param) {
return sendPost(mediaServerItem, "startSendRtp",param, null);
}
public JSONObject stopSendRtp(IMediaServerItem mediaServerItem,Map<String, Object> param) {
public JSONObject stopSendRtp(MediaServerItem mediaServerItem, Map<String, Object> param) {
return sendPost(mediaServerItem, "stopSendRtp",param, null);
}
public JSONObject addStreamProxy(IMediaServerItem mediaServerItem,String app, String stream, String url, boolean enable_hls, boolean enable_mp4, String rtp_type) {
public JSONObject addStreamProxy(MediaServerItem mediaServerItem, String app, String stream, String url, boolean enable_hls, boolean enable_mp4, String rtp_type) {
Map<String, Object> param = new HashMap<>();
param.put("vhost", "__defaultVhost__");
param.put("app", app);
@ -231,7 +228,7 @@ public class ZLMRESTfulUtils {
return sendPost(mediaServerItem, "addStreamProxy",param, null);
}
public JSONObject closeStreams(IMediaServerItem mediaServerItem,String app, String stream) {
public JSONObject closeStreams(MediaServerItem mediaServerItem, String app, String stream) {
Map<String, Object> param = new HashMap<>();
param.put("vhost", "__defaultVhost__");
param.put("app", app);
@ -240,17 +237,17 @@ public class ZLMRESTfulUtils {
return sendPost(mediaServerItem, "close_streams",param, null);
}
public JSONObject getAllSession(IMediaServerItem mediaServerItem) {
public JSONObject getAllSession(MediaServerItem mediaServerItem) {
return sendPost(mediaServerItem, "getAllSession",null, null);
}
public void kickSessions(IMediaServerItem mediaServerItem, String localPortSStr) {
public void kickSessions(MediaServerItem mediaServerItem, String localPortSStr) {
Map<String, Object> param = new HashMap<>();
param.put("local_port", localPortSStr);
sendPost(mediaServerItem, "kick_sessions",param, null);
}
public void getSnap(IMediaServerItem mediaServerItem, String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
public void getSnap(MediaServerItem mediaServerItem, String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
Map<String, Object> param = new HashMap<>();
param.put("url", flvUrl);
param.put("timeout_sec", timeout_sec);

80
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java

@ -2,10 +2,7 @@ package com.genersoft.iot.vmp.media.zlm;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.gb28181.session.SsrcUtil;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -20,29 +17,20 @@ public class ZLMRTPServerFactory {
private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory");
@Autowired
private MediaConfig mediaConfig;
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
private int[] portRangeArray = new int[2];
private int currentPort = 0;
private Map<String, Integer> currentStreams = null;
public int createRTPServer(IMediaServerItem mediaServerItem, String streamId) {
if (currentStreams == null) {
currentStreams = new HashMap<>();
JSONObject jsonObject = zlmresTfulUtils.listRtpServer(mediaServerItem);
if (jsonObject != null) {
JSONArray data = jsonObject.getJSONArray("data");
if (data != null) {
for (int i = 0; i < data.size(); i++) {
JSONObject dataItem = data.getJSONObject(i);
currentStreams.put(dataItem.getString("stream_id"), dataItem.getInteger("port"));
}
public int createRTPServer(MediaServerItem mediaServerItem, String streamId) {
Map<String, Integer> currentStreams = new HashMap<>();
JSONObject listRtpServerJsonResult = zlmresTfulUtils.listRtpServer(mediaServerItem);
if (listRtpServerJsonResult != null) {
JSONArray data = listRtpServerJsonResult.getJSONArray("data");
if (data != null) {
for (int i = 0; i < data.size(); i++) {
JSONObject dataItem = data.getJSONObject(i);
currentStreams.put(dataItem.getString("stream_id"), dataItem.getInteger("port"));
}
}
}
@ -56,18 +44,18 @@ public class ZLMRTPServerFactory {
Map<String, Object> param = new HashMap<>();
int result = -1;
int newPort = getPortFromportRange();
int newPort = getPortFromportRange(mediaServerItem);
param.put("port", newPort);
param.put("enable_tcp", 1);
param.put("stream_id", streamId);
JSONObject jsonObject = zlmresTfulUtils.openRtpServer(mediaServerItem, param);
JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param);
if (jsonObject != null) {
switch (jsonObject.getInteger("code")){
if (openRtpServerResultJson != null) {
switch (openRtpServerResultJson.getInteger("code")){
case 0:
result= newPort;
break;
case -300: // id已经存在, 可能已经在其他端口推流
case -300: // id已经存在, 可能已经在其他端口推流, TODO 也可能是设备不等ack就直接推流了, 需要查询与设置的推流ip端口是否一致
Map<String, Object> closeRtpServerParam = new HashMap<>();
closeRtpServerParam.put("stream_id", streamId);
zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam);
@ -77,7 +65,7 @@ public class ZLMRTPServerFactory {
result= createRTPServer(mediaServerItem, streamId);
break;
default:
logger.error("创建RTP Server 失败 {}: " + jsonObject.getString("msg"), newPort);
logger.error("创建RTP Server 失败 {}: " + openRtpServerResultJson.getString("msg"), newPort);
break;
}
}else {
@ -87,7 +75,7 @@ public class ZLMRTPServerFactory {
return result;
}
public boolean closeRTPServer(IMediaServerItem serverItem, String streamId) {
public boolean closeRTPServer(MediaServerItem serverItem, String streamId) {
boolean result = false;
if (serverItem !=null){
Map<String, Object> param = new HashMap<>();
@ -107,21 +95,25 @@ public class ZLMRTPServerFactory {
return result;
}
private int getPortFromportRange() {
private int getPortFromportRange(MediaServerItem mediaServerItem) {
int currentPort = mediaServerItem.getCurrentPort();
if (currentPort == 0) {
String[] portRangeStrArray = mediaConfig.getRtpPortRange().split(",");
String[] portRangeStrArray = mediaServerItem.getRtpPortRange().split(",");
portRangeArray[0] = Integer.parseInt(portRangeStrArray[0]);
portRangeArray[1] = Integer.parseInt(portRangeStrArray[1]);
}
if (currentPort == 0 || currentPort++ > portRangeArray[1]) {
currentPort = portRangeArray[0];
mediaServerItem.setCurrentPort(currentPort);
return portRangeArray[0];
} else {
if (currentPort % 2 == 1) {
currentPort++;
}
return currentPort++;
currentPort++;
mediaServerItem.setCurrentPort(currentPort);
return currentPort;
}
}
@ -135,10 +127,14 @@ public class ZLMRTPServerFactory {
* @param tcp 是否为tcp
* @return SendRtpItem
*/
public SendRtpItem createSendRtpItem(IMediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){
String playSsrc = SsrcUtil.getPlaySsrc();
int localPort = createRTPServer(serverItem, SsrcUtil.getPlaySsrc());
public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){
// 使用RTPServer 功能找一个可用的端口
String playSsrc = serverItem.getSsrcConfig().getPlaySsrc();
int localPort = createRTPServer(serverItem, playSsrc);
if (localPort != -1) {
// TODO 高并发时可能因为未放入缓存而ssrc冲突
serverItem.getSsrcConfig().releaseSsrc(playSsrc);
closeRTPServer(serverItem, playSsrc);
}else {
logger.error("没有可用的端口");
@ -168,10 +164,12 @@ public class ZLMRTPServerFactory {
* @param tcp 是否为tcp
* @return SendRtpItem
*/
public SendRtpItem createSendRtpItem(IMediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){
String playSsrc = SsrcUtil.getPlaySsrc();
int localPort = createRTPServer(serverItem, SsrcUtil.getPlaySsrc());
public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){
String playSsrc = serverItem.getSsrcConfig().getPlaySsrc();
int localPort = createRTPServer(serverItem, playSsrc);
if (localPort != -1) {
// TODO 高并发时可能因为未放入缓存而ssrc冲突
serverItem.getSsrcConfig().releaseSsrc(ssrc);
closeRTPServer(serverItem, playSsrc);
}else {
logger.error("没有可用的端口");
@ -194,7 +192,7 @@ public class ZLMRTPServerFactory {
/**
* 调用zlm RESTful API startSendRtp
*/
public Boolean startSendRtpStream(IMediaServerItem mediaServerItem, Map<String, Object>param) {
public Boolean startSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
Boolean result = false;
JSONObject jsonObject = zlmresTfulUtils.startSendRtp(mediaServerItem, param);
if (jsonObject == null) {
@ -219,7 +217,7 @@ public class ZLMRTPServerFactory {
/**
* 查询待转推的流是否就绪
*/
public Boolean isStreamReady(IMediaServerItem mediaServerItem, String app, String streamId) {
public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) {
JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online"));
}
@ -229,7 +227,7 @@ public class ZLMRTPServerFactory {
* @param streamId
* @return
*/
public int totalReaderCount(IMediaServerItem mediaServerItem, String app, String streamId) {
public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) {
JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
return mediaInfo.getInteger("totalReaderCount");
}
@ -237,7 +235,7 @@ public class ZLMRTPServerFactory {
/**
* 调用zlm RESTful API stopSendRtp
*/
public Boolean stopSendRtpStream(IMediaServerItem mediaServerItem,Map<String, Object>param) {
public Boolean stopSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
Boolean result = false;
JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param);
if (jsonObject == null) {

34
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java

@ -4,22 +4,16 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
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.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.service.IStreamProxyService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.print.attribute.standard.Media;
import java.util.*;
@Component
@ -47,16 +41,26 @@ public class ZLMRunner implements CommandLineRunner {
@Override
public void run(String... strings) throws Exception {
IMediaServerItem presetMediaServer = mediaServerService.getOneByHostAndPort(
// 清楚redis缓存的在线zlm信息
mediaServerService.clearMediaServerForOnline();
// 将配置文件的meida配置写入数据库
MediaServerItem presetMediaServer = mediaServerService.getOneByHostAndPort(
mediaConfig.getIp(), mediaConfig.getHttpPort());
if (presetMediaServer != null) {
mediaConfig.setId(presetMediaServer.getId());
mediaServerService.update(mediaConfig);
MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem();
mediaSerItem.setId(presetMediaServer.getId());
mediaServerService.update(mediaSerItem);
}else {
if (mediaConfig.getId() != null) {
MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem();
mediaServerService.add(mediaSerItem);
}
}
// 订阅 zlm启动事件, 新的zlm也会从这里进入系统
hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,null,
(IMediaServerItem mediaServerItem, JSONObject response)->{
(MediaServerItem mediaServerItem, JSONObject response)->{
ZLMServerConfig zlmServerConfig = JSONObject.toJavaObject(response, ZLMServerConfig.class);
if (zlmServerConfig !=null ) {
startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId());
@ -69,23 +73,25 @@ public class ZLMRunner implements CommandLineRunner {
logger.info("等待默认zlm接入...");
// 获取所有的zlm, 并开启主动连接
List<IMediaServerItem> all = mediaServerService.getAll();
List<MediaServerItem> all = mediaServerService.getAll();
if (presetMediaServer == null) {
all.add(mediaConfig.getMediaSerItem());
}
for (IMediaServerItem mediaServerItem : all) {
for (MediaServerItem mediaServerItem : all) {
if (startGetMedia == null) startGetMedia = new HashMap<>();
startGetMedia.put(mediaServerItem.getId(), true);
new Thread(() -> {
ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem);
if (zlmServerConfig != null) {
zlmServerConfig.setIp(mediaServerItem.getIp());
zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort());
startGetMedia.remove(mediaServerItem.getId());
mediaServerService.handLeZLMServerConfig(zlmServerConfig);
}
}).start();
}
Timer timer = new Timer();
// 1分钟后未连接到则不再去主动连接
// 2分钟后未连接到则不再去主动连接, TODO 并对重启前使用此在zlm的通道发送bye
timer.schedule(new TimerTask() {
@Override
public void run() {
@ -100,7 +106,7 @@ public class ZLMRunner implements CommandLineRunner {
}, 60 * 1000 * 2);
}
public ZLMServerConfig getMediaServerConfig(IMediaServerItem mediaServerItem) {
public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem) {
if ( startGetMedia.get(mediaServerItem.getId()) == null || !startGetMedia.get(mediaServerItem.getId())) return null;
JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
ZLMServerConfig ZLMServerConfig = null;

92
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IMediaServerItem.java

@ -1,92 +0,0 @@
package com.genersoft.iot.vmp.media.zlm.dto;
public interface IMediaServerItem {
String getId();
void setId(String id);
String getIp();
void setIp(String ip);
String getHookIp();
void setHookIp(String hookIp);
String getSdpIp();
void setSdpIp(String sdpIp);
String getStreamIp();
void setStreamIp(String streamIp);
int getHttpPort();
void setHttpPort(int httpPort);
int getHttpSSlPort();
void setHttpSSlPort(int httpSSlPort);
int getRtmpPort();
void setRtmpPort(int rtmpPort);
int getRtmpSSlPort();
void setRtmpSSlPort(int rtmpSSlPort);
int getRtpProxyPort();
void setRtpProxyPort(int rtpProxyPort);
int getRtspPort();
void setRtspPort(int rtspPort);
int getRtspSSLPort();
void setRtspSSLPort(int rtspSSLPort);
boolean isAutoConfig();
void setAutoConfig(boolean autoConfig);
String getSecret();
void setSecret(String secret);
String getStreamNoneReaderDelayMS();
void setStreamNoneReaderDelayMS(String streamNoneReaderDelayMS);
boolean isRtpEnable();
void setRtpEnable(boolean rtpEnable);
String getRtpPortRange();
void setRtpPortRange(String rtpPortRange);
int getRecordAssistPort();
void setRecordAssistPort(int recordAssistPort);
boolean isDocker();
void setDocker(boolean docker);
String getUpdateTime();
void setUpdateTime(String updateTime);
String getCreateTime();
void setCreateTime(String createTime);
int getCount();
void setCount(int count);
}

54
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java

@ -1,10 +1,13 @@
package com.genersoft.iot.vmp.media.zlm.dto;
import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import org.springframework.util.StringUtils;
public class MediaServerItem implements IMediaServerItem{
import java.util.HashMap;
public class MediaServerItem{
private String id;
@ -46,9 +49,18 @@ public class MediaServerItem implements IMediaServerItem{
private String updateTime;
private boolean docker;
private boolean defaultServer;
private SsrcConfig ssrcConfig;
private int currentPort;
private int count;
/**
* 每一台ZLM都有一套独立的SSRC列表
* 在ApplicationCheckRunner里对mediaServerSsrcMap进行初始化
*/
private HashMap<String, SsrcConfig> mediaServerSsrcMap;
public MediaServerItem() {
}
@ -218,14 +230,12 @@ public class MediaServerItem implements IMediaServerItem{
this.recordAssistPort = recordAssistPort;
}
@Override
public boolean isDocker() {
return docker;
public boolean isDefaultServer() {
return defaultServer;
}
@Override
public void setDocker(boolean docker) {
this.docker = docker;
public void setDefaultServer(boolean defaultServer) {
this.defaultServer = defaultServer;
}
public String getCreateTime() {
@ -244,11 +254,29 @@ public class MediaServerItem implements IMediaServerItem{
this.updateTime = updateTime;
}
public int getCount() {
return count;
public HashMap<String, SsrcConfig> getMediaServerSsrcMap() {
return mediaServerSsrcMap;
}
public void setMediaServerSsrcMap(HashMap<String, SsrcConfig> mediaServerSsrcMap) {
this.mediaServerSsrcMap = mediaServerSsrcMap;
}
public SsrcConfig getSsrcConfig() {
return ssrcConfig;
}
public void setSsrcConfig(SsrcConfig ssrcConfig) {
this.ssrcConfig = ssrcConfig;
}
public void setCount(int count) {
this.count = count;
public int getCurrentPort() {
return currentPort;
}
public void setCurrentPort(int currentPort) {
this.currentPort = currentPort;
}
}

30
src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java

@ -3,8 +3,8 @@ package com.genersoft.iot.vmp.service;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import java.util.List;
@ -13,11 +13,13 @@ import java.util.List;
*/
public interface IMediaServerService {
List<IMediaServerItem> getAll();
List<MediaServerItem> getAll();
IMediaServerItem getOne(String generalMediaServerId);
List<MediaServerItem> getAllOnline();
IMediaServerItem getOneByHostAndPort(String host, int port);
MediaServerItem getOne(String generalMediaServerId);
MediaServerItem getOneByHostAndPort(String host, int port);
/**
* 新的节点加入
@ -26,19 +28,27 @@ public interface IMediaServerService {
*/
void handLeZLMServerConfig(ZLMServerConfig zlmServerConfig);
void updateServerCatch(IMediaServerItem mediaServerItem, Integer count, Boolean b);
IMediaServerItem getMediaServerForMinimumLoad();
MediaServerItem getMediaServerForMinimumLoad();
void setZLMConfig(IMediaServerItem mediaServerItem);
void setZLMConfig(MediaServerItem mediaServerItem);
void init();
SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId);
void closeRTPServer(Device device, String channelId);
void update(MediaConfig mediaConfig);
void clearRTPServer(MediaServerItem mediaServerItem);
void update(MediaServerItem mediaSerItem);
void addCount(String mediaServerId);
void removeCount(String mediaServerId);
void releaseSsrc(MediaServerItem mediaServerItem, String ssrc);
void clearMediaServerForOnline();
void add(MediaServerItem mediaSerItem);
void resetOnlineServerItem(MediaServerItem serverItem);
}

5
src/main/java/com/genersoft/iot/vmp/service/IMediaService.java

@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.service;
import com.alibaba.fastjson.JSONArray;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
/**
@ -33,7 +32,7 @@ public interface IMediaService {
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaServerItem, String app, String stream, JSONArray tracks);
StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, JSONArray tracks);
/**
* 根据应用名和流ID获取播放地址, 只是地址拼接返回的ip使用远程访问ip适用与zlm与wvp在一台主机的情况
@ -41,5 +40,5 @@ public interface IMediaService {
* @param stream
* @return
*/
StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr);
StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr);
}

9
src/main/java/com/genersoft/iot/vmp/service/IPlayService.java

@ -4,7 +4,6 @@ import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
@ -13,10 +12,10 @@ import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
*/
public interface IPlayService {
void onPublishHandlerForPlayBack(IMediaServerItem mediaServerItem,JSONObject resonse, String deviceId, String channelId, String uuid);
void onPublishHandlerForPlay(IMediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid);
void onPublishHandlerForPlayBack(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid);
void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid);
PlayResult play(IMediaServerItem 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);
IMediaServerItem getNewMediaServerItem(Device device);
MediaServerItem getNewMediaServerItem(Device device);
}

3
src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java

@ -1,7 +1,6 @@
package com.genersoft.iot.vmp.service;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.github.pagehelper.PageInfo;
@ -63,5 +62,5 @@ public interface IStreamProxyService {
* 获取ffmpeg.cmd模板
* @return
*/
JSONObject getFFmpegCMDs(IMediaServerItem mediaServerItem);
JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem);
}

4
src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java

@ -1,7 +1,7 @@
package com.genersoft.iot.vmp.service;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem;
import com.github.pagehelper.PageInfo;
@ -9,7 +9,7 @@ import java.util.List;
public interface IStreamPushService {
List<StreamPushItem> handleJSON(String json, IMediaServerItem mediaServerItem);
List<StreamPushItem> handleJSON(String json, MediaServerItem mediaServerItem);
/**
* 将应用名和流ID加入国标关联

38
src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java

@ -0,0 +1,38 @@
package com.genersoft.iot.vmp.service.bean;
public class SSRCInfo {
private int port;
private String ssrc;
private String StreamId;
public SSRCInfo(int port, String ssrc, String streamId) {
this.port = port;
this.ssrc = ssrc;
StreamId = streamId;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getSsrc() {
return ssrc;
}
public void setSsrc(String ssrc) {
this.ssrc = ssrc;
}
public String getStreamId() {
return StreamId;
}
public void setStreamId(String streamId) {
StreamId = streamId;
}
}

385
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java

@ -1,29 +1,32 @@
package com.genersoft.iot.vmp.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.conf.ProxyServletConfig;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.ZLMRunInfo;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.MediaServerMapper;
import org.mitre.dsmiley.httpproxy.ProxyServlet;
import com.genersoft.iot.vmp.utils.redis.JedisUtil;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import java.sql.Array;
import java.text.SimpleDateFormat;
import java.util.*;
@ -31,15 +34,13 @@ import java.util.*;
* 媒体服务器节点管理
*/
@Service
public class MediaServerServiceImpl implements IMediaServerService {
@Order(value=2)
public class MediaServerServiceImpl implements IMediaServerService, CommandLineRunner {
private final static Logger logger = LoggerFactory.getLogger(MediaServerServiceImpl.class);
private Map<String, IMediaServerItem> zlmServers = new HashMap<>(); // 所有数据库的zlm的缓存
private Map<String, Integer> zlmServerStatus = new LinkedHashMap<>(); // 所有上线的zlm的缓存以及负载
@Value("${sip.ip}")
private String sipIp;
@Autowired
private SipConfig sipConfig;
@Value("${server.ssl.enabled:false}")
private boolean sslEnabled;
@ -56,7 +57,6 @@ public class MediaServerServiceImpl implements IMediaServerService {
@Autowired
private MediaServerMapper mediaServerMapper;
@Autowired
private IRedisCatchStorage redisCatchStorage;
@ -66,53 +66,134 @@ public class MediaServerServiceImpl implements IMediaServerService {
@Autowired
private ZLMRTPServerFactory zlmrtpServerFactory;
private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Autowired
private RedisUtil redisUtil;
@Autowired
JedisUtil jedisUtil;
private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 初始化
*/
@Override
public void init() {
zlmServers.clear();
zlmServerStatus.clear();
public void run(String... args) throws Exception {
logger.info("Media Server 缓存初始化");
List<MediaServerItem> mediaServerItemList = mediaServerMapper.queryAll();
for (IMediaServerItem mediaServerItem : mediaServerItemList) {
zlmServers.put(mediaServerItem.getId(), mediaServerItem);
for (MediaServerItem mediaServerItem : mediaServerItemList) {
// 更新
if (mediaServerItem.getSsrcConfig() == null) {
SsrcConfig ssrcConfig = new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getSipDomain());
mediaServerItem.setSsrcConfig(ssrcConfig);
redisUtil.set(VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerItem.getId(), mediaServerItem);
}
// 查询redis是否存在此mediaServer
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerItem.getId();
if (!redisUtil.hasKey(key)) {
redisUtil.set(key, mediaServerItem);
}
}
}
@Override
public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId) {
if (mediaServerItem == null || mediaServerItem.getId() == null) return null;
// 获取mediaServer可用的ssrc
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerItem.getId();
SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig();
if (ssrcConfig == null) {
logger.info("media server [ {} ] ssrcConfig is null", mediaServerItem.getId());
return null;
}else {
String ssrc = ssrcConfig.getPlaySsrc();
if (streamId == null) streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase();
int rtpServerPort = mediaServerItem.getRtpProxyPort();
if (mediaServerItem.isRtpEnable()) {
rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId);
}
redisUtil.set(key, mediaServerItem);
return new SSRCInfo(rtpServerPort, ssrc, streamId);
}
}
@Override
public void closeRTPServer(Device device, String channelId) {
StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId);
IMediaServerItem mediaServerItem = null;
if (streamInfo != null) {
mediaServerItem = this.getOne (streamInfo.getMediaServerId());
String mediaServerId = streamSession.getMediaServerId(device.getDeviceId(), channelId);
MediaServerItem mediaServerItem = this.getOne(mediaServerId);
if (mediaServerItem != null) {
String streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
zlmrtpServerFactory.closeRTPServer(mediaServerItem, streamId);
releaseSsrc(mediaServerItem, streamSession.getSSRC(device.getDeviceId(), channelId));
}
String streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
zlmrtpServerFactory.closeRTPServer(mediaServerItem, streamId);
streamSession.remove(device.getDeviceId(), channelId);
}
@Override
public void update(MediaConfig mediaConfig) {
public void releaseSsrc(MediaServerItem mediaServerItem, String ssrc) {
if (mediaServerItem == null || ssrc == null) return;
SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig();
ssrcConfig.releaseSsrc(ssrc);
mediaServerItem.setSsrcConfig(ssrcConfig);
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerItem.getId();
redisUtil.set(key, mediaServerItem);
}
/**
* zlm 重启后重置他的推流信息 TODO 给正在使用的设备发送停止命令
* @param mediaServerItem
*/
@Override
public void clearRTPServer(MediaServerItem mediaServerItem) {
mediaServerItem.setSsrcConfig(new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getSipDomain()));
redisUtil.zAdd(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX, mediaServerItem.getId(), 0);
}
@Override
public List<IMediaServerItem> getAll() {
if (zlmServers.size() == 0) {
init();
public void update(MediaServerItem mediaSerItem) {
mediaServerMapper.update(mediaSerItem);
MediaServerItem mediaServerItemInRedis = getOne(mediaSerItem.getId());
MediaServerItem mediaServerItemInDataBase = mediaServerMapper.queryOne(mediaSerItem.getId());
if (mediaServerItemInRedis != null && mediaServerItemInRedis.getSsrcConfig() != null) {
mediaServerItemInDataBase.setSsrcConfig(mediaServerItemInRedis.getSsrcConfig());
}else {
mediaServerItemInDataBase.setSsrcConfig(
new SsrcConfig(
mediaServerItemInDataBase.getId(),
null,
sipConfig.getSipDomain()
)
);
}
List<IMediaServerItem> result = new ArrayList<>();
for (String id : zlmServers.keySet()) {
IMediaServerItem mediaServerItem = zlmServers.get(id);
mediaServerItem.setCount(zlmServerStatus.get(id) == null ? 0 : zlmServerStatus.get(id));
result.add(mediaServerItem);
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerItemInDataBase.getId();
redisUtil.set(key, mediaServerItemInDataBase);
}
@Override
public List<MediaServerItem> getAll() {
List<MediaServerItem> result = new ArrayList<>();
List<Object> mediaServerKeys = redisUtil.scan(String.format("%S*", VideoManagerConstants.MEDIA_SERVER_PREFIX));
for (int i = 0; i < mediaServerKeys.size(); i++) {
String key = (String) mediaServerKeys.get(i);
result.add((MediaServerItem)redisUtil.get(key));
}
return result;
}
// return mediaServerMapper.queryAll();
@Override
public List<MediaServerItem> getAllOnline() {
String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
Set<String> mediaServerIdSet = redisUtil.zRevRange(key, 0, -1);
List<MediaServerItem> result = new ArrayList<>();
if (mediaServerIdSet != null && mediaServerIdSet.size() > 0) {
for (String mediaServerId : mediaServerIdSet) {
String serverKey = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerId;
result.add((MediaServerItem) redisUtil.get(serverKey));
}
}
return result;
}
/**
@ -121,26 +202,28 @@ public class MediaServerServiceImpl implements IMediaServerService {
* @return MediaServerItem
*/
@Override
public IMediaServerItem getOne(String mediaServerId) {
if (mediaServerId ==null) return null;
IMediaServerItem mediaServerItem = zlmServers.get(mediaServerId);
if (mediaServerItem != null) {
mediaServerItem.setCount(zlmServerStatus.get(mediaServerId) == null ? 0 : zlmServerStatus.get(mediaServerId));
return mediaServerItem;
}else {
IMediaServerItem item = mediaServerMapper.queryOne(mediaServerId);
if (item != null) {
zlmServers.put(item.getId(), item);
}
return item;
}
public MediaServerItem getOne(String mediaServerId) {
if (mediaServerId == null) return null;
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + mediaServerId;
return (MediaServerItem)redisUtil.get(key);
}
@Override
public IMediaServerItem getOneByHostAndPort(String host, int port) {
public MediaServerItem getOneByHostAndPort(String host, int port) {
return mediaServerMapper.queryOneByHostAndPort(host, port);
}
@Override
public void clearMediaServerForOnline() {
String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
redisUtil.del(key);
}
@Override
public void add(MediaServerItem mediaSerItem) {
mediaServerMapper.add(mediaSerItem);
}
/**
* 处理zlm上线
* @param zlmServerConfig zlm上线携带的参数
@ -150,111 +233,100 @@ public class MediaServerServiceImpl implements IMediaServerService {
logger.info("[ {} ]-[ {}:{} ]已连接",
zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
IMediaServerItem serverItem = getOne(zlmServerConfig.getGeneralMediaServerId());
String now = this.format.format(new Date(System.currentTimeMillis()));
if (serverItem != null) {
serverItem.setSecret(zlmServerConfig.getApiSecret());
serverItem.setIp(zlmServerConfig.getIp());
MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId());
if (serverItem == null) {
serverItem = mediaServerMapper.queryOneByHostAndPort(zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
}
if (zlmServerConfig.getGeneralMediaServerId().equals(mediaConfig.getId())
|| (zlmServerConfig.getIp().equals(mediaConfig.getIp()) && zlmServerConfig.getHttpPort() == mediaConfig.getHttpPort())) {
// 配置文件的zlm
// 如果是配置文件中的zlm。 也就是默认zlm。 一切以配置文件内容为准
// docker部署不会使用zlm配置的端口号;
// 直接编译部署的使用配置文件的端口号,如果zlm修改配改了配置,wvp自动修改
if (serverItem.getId().equals(mediaConfig.getId())
|| (serverItem.getIp().equals(mediaConfig.getIp()) && serverItem.getHttpPort() == mediaConfig.getHttpPort())) {
// 配置文件的zlm
mediaConfig.setId(zlmServerConfig.getGeneralMediaServerId());
mediaConfig.setUpdateTime(now);
if (mediaConfig.getHttpPort() == 0) mediaConfig.setHttpPort(zlmServerConfig.getHttpPort());
if (mediaConfig.getHttpSSlPort() == 0) mediaConfig.setHttpSSlPort(zlmServerConfig.getHttpSSLport());
if (mediaConfig.getRtmpPort() == 0) mediaConfig.setRtmpPort(zlmServerConfig.getRtmpPort());
if (mediaConfig.getRtmpSSlPort() == 0) mediaConfig.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
if (mediaConfig.getRtspPort() == 0) mediaConfig.setRtspPort(zlmServerConfig.getRtspPort());
if (mediaConfig.getRtspSSLPort() == 0) mediaConfig.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
if (mediaConfig.getRtpProxyPort() == 0) mediaConfig.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
mediaServerMapper.update(mediaConfig);
serverItem = mediaConfig.getMediaSerItem();
setZLMConfig(mediaConfig);
}else {
if (!serverItem.isDocker()) {
serverItem.setHttpPort(zlmServerConfig.getHttpPort());
serverItem.setHttpSSlPort(zlmServerConfig.getHttpSSLport());
serverItem.setRtmpPort(zlmServerConfig.getRtmpPort());
serverItem.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
serverItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
serverItem.setRtspPort(zlmServerConfig.getRtspPort());
// docker部署不会使用zlm配置的端口号不是默认的则不做更新, 配置修改需要自行修改server配置;
MediaServerItem serverItemFromConfig = mediaConfig.getMediaSerItem();
serverItemFromConfig.setId(zlmServerConfig.getGeneralMediaServerId());
if (mediaConfig.getHttpPort() == 0) serverItemFromConfig.setHttpPort(zlmServerConfig.getHttpPort());
if (mediaConfig.getHttpSSlPort() == 0) serverItemFromConfig.setHttpSSlPort(zlmServerConfig.getHttpSSLport());
if (mediaConfig.getRtmpPort() == 0) serverItemFromConfig.setRtmpPort(zlmServerConfig.getRtmpPort());
if (mediaConfig.getRtmpSSlPort() == 0) serverItemFromConfig.setRtmpSSlPort(zlmServerConfig.getRtmpSslPort());
if (mediaConfig.getRtspPort() == 0) serverItemFromConfig.setRtspPort(zlmServerConfig.getRtspPort());
if (mediaConfig.getRtspSSLPort() == 0) serverItemFromConfig.setRtspSSLPort(zlmServerConfig.getRtspSSlport());
if (mediaConfig.getRtpProxyPort() == 0) serverItemFromConfig.setRtpProxyPort(zlmServerConfig.getRtpProxyPort());
if (serverItem != null){
// 可能是同一个zlm但id发生了变化
if (!serverItem.getId().equals(zlmServerConfig.getGeneralMediaServerId())) {
mediaServerMapper.delOne(serverItem.getId());
redisUtil.del(VideoManagerConstants.MEDIA_SERVER_PREFIX + serverItem.getId());
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + serverItemFromConfig.getId();
serverItemFromConfig.setSsrcConfig(new SsrcConfig(serverItemFromConfig.getId(), null, sipConfig.getSipDomain()));
redisUtil.set(key, serverItemFromConfig);
mediaServerMapper.add(serverItemFromConfig);
}else {
mediaServerMapper.update(serverItemFromConfig);
}
serverItem.setUpdateTime(now);
mediaServerMapper.update(serverItem);
setZLMConfig(serverItem);
}else {
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + serverItemFromConfig.getId();
serverItemFromConfig.setSsrcConfig(new SsrcConfig(serverItemFromConfig.getId(), null, sipConfig.getSipDomain()));
redisUtil.set(key, serverItemFromConfig);
mediaServerMapper.add(serverItemFromConfig);
}
resetOnlineServerItem(serverItemFromConfig);
setZLMConfig(serverItemFromConfig);
}else {
if (zlmServerConfig.getGeneralMediaServerId().equals(mediaConfig.getId())
|| (zlmServerConfig.getIp().equals(mediaConfig.getIp()) && zlmServerConfig.getHttpPort() == mediaConfig.getHttpPort())) {
mediaConfig.setId(zlmServerConfig.getGeneralMediaServerId());
mediaConfig.setCreateTime(now);
mediaConfig.setUpdateTime(now);
serverItem = mediaConfig.getMediaSerItem();
mediaServerMapper.add(mediaConfig);
}else {
// 一个新的zlm接入wvp
serverItem = new MediaServerItem(zlmServerConfig, sipIp);
serverItem.setCreateTime(now);
serverItem.setUpdateTime(now);
String now = this.format.format(new Date(System.currentTimeMillis()));
if (serverItem == null){
// 一个新的zlm接入wvp
serverItem = new MediaServerItem(zlmServerConfig, sipConfig.getSipIp());
serverItem.setCreateTime(now);
serverItem.setUpdateTime(now);
String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + serverItem.getId();
serverItem.setSsrcConfig(new SsrcConfig(serverItem.getId(), null, sipConfig.getSipDomain()));
redisUtil.set(key, serverItem);
// 存入数据库
mediaServerMapper.add(serverItem);
setZLMConfig(serverItem);
}
resetOnlineServerItem(serverItem);
}
// 更新缓存
if (zlmServerStatus.get(serverItem.getId()) == null) {
zlmServers.put(serverItem.getId(), serverItem);
zlmServerStatus.put(serverItem.getId(),0);
}
// 查询服务流数量
IMediaServerItem finalServerItem = serverItem;
zlmresTfulUtils.getMediaList(serverItem, null, null, "rtmp",(mediaList ->{
Integer code = mediaList.getInteger("code");
if (code == 0) {
JSONArray data = mediaList.getJSONArray("data");
if (data != null) {
zlmServerStatus.put(finalServerItem.getId(),data.size());
}else {
zlmServerStatus.put(finalServerItem.getId(),0);
}
}
}));
}
/**
* 更新缓存
* @param mediaServerItem zlm服务
* @param count 在线数
* @param online 在线状态
*/
@Override
public void updateServerCatch(IMediaServerItem mediaServerItem, Integer count, Boolean online) {
if (mediaServerItem != null) {
zlmServers.put(mediaServerItem.getId(), mediaServerItem);
Collection<Integer> values = zlmServerStatus.values();
if (online != null && count != null) {
zlmServerStatus.put(mediaServerItem.getId(), count);
}
public void resetOnlineServerItem(MediaServerItem serverItem) {
// 更新缓存
String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
// 使用zset的分数作为当前并发量, 默认值设置为0
if (redisUtil.zScore(key, serverItem.getId()) == null) { // 不存在则设置默认值 已存在则重置
redisUtil.zAdd(key, serverItem.getId(), 0L);
// 查询服务流数量
zlmresTfulUtils.getMediaList(serverItem, null, null, "rtmp",(mediaList ->{
Integer code = mediaList.getInteger("code");
if (code == 0) {
JSONArray data = mediaList.getJSONArray("data");
if (data != null) {
redisUtil.zAdd(key, serverItem.getId(), data.size());
}
}
}));
}else {
clearRTPServer(serverItem);
}
}
@Override
public void addCount(String mediaServerId) {
if (zlmServerStatus.get(mediaServerId) != null) {
zlmServerStatus.put(mediaServerId, zlmServerStatus.get(mediaServerId) + 1);
}
if (mediaServerId == null) return;
String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
Double aDouble = redisUtil.zScore(key, mediaServerId);
redisUtil.zIncrScore(key, mediaServerId, 1);
}
@Override
public void removeCount(String mediaServerId) {
if (zlmServerStatus.get(mediaServerId) != null) {
zlmServerStatus.put(mediaServerId, zlmServerStatus.get(mediaServerId) - 1);
}
String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
redisUtil.zIncrScore(key, mediaServerId, - 1);
}
/**
@ -262,35 +334,18 @@ public class MediaServerServiceImpl implements IMediaServerService {
* @return MediaServerItem
*/
@Override
public IMediaServerItem getMediaServerForMinimumLoad() {
int mediaCount = -1;
String key = null;
System.out.println(JSON.toJSONString(zlmServerStatus));
if (zlmServerStatus.size() == 1) {
Map.Entry entry = zlmServerStatus.entrySet().iterator().next();
key= (String) entry.getKey();
}else {
for (String id : zlmServerStatus.keySet()) {
if (key == null) {
key = id;
mediaCount = zlmServerStatus.get(id);
}
if (zlmServerStatus.get(id) == 0) {
key = id;
break;
}else if (mediaCount >= zlmServerStatus.get(id)){
mediaCount = zlmServerStatus.get(id);
key = id;
}
}
}
public MediaServerItem getMediaServerForMinimumLoad() {
String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX;
if (key == null) {
if (redisUtil.zSize(key) == null || redisUtil.zSize(key) == 0) {
logger.info("获取负载最低的节点时无在线节点");
return null;
}else{
return zlmServers.get(key);
}
// 获取分数最低的,及并发最低的
Set<Object> objects = redisUtil.ZRange(key, 0, -1);
ArrayList<Object> MediaServerObjectS = new ArrayList<>(objects);
String mediaServerId = (String)MediaServerObjectS.get(0);
return getOne(mediaServerId);
}
/**
@ -298,7 +353,7 @@ public class MediaServerServiceImpl implements IMediaServerService {
* @param mediaServerItem 服务ID
*/
@Override
public void setZLMConfig(IMediaServerItem mediaServerItem) {
public void setZLMConfig(MediaServerItem mediaServerItem) {
logger.info("[ {} ]-[ {}:{} ]设置zlm",
mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
String protocol = sslEnabled ? "https" : "http";
@ -333,8 +388,10 @@ public class MediaServerServiceImpl implements IMediaServerService {
logger.info("[ {} ]-[ {}:{} ]设置zlm成功",
mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
}else {
logger.info("[ {} ]-[ {}:{} ]设置zlm失败" + responseJSON.getString("msg"),
logger.info("[ {} ]-[ {}:{} ]设置zlm失败",
mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
}
}
}

8
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java

@ -4,9 +4,7 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@ -33,14 +31,14 @@ public class MediaServiceImpl implements IMediaService {
@Override
public StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks) {
public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, JSONArray tracks) {
return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null);
}
@Override
public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr) {
StreamInfo streamInfo = null;
IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (mediaInfo == null) {
return streamInfo;
}
@ -63,7 +61,7 @@ public class MediaServiceImpl implements IMediaService {
}
@Override
public StreamInfo getStreamInfoByAppAndStream(IMediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr) {
public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, JSONArray tracks, String addr) {
StreamInfo streamInfoResult = new StreamInfo();
streamInfoResult.setStreamId(stream);
streamInfoResult.setApp(app);

65
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java

@ -14,11 +14,12 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
import com.genersoft.iot.vmp.service.IMediaService;
@ -53,6 +54,9 @@ public class PlayServiceImpl implements IPlayService {
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private RedisUtil redis;
@Autowired
private DeferredResultHolder resultHolder;
@ -73,7 +77,7 @@ public class PlayServiceImpl implements IPlayService {
@Override
public PlayResult play(IMediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent) {
public PlayResult play(MediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent) {
PlayResult playResult = new PlayResult();
if (mediaServerItem == null) {
RequestMessage msg = new RequestMessage();
@ -97,14 +101,21 @@ public class PlayServiceImpl implements IPlayService {
// 超时处理
result.onTimeout(()->{
logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId));
// 释放rtpserver
mediaServerService.closeRTPServer(playResult.getDevice(), channelId);
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + playResult.getUuid());
WVPResult wvpResult = new WVPResult();
wvpResult.setCode(-1);
wvpResult.setMsg("Timeout");
SIPDialog dialog = streamSession.getDialog(deviceId, channelId);
if (dialog != null) {
wvpResult.setMsg("收流超时,请稍候重试");
}else {
wvpResult.setMsg("点播超时,请稍候重试");
}
msg.setData(wvpResult);
// 点播超时回复BYE
cmder.streamByeCmd(device.getDeviceId(), channelId);
// 释放rtpserver
mediaServerService.closeRTPServer(playResult.getDevice(), channelId);
resultHolder.invokeResult(msg);
});
result.onCompletion(()->{
@ -131,7 +142,7 @@ public class PlayServiceImpl implements IPlayService {
WVPResult wvpResult = (WVPResult)responseEntity.getBody();
if (wvpResult.getCode() == 0) {
StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
IMediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
String streamUrl = streamInfoForSuccess.getFmp4();
// 请求截图
zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName);
@ -142,14 +153,23 @@ public class PlayServiceImpl implements IPlayService {
}
});
if (streamInfo == null) {
SSRCInfo ssrcInfo;
String streamId = null;
if (mediaServerItem.isRtpEnable()) {
streamId = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
}
ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId);
// 发送点播消息
cmder.playStreamCmd(mediaServerItem, device, channelId, (IMediaServerItem mediaServerItemInUse, JSONObject response) -> {
cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString());
onPublishHandlerForPlay(mediaServerItemInUse, response, deviceId, channelId, uuid.toString());
if (hookEvent != null) {
hookEvent.response(mediaServerItem, response);
}
}, (event) -> {
// 点播返回sip错误
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
Response response = event.getResponse();
@ -162,6 +182,7 @@ public class PlayServiceImpl implements IPlayService {
if (errorEvent != null) {
errorEvent.response(event);
}
});
} else {
String streamId = streamInfo.getStreamId();
@ -176,7 +197,7 @@ public class PlayServiceImpl implements IPlayService {
return playResult;
}
String mediaServerId = streamInfo.getMediaServerId();
IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId);
if (rtpInfo != null && rtpInfo.getBoolean("exist")) {
@ -194,9 +215,17 @@ public class PlayServiceImpl implements IPlayService {
hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo)));
}
} else {
// TODO 点播前是否重置状态
redisCatchStorage.stopPlay(streamInfo);
storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
cmder.playStreamCmd(mediaServerItem, device, channelId, (IMediaServerItem mediaServerItemInuse, JSONObject response) -> {
SSRCInfo ssrcInfo;
String streamId2 = null;
if (mediaServerItem.isRtpEnable()) {
streamId2 = String.format("gb_play_%s_%s", device.getDeviceId(), channelId);
}
ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId2);
cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString());
onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid.toString());
}, (event) -> {
@ -218,7 +247,7 @@ public class PlayServiceImpl implements IPlayService {
}
@Override
public void onPublishHandlerForPlay(IMediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
public void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) {
RequestMessage msg = new RequestMessage();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId, uuid);
@ -228,14 +257,6 @@ public class PlayServiceImpl implements IPlayService {
deviceChannel.setStreamId(streamInfo.getStreamId());
storager.startPlay(deviceId, channelId, streamInfo.getStreamId());
}
ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId);
SIPDialog dialog = (SIPDialog)transaction.getDialog();
StreamInfo.TransactionInfo transactionInfo = new StreamInfo.TransactionInfo();
transactionInfo.callId = dialog.getCallId().getCallId();
transactionInfo.localTag = dialog.getLocalTag();
transactionInfo.remoteTag = dialog.getRemoteTag();
transactionInfo.branch = dialog.getFirstTransactionInt().getBranchId();
streamInfo.setTransactionInfo(transactionInfo);
redisCatchStorage.startPlay(streamInfo);
msg.setData(JSON.toJSONString(streamInfo));
@ -254,10 +275,10 @@ public class PlayServiceImpl implements IPlayService {
}
@Override
public IMediaServerItem getNewMediaServerItem(Device device) {
public MediaServerItem getNewMediaServerItem(Device device) {
if (device == null) return null;
String mediaServerId = device.getMediaServerId();
IMediaServerItem mediaServerItem = null;
MediaServerItem mediaServerItem = null;
if (mediaServerId == null) {
mediaServerItem = mediaServerService.getMediaServerForMinimumLoad();
}else {
@ -270,7 +291,7 @@ public class PlayServiceImpl implements IPlayService {
}
@Override
public void onPublishHandlerForPlayBack(IMediaServerItem 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();
msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId, uuid);
@ -285,7 +306,7 @@ public class PlayServiceImpl implements IPlayService {
}
}
public StreamInfo onPublishHandler(IMediaServerItem 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");
JSONArray tracks = resonse.getJSONArray("tracks");
StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem,"rtp", streamId, tracks);

9
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java

@ -3,7 +3,6 @@ package com.genersoft.iot.vmp.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.service.IGbStreamService;
@ -58,7 +57,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
@Override
public String save(StreamProxyItem param) {
IMediaServerItem mediaInfo;
MediaServerItem mediaInfo;
if ("auto".equals(param.getMediaServerId())){
mediaInfo = mediaServerService.getMediaServerForMinimumLoad();
}else {
@ -120,7 +119,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
@Override
public JSONObject addStreamProxyToZlm(StreamProxyItem param) {
JSONObject result = null;
IMediaServerItem mediaServerItem = null;
MediaServerItem mediaServerItem = null;
if (param.getMediaServerId() == null) {
logger.warn("添加代理时MediaServerId 为null");
return null;
@ -141,7 +140,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
@Override
public JSONObject removeStreamProxyFromZlm(StreamProxyItem param) {
if (param ==null) return null;
IMediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId());
JSONObject result = zlmresTfulUtils.closeStreams(mediaServerItem, param.getApp(), param.getStream());
return result;
}
@ -198,7 +197,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
}
@Override
public JSONObject getFFmpegCMDs(IMediaServerItem mediaServerItem) {
public JSONObject getFFmpegCMDs(MediaServerItem mediaServerItem) {
JSONObject result = new JSONObject();
JSONObject mediaServerConfigResuly = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
if (mediaServerConfigResuly != null && mediaServerConfigResuly.getInteger("code") == 0

5
src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java

@ -5,7 +5,6 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.genersoft.iot.vmp.gb28181.bean.GbStream;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
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.StreamPushItem;
@ -43,7 +42,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
private IMediaServerService mediaServerService;
@Override
public List<StreamPushItem> handleJSON(String jsonData, IMediaServerItem mediaServerItem) {
public List<StreamPushItem> handleJSON(String jsonData, MediaServerItem mediaServerItem) {
if (jsonData == null) return null;
Map<String, StreamPushItem> result = new HashMap<>();
@ -98,7 +97,7 @@ public class StreamPushServiceImpl implements IStreamPushService {
@Override
public boolean removeFromGB(GbStream stream) {
int del = gbStreamMapper.del(stream.getApp(), stream.getStream());
IMediaServerItem mediaInfo = mediaServerService.getOne(stream.getMediaServerId());
MediaServerItem mediaInfo = mediaServerService.getOne(stream.getMediaServerId());
JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaInfo, stream.getApp(), stream.getStream());
if (mediaList == null) {
streamPushMapper.del(stream.getApp(), stream.getStream());

2
src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java

@ -5,8 +5,6 @@ import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import java.util.List;
import java.util.Map;

1
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java

@ -3,7 +3,6 @@ package com.genersoft.iot.vmp.storager;
import java.util.List;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
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.StreamPushItem;

10
src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java

@ -1,7 +1,5 @@
package com.genersoft.iot.vmp.storager.dao;
import com.genersoft.iot.vmp.conf.MediaConfig;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
@ -35,6 +33,7 @@ public interface MediaServerMapper {
"rtpEnable, " +
"rtpPortRange, " +
"recordAssistPort, " +
"defaultServer, " +
"createTime, " +
"updateTime" +
") VALUES " +
@ -57,9 +56,10 @@ public interface MediaServerMapper {
"${rtpEnable}, " +
"'${rtpPortRange}', " +
"${recordAssistPort}, " +
"${defaultServer}, " +
"'${createTime}', " +
"'${updateTime}')")
int add(IMediaServerItem mediaServerItem);
int add(MediaServerItem mediaServerItem);
@Update(value = {" <script>" +
"UPDATE media_server " +
@ -83,7 +83,7 @@ public interface MediaServerMapper {
"<if test=\"recordAssistPort != null\">, recordAssistPort=${recordAssistPort}</if>" +
"WHERE id='${id}'"+
" </script>"})
int update(IMediaServerItem mediaServerItem);
int update(MediaServerItem mediaServerItem);
@Select("SELECT * FROM media_server WHERE id='${id}'")
MediaServerItem queryOne(String id);
@ -92,7 +92,7 @@ public interface MediaServerMapper {
List<MediaServerItem> queryAll();
@Select("DELETE FROM media_server WHERE id='${id}'")
int delOne(String secret);
void delOne(String id);
@Select("SELECT * FROM media_server WHERE ip='${host}' and httpPort=${port}")
MediaServerItem queryOneByHostAndPort(String host, int port);

3
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java

@ -3,10 +3,7 @@ package com.genersoft.iot.vmp.storager.impl;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;

8
src/main/java/com/genersoft/iot/vmp/utils/ConfigConst.java

@ -0,0 +1,8 @@
package com.genersoft.iot.vmp.utils;
public class ConfigConst {
/**
* 播流最大并发个数
*/
public static final Integer MAX_STRTEAM_COUNT = 10000;
}

31
src/main/java/com/genersoft/iot/vmp/utils/SerializeUtils.java

@ -0,0 +1,31 @@
package com.genersoft.iot.vmp.utils;
import java.io.*;
public class SerializeUtils {
public static byte[] serialize(Object obj){
byte[] bytes = null;
try {
ByteArrayOutputStream baos=new ByteArrayOutputStream();;
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(obj);
bytes=baos.toByteArray();
baos.close();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
return bytes;
}
public static Object deSerialize(byte[] bytes){
Object obj=null;
try {
ByteArrayInputStream bais=new ByteArrayInputStream(bytes);
ObjectInputStream ois=new ObjectInputStream(bais);
obj=ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}

97
src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java

@ -0,0 +1,97 @@
package com.genersoft.iot.vmp.utils.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.Set;
/**
* @Description:Jedis工具类
* @author: wangshaopeng@sunnybs.com
* @date: 2021年03月22日 下午8:27:29
*/
@Component
public class JedisUtil {
@Autowired
private JedisPool jedisPool;
// ============================== Key ==============================
/**
* 检查给定 key 是否存在
*
* @param key
* @return
*/
public Boolean exists(String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
Boolean exists = jedis.exists(key);
return exists;
} finally {
returnToPool(jedis);
}
}
// ============================== Set ==============================
/**
* SADD key member [member ...]
* 将一个或多个 member 元素加入到集合 key 当中已经存在于集合的 member 元素将被忽略
* 假如 key 不存在则创建一个只包含 member 元素作成员的集合
* key 不是集合类型时返回一个错误
*/
public Long sadd(String key, String... members) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
Long smove = jedis.sadd(key, members);
return smove;
} finally {
returnToPool(jedis);
}
}
/**
* SMEMBERS key
* 返回集合 key 中的所有成员
* 不存在的 key 被视为空集合
*/
public Set<String> smembers(String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
Set<String> smembers = jedis.smembers(key);
return smembers;
} finally {
returnToPool(jedis);
}
}
/**
* SREM key member1 [member2]
* 移除集合中一个或多个成员
*/
public Long srem(String key, String... member) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
Long srem = jedis.srem(key, member);
return srem;
} finally {
returnToPool(jedis);
}
}
private void returnToPool(Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}
}

6
src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java

@ -415,10 +415,10 @@ public class RedisUtil {
*
* @param key
* @param value
* @param score
* @param delta -1 表示减 1 表示加1
*/
public Double zIncrScore(Object key, Object value, double score) {
return redisTemplate.opsForZSet().incrementScore(key, value, score);
public Double zIncrScore(Object key, Object value, double delta) {
return redisTemplate.opsForZSet().incrementScore(key, value, delta);
}
/**

25
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java

@ -2,13 +2,12 @@ package com.genersoft.iot.vmp.vmanager.gb28181.play;
import com.alibaba.fastjson.JSONArray;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@ -37,7 +36,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import org.springframework.web.context.request.async.DeferredResult;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@ -89,7 +88,7 @@ public class PlayController {
// 获取可用的zlm
Device device = storager.queryVideoDevice(deviceId);
IMediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
PlayResult playResult = playService.play(newMediaServerItem, deviceId, channelId, null, null);
return playResult.getResult();
@ -174,7 +173,7 @@ public class PlayController {
logger.warn("视频转码API调用失败!, 视频流已经停止!");
return new ResponseEntity<String>("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK);
}
IMediaServerItem mediaInfo = mediaServerService.getOne(streamInfo.getMediaServerId());
MediaServerItem mediaInfo = mediaServerService.getOne(streamInfo.getMediaServerId());
JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId);
if (!rtpInfo.getBoolean("exist")) {
logger.warn("视频转码API调用失败!, 视频流已停止推流!");
@ -219,7 +218,7 @@ public class PlayController {
result.put("msg", "mediaServerId is null");
return new ResponseEntity<String>( result.toJSONString(), HttpStatus.BAD_REQUEST);
}
IMediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
if (mediaInfo == null) {
result.put("code", 0);
result.put("msg", "使用的流媒体已经停止运行");
@ -307,16 +306,16 @@ public class PlayController {
logger.debug("获取所有的ssrc");
}
JSONArray objects = new JSONArray();
for(Map.Entry<String, String> entry: streamSession.getSsrcMap().entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
List<SsrcTransaction> allSsrc = streamSession.getAllSsrc();
for (SsrcTransaction transaction : allSsrc) {
JSONObject jsonObject = new JSONObject();
String[] keyArray = entry.getKey().split("_");
jsonObject.put("deviceId", keyArray[0]);
jsonObject.put("channelId", keyArray[1]);
jsonObject.put("ssrc", entry.getValue());
jsonObject.put("streamId", streamSession.getStreamIdMap().get(entry.getKey()));
jsonObject.put("deviceId", transaction.getDeviceId());
jsonObject.put("channelId", transaction.getChannelId());
jsonObject.put("ssrc", transaction.getSsrc());
jsonObject.put("streamId", transaction.getStreamId());
objects.add(jsonObject);
}
WVPResult<JSONObject> result = new WVPResult<>();
result.setCode(0);
result.setMsg("success");

14
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java

@ -3,9 +3,9 @@ package com.genersoft.iot.vmp.vmanager.gb28181.playback;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
//import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.service.IPlayService;
import io.swagger.annotations.Api;
@ -58,6 +58,9 @@ public class DownloadController {
@Autowired
private DeferredResultHolder resultHolder;
@Autowired
private IMediaServerService mediaServerService;
@ApiOperation("开始历史媒体下载")
@ApiImplicitParams({
@ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
@ -90,7 +93,7 @@ public class DownloadController {
cmder.streamByeCmd(deviceId, channelId);
}
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result);
IMediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
if (newMediaServerItem == null) {
logger.warn(String.format("设备下载响应超时,deviceId:%s ,channelId:%s", deviceId, channelId));
RequestMessage msg = new RequestMessage();
@ -99,7 +102,10 @@ public class DownloadController {
resultHolder.invokeResult(msg);
return result;
}
cmder.downloadStreamCmd(newMediaServerItem, device, channelId, startTime, endTime, downloadSpeed, (IMediaServerItem mediaServerItem, JSONObject response) -> {
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null);
cmder.downloadStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, (MediaServerItem mediaServerItem, JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString());
playService.onPublishHandlerForPlayBack(mediaServerItem, response, deviceId, channelId, uuid.toString());
}, event -> {

22
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java

@ -4,8 +4,9 @@ import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
//import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.service.IPlayService;
import io.swagger.annotations.Api;
@ -58,6 +59,9 @@ public class PlaybackController {
@Autowired
private DeferredResultHolder resultHolder;
@Autowired
private IMediaServerService mediaServerService;
@ApiOperation("开始视频回放")
@ApiImplicitParams({
@ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class),
@ -74,6 +78,15 @@ public class PlaybackController {
}
UUID uuid = UUID.randomUUID();
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(30000L);
Device device = storager.queryVideoDevice(deviceId);
if (device == null) {
result.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
return result;
}
MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null);
// 超时处理
result.onTimeout(()->{
logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId));
@ -82,14 +95,14 @@ public class PlaybackController {
msg.setData("Timeout");
resultHolder.invokeResult(msg);
});
Device device = storager.queryVideoDevice(deviceId);
StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId);
if (streamInfo != null) {
// 停止之前的回放
cmder.streamByeCmd(deviceId, channelId);
}
resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result);
IMediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);
if (newMediaServerItem == null) {
logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId));
RequestMessage msg = new RequestMessage();
@ -98,7 +111,8 @@ public class PlaybackController {
resultHolder.invokeResult(msg);
return result;
}
cmder.playbackStreamCmd(newMediaServerItem, device, channelId, startTime, endTime, (IMediaServerItem mediaServerItem, JSONObject response) -> {
cmder.playbackStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, (MediaServerItem mediaServerItem, JSONObject response) -> {
logger.info("收到订阅消息: " + response.toJSONString());
playService.onPublishHandlerForPlayBack(mediaServerItem, response, deviceId, channelId, uuid.toString());
}, event -> {

23
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/session/PlayTypeEnum.java

@ -0,0 +1,23 @@
package com.genersoft.iot.vmp.vmanager.gb28181.session;
public enum PlayTypeEnum {
PLAY("0", "直播"),
PLAY_BACK("1", "回放");
private String value;
private String name;
PlayTypeEnum(String value, String name) {
this.value = value;
this.name = name;
}
public String getValue() {
return value;
}
public String getName() {
return name;
}
}

24
src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java

@ -1,10 +1,8 @@
package com.genersoft.iot.vmp.vmanager.server;
import com.genersoft.iot.vmp.VManageBootstrap;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import gov.nist.javax.sip.SipStackImpl;
@ -17,7 +15,6 @@ import org.springframework.web.bind.annotation.*;
import javax.sip.ListeningPoint;
import javax.sip.ObjectInUseException;
import javax.sip.SipProvider;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@ -38,19 +35,30 @@ public class ServerController {
@ApiOperation("流媒体服务列表")
@GetMapping(value = "/media_server/list")
@ResponseBody
public WVPResult<List<IMediaServerItem>> getMediaServerList(){
WVPResult<List<IMediaServerItem>> result = new WVPResult<>();
public WVPResult<List<MediaServerItem>> getMediaServerList(){
WVPResult<List<MediaServerItem>> result = new WVPResult<>();
result.setCode(0);
result.setMsg("success");
result.setData(mediaServerService.getAll());
return result;
}
@ApiOperation("在线流媒体服务列表")
@GetMapping(value = "/media_server/online/list")
@ResponseBody
public WVPResult<List<MediaServerItem>> getOnlineMediaServerList(){
WVPResult<List<MediaServerItem>> result = new WVPResult<>();
result.setCode(0);
result.setMsg("success");
result.setData(mediaServerService.getAllOnline());
return result;
}
@ApiOperation("获取流媒体服务")
@GetMapping(value = "/media_server/one/{id}")
@ResponseBody
public WVPResult<IMediaServerItem> getMediaServer(@PathVariable String id){
WVPResult<IMediaServerItem> result = new WVPResult<>();
public WVPResult<MediaServerItem> getMediaServer(@PathVariable String id){
WVPResult<MediaServerItem> result = new WVPResult<>();
result.setCode(0);
result.setMsg("success");
result.setData(mediaServerService.getOne(id));

4
src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java

@ -1,7 +1,6 @@
package com.genersoft.iot.vmp.vmanager.streamProxy;
import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.media.zlm.dto.IMediaServerItem;
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;
@ -9,7 +8,6 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.service.IStreamProxyService;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.github.pagehelper.PageInfo;
import io.netty.util.internal.StringUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
@ -86,7 +84,7 @@ public class StreamProxyController {
public WVPResult getFFmpegCMDs(@RequestParam String mediaServerId){
logger.debug("获取节点[ {} ]ffmpeg.cmd模板", mediaServerId );
IMediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
JSONObject data = streamProxyService.getFFmpegCMDs(mediaServerItem);
WVPResult<JSONObject> result = new WVPResult<>();
result.setCode(0);

7
src/main/resources/all-application.yml

@ -17,6 +17,12 @@ spring:
password:
# [可选] 超时时间
timeout: 10000
# [可选] 一个pool最多可分配多少个jedis实例
poolMaxTotal: 1000
# [可选] 一个pool最多有多少个状态为idle(空闲)的jedis实例
poolMaxIdle: 500
# [可选] 最大的等待时间(秒)
poolMaxWait: 5
# [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置
datasource:
# 使用mysql 打开23-28行注释, 删除29-36行
@ -124,6 +130,7 @@ logging:
level:
com.genersoft.iot: debug
com.genersoft.iot.vmp.storager.dao: info
com.genersoft.iot.vmp.gb28181: info
# [根据业务需求配置]
user-settings:
# [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true

2
src/main/resources/application-dev.yml

@ -76,6 +76,8 @@ logging:
level:
com.genersoft.iot: debug
com.genersoft.iot.vmp.storager.dao: info
com.genersoft.iot.vmp.gb28181: info
# [根据业务需求配置]
user-settings:
# 推流直播是否录制

BIN
src/main/resources/wvp.sqlite

Binary file not shown.

2
web_src/src/components/service/MediaServer.js

@ -9,7 +9,7 @@ class MediaServer{
getMediaServerList(callback){
this.$axios({
method: 'get',
url:`/api/server/media_server/list`,
url:`/api/server/media_server/online/list`,
}).then(function (res) {
if (typeof (callback) == "function") callback(res.data)
}).catch(function (error) {

Loading…
Cancel
Save