第一次提交

This commit is contained in:
wyw
2024-08-08 00:31:26 +08:00
commit c202e2b63d
1819 changed files with 221890 additions and 0 deletions

View File

@ -0,0 +1,29 @@
## GB28181 Roadmap
- 设备基础信息上报
- 物模型适配(属性,功能)
- 设备直播
- 设备录像拉取
- PTZ云台控制
- 设备自动拉流 云端录像
- tcp传输信令
- 添加事件处理机制
- srs m7s流媒体服务器支持
- 百度evs七牛云qvs等云平台api对接
## webhook
- on_http_access
- on_play
- on_publish
- on_stream_none_reader //StopPlay
- on_stream_not_found
- on_stream_changed
## zlm api
- url + "/index/api/getRtpInfo?secret=" + secret + "&stream_id=" + ssrc 获取流在zlm上的信息
- url + "/index/api/openRtpServer?secret=" + secret + "&port=0&enable_tcp=1&stream_id=" + streamId + "&ssrc=" + ssrc 创建GB28181 RTP接收端口
- url + "/index/api/closeRtpServer?secret=" + secret + "&stream_id=" + streamId 关闭GB28181 RTP接收端口
- url + "/index/api/listRtpServer?secret=" + secret 获取RTP服务器
- url + "/index/api/close_streams?secret=" + secret + "&stream=" + ssrc 关闭流
- url + "/index/api/getMediaList?secret=" + secret + "&stream=" + ssrc 获取流信息

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>fastbee-server</artifactId>
<groupId>com.fastbee</groupId>
<version>3.8.5</version>
</parent>
<artifactId>sip-server</artifactId>
<description>GB28181信令服务</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- ttl -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.14.0</version>
</dependency>
<!-- sip -->
<dependency>
<groupId>javax.sip</groupId>
<artifactId>jain-sip-ri</artifactId>
<version>1.3.0-91</version>
</dependency>
<!-- xml解析库 -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>org.apache.log4j</groupId>
<artifactId>com.springsource.org.apache.log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>com.fastbee</groupId>
<artifactId>fastbee-mqtt-client</artifactId>
</dependency>
<dependency>
<groupId>com.fastbee</groupId>
<artifactId>fastbee-oss</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,20 @@
package com.fastbee.sip.conf;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Setter
@Getter
@Component
@ConfigurationProperties(prefix = "sip")
public class SysSipConfig {
boolean enabled;
String ip;
Integer port;
String domain;
String id;
String password;
}

View File

@ -0,0 +1,32 @@
package com.fastbee.sip.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync(proxyTargetClass = true)
public class ThreadPoolTaskConfig {
public static final int cpuNum = Runtime.getRuntime().availableProcessors();
private static final int corePoolSize = cpuNum;
private static final int maxPoolSize = cpuNum*2;
private static final int keepAliveTime = 30;
private static final int queueCapacity = 10000;
private static final String threadNamePrefix = "sip-";
@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveTime);
executor.setThreadNamePrefix(threadNamePrefix);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}

View File

@ -0,0 +1,28 @@
package com.fastbee.sip.domain;
import lombok.Data;
/**
* @author bill
*/
@Data
public class BindingChannel {
/**
* 监控设备ID
*/
private String channelId;
/**
* 设备id
*/
private Long deviceId;
/**
* 场景id
*/
private Long sceneModelId;
private String deviceName;
private String sceneModelName;
}

View File

@ -0,0 +1,120 @@
package com.fastbee.sip.domain;
import com.fastbee.common.annotation.Excel;
import com.fastbee.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 流媒体服务器配置对象 media_server
*
* @author zhuangpeng.li
* @date 2022-11-30
*/
@ApiModel(value = "MediaServer", description = "流媒体服务器配置对象 media_server")
@Data
public class MediaServer extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 流媒体配置ID */
@ApiModelProperty("流媒体配置ID")
private Long id;
@ApiModelProperty("服务器标识")
private String serverId;
/** 租户ID */
@ApiModelProperty("租户ID")
@Excel(name = "租户ID")
private Long tenantId;
/** 租户名称 */
@ApiModelProperty("租户名称")
@Excel(name = "租户名称")
private String tenantName;
/** 是否系统通用0-否1-是) */
@ApiModelProperty(value = "是否系统通用", notes = "0-否1-是)")
@Excel(name = "是否系统通用", readConverterExp = "0=-否1-是")
private Integer isSys;
/** 使能开关 */
@ApiModelProperty("使能开关")
@Excel(name = "使能开关")
private Integer enabled;
/** 默认播放协议 */
@ApiModelProperty("默认播放协议")
@Excel(name = "默认播放协议")
private String protocol;
@ApiModelProperty("回调服务器地址")
@Excel(name = "回调服务器地址")
private String hookurl;
/** 服务器ip */
@ApiModelProperty("服务器ip")
@Excel(name = "服务器ip")
private String ip;
/** 服务器域名 */
@ApiModelProperty("服务器域名")
@Excel(name = "服务器域名")
private String domain;
/** 流媒体密钥 */
@ApiModelProperty("流媒体密钥")
@Excel(name = "流媒体密钥")
private String secret;
/** http端口 */
@ApiModelProperty("http端口")
@Excel(name = "http端口")
private Long portHttp;
/** ws端口 */
@ApiModelProperty("ws端口")
@Excel(name = "HTTPS端口")
private Long portHttps;
/** rtmp端口 */
@ApiModelProperty("rtmp端口")
@Excel(name = "rtmp端口")
private Long portRtmp;
/** rtsp端口 */
@ApiModelProperty("rtsp端口")
@Excel(name = "rtsp端口")
private Long portRtsp;
@ApiModelProperty("RTP收流端口")
@Excel(name = "RTP收流端口单端口模式有用")
private Long rtpProxyPort;
@ApiModelProperty("是否使用多端口模式")
@Excel(name = "是否使用多端口模式")
private Integer rtpEnable;
/** rtp端口范围 */
@ApiModelProperty("rtp端口范围")
@Excel(name = "rtp端口范围")
private String rtpPortRange;
@ApiModelProperty("是否自动同步配置ZLM")
@Excel(name = "是否自动同步配置ZLM")
private Integer autoConfig;
@ApiModelProperty("状态")
@Excel(name = "状态")
private Integer status;
@ApiModelProperty("录像服务端口")
@Excel(name = "录像服务端口")
private Long recordPort;
/** 删除标志0代表存在 2代表删除 */
@ApiModelProperty("删除标志")
private String delFlag;
}

View File

@ -0,0 +1,95 @@
package com.fastbee.sip.domain;
import com.fastbee.common.annotation.Excel;
import com.fastbee.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* sip系统配置对象 sip_config
*
* @author zhuangpeng.li
* @date 2023-02-21
*/
@ApiModel(value = "SipConfig", description = "sip系统配置对象 sip_config")
@Data
public class SipConfig extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 配置ID */
@ApiModelProperty("配置ID")
private Long id;
/** 是否系统通用0-否1-是) */
@ApiModelProperty(value = "是否系统通用", notes = "0-否1-是)")
@Excel(name = "是否系统通用", readConverterExp = "0=-否1-是")
private Integer isSys;
/** 产品ID */
@ApiModelProperty("产品ID")
@Excel(name = "产品ID")
private Long productId;
/**
* 产品名称
*/
@ApiModelProperty("产品名称")
@Excel(name = "产品名称")
private String productName;
/**
* 使能开关
*/
@ApiModelProperty("使能开关")
@Excel(name = "使能开关")
private Integer enabled;
/**
* 系统默认配置
*/
@ApiModelProperty("系统默认配置")
@Excel(name = "系统默认配置")
private Integer isdefault;
/**
* 拓展sdp
*/
@ApiModelProperty("拓展sdp")
@Excel(name = "拓展sdp")
private Integer seniorsdp;
/**
* 服务器域
*/
@ApiModelProperty("服务器域")
@Excel(name = "服务器域")
private String domain;
/**
* 服务器sipid
*/
@ApiModelProperty("服务器sipid")
@Excel(name = "服务器sipid")
private String serverSipid;
/** sip认证密码 */
@ApiModelProperty("sip认证密码")
@Excel(name = "sip认证密码")
private String password;
/** sip接入IP */
@ApiModelProperty("sip接入IP")
@Excel(name = "sip接入IP")
private String ip;
/** sip接入端口号 */
@ApiModelProperty("sip接入端口号")
@Excel(name = "sip接入端口号")
private Integer port;
/** 删除标志0代表存在 2代表删除 */
@ApiModelProperty("删除标志")
private String delFlag;
}

View File

@ -0,0 +1,310 @@
package com.fastbee.sip.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fastbee.common.annotation.Excel;
import com.fastbee.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.Date;
/**
* 监控设备对象 sip_device
*
* @author zhuangpeng.li
* @date 2023-02-24
*/
@ApiModel(value = "SipDevice", description = "监控设备对象 sip_device")
public class SipDevice extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 设备ID */
@ApiModelProperty("设备ID")
private Long deviceId;
/** 产品ID */
@ApiModelProperty("产品ID")
@Excel(name = "产品ID")
private Long productId;
/** 产品名称 */
@ApiModelProperty("产品名称")
@Excel(name = "产品名称")
private String productName;
/** 设备SipID */
@ApiModelProperty("设备SipID")
@Excel(name = "设备SipID")
private String deviceSipId;
/** 设备名称 */
@ApiModelProperty("设备名称")
@Excel(name = "设备名称")
private String deviceName;
/** 厂商名称 */
@ApiModelProperty("厂商名称")
@Excel(name = "厂商名称")
private String manufacturer;
/** 产品型号 */
@ApiModelProperty("产品型号")
@Excel(name = "产品型号")
private String model;
/** 固件版本 */
@ApiModelProperty("固件版本")
@Excel(name = "固件版本")
private String firmware;
/** 传输模式 */
@ApiModelProperty("传输模式")
@Excel(name = "传输模式")
private String transport;
/** 流模式 */
@ApiModelProperty("流模式")
@Excel(name = "流模式")
private String streammode;
/** 在线状态 */
@ApiModelProperty("在线状态")
@Excel(name = "在线状态")
private String online;
/** 注册时间 */
@ApiModelProperty("注册时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "注册时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date registertime;
/** 最后上线时间 */
@ApiModelProperty("最后上线时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "最后上线时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date lastconnecttime;
/** 激活时间 */
@ApiModelProperty("激活时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "激活时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date activeTime;
/** 设备入网IP */
@ApiModelProperty("设备入网IP")
@Excel(name = "设备入网IP")
private String ip;
/** 设备接入端口号 */
@ApiModelProperty("设备接入端口号")
@Excel(name = "设备接入端口号")
private Integer port;
/** 设备地址 */
@ApiModelProperty("设备地址")
@Excel(name = "设备地址")
private String hostaddress;
/** 删除标志0代表存在 2代表删除 */
@ApiModelProperty("删除标志")
private String delFlag;
public void setDeviceId(Long deviceId)
{
this.deviceId = deviceId;
}
public Long getDeviceId()
{
return deviceId;
}
public void setProductId(Long productId)
{
this.productId = productId;
}
public Long getProductId()
{
return productId;
}
public void setProductName(String productName)
{
this.productName = productName;
}
public String getProductName()
{
return productName;
}
public void setDeviceSipId(String deviceSipId)
{
this.deviceSipId = deviceSipId;
}
public String getDeviceSipId()
{
return deviceSipId;
}
public void setDeviceName(String deviceName)
{
this.deviceName = deviceName;
}
public String getDeviceName()
{
return deviceName;
}
public void setManufacturer(String manufacturer)
{
this.manufacturer = manufacturer;
}
public String getManufacturer()
{
return manufacturer;
}
public void setModel(String model)
{
this.model = model;
}
public String getModel()
{
return model;
}
public void setFirmware(String firmware)
{
this.firmware = firmware;
}
public String getFirmware()
{
return firmware;
}
public void setTransport(String transport)
{
this.transport = transport;
}
public String getTransport()
{
return transport;
}
public void setStreammode(String streammode)
{
this.streammode = streammode;
}
public String getStreammode()
{
return streammode;
}
public void setOnline(String online)
{
this.online = online;
}
public String getOnline()
{
return online;
}
public void setRegistertime(Date registertime)
{
this.registertime = registertime;
}
public Date getRegistertime()
{
return registertime;
}
public void setLastconnecttime(Date lastconnecttime)
{
this.lastconnecttime = lastconnecttime;
}
public Date getLastconnecttime()
{
return lastconnecttime;
}
public void setActiveTime(Date activeTime)
{
this.activeTime = activeTime;
}
public Date getActiveTime()
{
return activeTime;
}
public void setIp(String ip)
{
this.ip = ip;
}
public String getIp()
{
return ip;
}
public void setPort(Integer port)
{
this.port = port;
}
public Integer getPort()
{
return port;
}
public void setHostaddress(String hostaddress)
{
this.hostaddress = hostaddress;
}
public String getHostaddress()
{
return hostaddress;
}
public void setDelFlag(String delFlag)
{
this.delFlag = delFlag;
}
public String getDelFlag()
{
return delFlag;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("deviceId", getDeviceId())
.append("productId", getProductId())
.append("productName", getProductName())
.append("deviceSipId", getDeviceSipId())
.append("deviceName", getDeviceName())
.append("manufacturer", getManufacturer())
.append("model", getModel())
.append("firmware", getFirmware())
.append("transport", getTransport())
.append("streammode", getStreammode())
.append("online", getOnline())
.append("registertime", getRegistertime())
.append("lastconnecttime", getLastconnecttime())
.append("activeTime", getActiveTime())
.append("ip", getIp())
.append("port", getPort())
.append("hostaddress", getHostaddress())
.append("delFlag", getDelFlag())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
public String getHostAndPort() {
return getIp() + ":" + getPort();
}
}

View File

@ -0,0 +1,291 @@
package com.fastbee.sip.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fastbee.common.annotation.Excel;
import com.fastbee.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 监控设备通道信息对象 sip_device_channel
*
* @author zhuangpeng.li
* @date 2023-02-23
*/
@ApiModel(value = "SipDeviceChannel", description = "监控设备通道信息对象 sip_device_channel")
@Data
public class SipDeviceChannel extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@ApiModelProperty("ID")
private Long id;
/**
* 租户ID
*/
@ApiModelProperty("租户ID")
@Excel(name = "租户ID")
private Long tenantId;
/**
* 租户名称
*/
@ApiModelProperty("租户名称")
@Excel(name = "租户名称")
private String tenantName;
/**
* 产品ID
*/
@ApiModelProperty("产品ID")
@Excel(name = "产品ID")
private Long productId;
/**
* 产品名称
*/
@ApiModelProperty("产品名称")
@Excel(name = "产品名称")
private String productName;
/**
* 产品ID
*/
@ApiModelProperty("产品ID")
@Excel(name = "产品ID")
private Long userId;
/**
* 产品名称
*/
@ApiModelProperty("产品名称")
@Excel(name = "产品名称")
private String userName;
/**
* 设备SipID
*/
@ApiModelProperty("设备SipID")
private String deviceSipId;
/**
* 通道SipID
*/
@ApiModelProperty("通道SipID")
@Excel(name = "通道SipID")
private String channelSipId;
/**
* 通道名称
*/
@ApiModelProperty("通道名称")
@Excel(name = "通道名称")
private String channelName;
/**
* 注册时间
*/
@ApiModelProperty("注册时间")
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "注册时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date registerTime;
/**
* 设备类型
*/
@ApiModelProperty("设备类型")
@Excel(name = "设备类型")
private String deviceType;
/**
* 通道类型
*/
@ApiModelProperty("通道类型")
@Excel(name = "通道类型")
private String channelType;
/**
* 城市编码
*/
@ApiModelProperty("城市编码")
@Excel(name = "城市编码")
private String citycode;
/**
* 行政区域
*/
@ApiModelProperty("行政区域")
@Excel(name = "行政区域")
private String civilcode;
/**
* 厂商名称
*/
@ApiModelProperty("厂商名称")
@Excel(name = "厂商名称")
private String manufacture;
/**
* 产品型号
*/
@ApiModelProperty("产品型号")
@Excel(name = "产品型号")
private String model;
/**
* 设备归属
*/
@ApiModelProperty("设备归属")
@Excel(name = "设备归属")
private String owner;
/**
* 警区
*/
@ApiModelProperty("警区")
@Excel(name = "警区")
private String block;
/**
* 安装地址
*/
@ApiModelProperty("安装地址")
@Excel(name = "安装地址")
private String address;
/** 父级id */
@ApiModelProperty("父级id")
@Excel(name = "父级id")
private String parentid;
/** 设备入网IP */
@ApiModelProperty("设备入网IP")
@Excel(name = "设备入网IP")
private String ipaddress;
/** 设备接入端口号 */
@ApiModelProperty("设备接入端口号")
@Excel(name = "设备接入端口号")
private Integer port;
/** 密码 */
@ApiModelProperty("密码")
@Excel(name = "密码")
private String password;
/** PTZ类型 */
@ApiModelProperty("PTZ类型")
@Excel(name = "PTZ类型")
private Long ptztype;
/** PTZ类型描述字符串 */
@ApiModelProperty("PTZ类型描述字符串")
@Excel(name = "PTZ类型描述字符串")
private String ptztypetext;
/**
* 设备状态1=-未使用2-禁用3-在线4-离线)
*/
@ApiModelProperty("设备状态1=-未使用2-禁用3-在线4-离线)")
@Excel(name = "设备状态", readConverterExp = "1=-未使用2-禁用3-在线4-离线")
private Integer status;
/**
* 推流状态1-未使用2-录像中)
*/
@ApiModelProperty("推流状态1-未使用2-录像中)")
@Excel(name = "推流状态", readConverterExp = "1-未使用2-录像中")
private Integer streamPush;
/**
* 直播录像状态1=-未使用2-录像中)
*/
@ApiModelProperty("直播录像状态1-未使用2-录像中)")
@Excel(name = "直播录像状态", readConverterExp = "1-未使用2-录像中")
private Integer streamRecord;
/**
* 录像转存状态1-未使用2-录像中)
*/
@ApiModelProperty("录像转存状态1-未使用2-录像中)")
@Excel(name = "录像转存状态", readConverterExp = "1-未使用2-录像中")
private Integer videoRecord;
/**
* 设备经度
*/
@ApiModelProperty("设备经度")
@Excel(name = "设备经度")
private BigDecimal longitude;
/**
* 设备纬度
*/
@ApiModelProperty("设备纬度")
@Excel(name = "设备纬度")
private BigDecimal latitude;
/**
* 流媒体ID
*/
@ApiModelProperty("流媒体ID")
@Excel(name = "流媒体ID")
private String streamid;
/**
* 子设备数
*/
@ApiModelProperty("子设备数")
@Excel(name = "子设备数")
private Long subcount;
/**
* 是否有子设备1-有, 0-没有)
*/
@ApiModelProperty("是否有子设备1-有, 0-没有)")
@Excel(name = "是否有子设备", readConverterExp = "1=-有,,0=-没有")
private Integer parental;
/**
* 是否含有音频1-有, 0-没有)
*/
@ApiModelProperty("是否含有音频1-有, 0-没有)")
@Excel(name = "是否含有音频", readConverterExp = "1=-有,,0=-没有")
private Integer hasaudio;
/**
* 删除标志0代表存在 2代表删除
*/
@ApiModelProperty("删除标志0代表存在 2代表删除")
private String delFlag;
@ApiModelProperty("关联信息")
private BindingChannel bindingChannel;
/**
* 设备id
*/
@ApiModelProperty("关联设备id")
private Long reDeviceId;
/**
* 直播流
*/
private String playUrl;
/**
* 场景id
*/
@ApiModelProperty("关联场景id")
private Long reSceneModelId;
@ApiModelProperty("关联设备名称")
private String reDeviceName;
@ApiModelProperty("关联场景名称")
private String reSceneModelName;
}

View File

@ -0,0 +1,20 @@
package com.fastbee.sip.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum AlarmMethod {
unknown("0", "未知"),
telAlarm("1", "电话报警"),
devAlarm("2", "设备报警"),
smsAlarm("3", "短信报警"),
gpsAlarm("4", "GPS报警"),
videoAlarm("5", "视频报警"),
devErrorAlarm("6", "设备故障报警"),
other("7", "其他报警");
private final String value;
private final String text;
}

View File

@ -0,0 +1,61 @@
package com.fastbee.sip.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@AllArgsConstructor
@Getter
public enum AlarmType {
//设备报警
videoLost("1", "视频丢失报警", AlarmMethod.devAlarm),
videoBroken("2", "设备防拆报警", AlarmMethod.devAlarm),
diskFull("3", "存储设备磁盘满报警", AlarmMethod.devAlarm),
hot("4", "设备高温报警", AlarmMethod.devAlarm),
cold("5", "设备低温报警", AlarmMethod.devAlarm),
//视频报警
manual("1", "人工视频报警", AlarmMethod.videoAlarm),
moving("2", "运动目标检测报警", AlarmMethod.videoAlarm),
residual("3", "遗留物检测报警", AlarmMethod.videoAlarm),
remove("4", "物体移除检测报警", AlarmMethod.videoAlarm),
tripLine("5", "绊线检测报警", AlarmMethod.videoAlarm),
intrusion("6", "入侵检测报警", AlarmMethod.videoAlarm),
retrograde("7", "逆行检测报警", AlarmMethod.videoAlarm),
wandering("8", "徘徊检测报警", AlarmMethod.videoAlarm),
density("9", "密度检测报警", AlarmMethod.videoAlarm),
error("10", "视频异常检测报警", AlarmMethod.videoAlarm),
fastMoving("11", "快速移动报警", AlarmMethod.videoAlarm),
//存储设备
storageDiskError("1", "存储设备磁盘故障报警", AlarmMethod.devErrorAlarm),
storageFanError("2", "存储设备风扇故障报警", AlarmMethod.devErrorAlarm);
private final String code;
private final String text;
private final AlarmMethod method;
private static final Map<AlarmMethod, Map<String, AlarmType>> fastMap = new EnumMap<>(AlarmMethod.class);
static {
for (AlarmType value : AlarmType.values()) {
fastMap.computeIfAbsent(value.method, (ignore) -> new HashMap<>())
.put(value.code, value);
}
}
public static Optional<AlarmType> of(AlarmMethod method, String code) {
Map<String, AlarmType> mapping = fastMap.get(method);
if (mapping == null) {
return Optional.empty();
}
return Optional.ofNullable(mapping.get(code));
}
}

View File

@ -0,0 +1,22 @@
package com.fastbee.sip.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum ChannelStatus {
online("ON", "在线"),
lost("VLOST", "视频丢失"),
defect("DEFECT", "故障"),
add("ADD", "新增"),
delete("DEL", "删除"),
update("UPDATE", "更新"),
offline("OFF", "离线"),
;
private final String code;
private final String text;
}

View File

@ -0,0 +1,5 @@
package com.fastbee.sip.enums;
public enum ChannelType{
CivilCode, BusinessGroup,VirtualOrganization,Other
}

View File

@ -0,0 +1,15 @@
package com.fastbee.sip.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum DeviceChannelStatus {
//1=-未使用2-禁用3-在线4-离线
notused(1),
off(2),
online(3),
offline(4);
private final Integer value;
}

View File

@ -0,0 +1,51 @@
package com.fastbee.sip.enums;
import lombok.AllArgsConstructor;
import java.util.Arrays;
@AllArgsConstructor
public enum Direct {
UP(0x08),
DOWN(0x04),
LEFT(0x02),
RIGHT(0x01),
ZOOM_IN(0x10),
ZOOM_OUT(0x20),
STOP(0) {
@Override
public int merge(int code) {
return code;
}
@Override
public boolean match(int code) {
return code == 0;
}
};
private final int code;
public int merge(int code) {
return code | this.code;
}
public boolean match(int code) {
return (code & this.code) != 0;
}
public static Direct[] values(int code) {
return Arrays
.stream(values())
.filter(direct -> direct.match(code))
.toArray(Direct[]::new);
}
public static Direct[] values(String code) {
String[] codes = code.toUpperCase().split(",");
return Arrays
.stream(codes)
.map(Direct::valueOf)
.toArray(Direct[]::new);
}
}

View File

@ -0,0 +1,25 @@
package com.fastbee.sip.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
@AllArgsConstructor
@Getter
public enum FunctionType {
VIDEOPUSH("video_push", "设备推流"),
AUDIOBROADCAST("audio_broadcast", "语音广播"),
OTHER("other", "其他");
private final String value;
private final String text;
public static FunctionType fromType(String Type) {
for (FunctionType type : FunctionType.values()) {
if (Objects.equals(type.getValue(), Type)) {
return type;
}
}
return null;
}
}

View File

@ -0,0 +1,17 @@
package com.fastbee.sip.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum PTZCmd {
out(32),
in(16),
up(8),
down(4),
left(2),
right(1);
private final Integer value;
}

View File

@ -0,0 +1,17 @@
package com.fastbee.sip.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum PTZType {
unknown(0, "未知"),
ball(1, "球机"),
hemisphere(2, "半球机"),
fixed(3, "固定抢机"),
remoteControl(4, "遥控抢机");
private final Integer value;
private final String text;
}

View File

@ -0,0 +1,8 @@
package com.fastbee.sip.enums;
public enum SessionType {
play,
playrecord,
playback,
download
}

View File

@ -0,0 +1,7 @@
package com.fastbee.sip.handler;
import javax.sip.RequestEvent;
public interface IReqHandler {
public void processMsg(RequestEvent evt);
}

View File

@ -0,0 +1,8 @@
package com.fastbee.sip.handler;
import javax.sip.ResponseEvent;
import java.text.ParseException;
public interface IResHandler {
public void processMsg(ResponseEvent evt) throws ParseException;
}

View File

@ -0,0 +1,36 @@
package com.fastbee.sip.handler.req;
import com.fastbee.sip.handler.IReqHandler;
import com.fastbee.sip.server.IGBListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import java.text.ParseException;
@Slf4j
@Component
public class AckReqHandler extends ReqAbstractHandler implements InitializingBean, IReqHandler {
@Autowired
private IGBListener sipListener;
@Override
public void processMsg(RequestEvent evt) {
log.info("接收到ACK信息");
try {
responseAck(evt);
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[回复ACK信息失败]{}", e.getMessage());
}
}
@Override
public void afterPropertiesSet() throws Exception {
String method = "ACK";
sipListener.addRequestProcessor(method, this);
}
}

View File

@ -0,0 +1,38 @@
package com.fastbee.sip.handler.req;
import com.fastbee.sip.handler.IReqHandler;
import com.fastbee.sip.server.IGBListener;
import gov.nist.javax.sip.message.SIPRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import javax.sip.message.Response;
import java.text.ParseException;
@Slf4j
@Component
public class ByeReqHandler extends ReqAbstractHandler implements InitializingBean, IReqHandler {
@Autowired
private IGBListener sipListener;
@Override
public void processMsg(RequestEvent evt) {
log.info("接收到BYE信息");
try {
responseAck(evt);
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[回复BYE信息失败]{}", e.getMessage());
}
}
@Override
public void afterPropertiesSet() throws Exception {
String method = "BYE";
sipListener.addRequestProcessor(method, this);
}
}

View File

@ -0,0 +1,36 @@
package com.fastbee.sip.handler.req;
import com.fastbee.sip.handler.IReqHandler;
import com.fastbee.sip.server.IGBListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import java.text.ParseException;
@Slf4j
@Component
public class CancelReqHandler extends ReqAbstractHandler implements InitializingBean, IReqHandler {
@Autowired
private IGBListener sipListener;
@Override
public void processMsg(RequestEvent evt) {
log.info("接收到CANCEL信息");
try {
responseAck(evt);
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[回复CANCEL信息失败]{}", e.getMessage());
}
}
@Override
public void afterPropertiesSet() throws Exception {
String method = "CANCEL";
sipListener.addRequestProcessor(method, this);
}
}

View File

@ -0,0 +1,36 @@
package com.fastbee.sip.handler.req;
import com.fastbee.sip.handler.IReqHandler;
import com.fastbee.sip.server.IGBListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import java.text.ParseException;
@Slf4j
@Component
public class InviteReqHandler extends ReqAbstractHandler implements InitializingBean, IReqHandler {
@Autowired
private IGBListener sipListener;
@Override
public void processMsg(RequestEvent evt) {
log.info("接收到INVITE信息");
try {
responseAck(evt);
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[回复INVITE信息失败]{}", e.getMessage());
}
}
@Override
public void afterPropertiesSet() throws Exception {
String method = "INVITE";
sipListener.addRequestProcessor(method, this);
}
}

View File

@ -0,0 +1,199 @@
package com.fastbee.sip.handler.req;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.iot.domain.Device;
import com.fastbee.iot.service.IDeviceService;
import com.fastbee.sip.conf.SysSipConfig;
import com.fastbee.sip.domain.SipConfig;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.domain.SipDeviceChannel;
import com.fastbee.sip.handler.IReqHandler;
import com.fastbee.sip.model.SipDate;
import com.fastbee.sip.server.IGBListener;
import com.fastbee.sip.server.MessageInvoker;
import com.fastbee.sip.service.IMqttService;
import com.fastbee.sip.service.ISipConfigService;
import com.fastbee.sip.service.ISipDeviceChannelService;
import com.fastbee.sip.service.ISipDeviceService;
import com.fastbee.sip.util.DigestAuthUtil;
import gov.nist.javax.sip.address.AddressImpl;
import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.header.Expires;
import gov.nist.javax.sip.header.SIPDateHeader;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import javax.sip.header.*;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
@Slf4j
@Component
public class RegisterReqHandler extends ReqAbstractHandler implements InitializingBean, IReqHandler {
@Autowired
private ISipDeviceService sipDeviceService;
@Autowired
private ISipDeviceChannelService sipDeviceChannelService;
@Autowired
private IDeviceService deviceService;
@Autowired
private ISipConfigService sipConfigService;
@Autowired
private IGBListener sipListener;
@Autowired
private MessageInvoker messageInvoker;
@Autowired
private SysSipConfig sysSipConfig;
@Autowired
private IMqttService mqttService;
@Override
public void processMsg(RequestEvent evt) {
try {
log.info("收到注册请求,开始处理");
Request request = evt.getRequest();
Response response;
// 注册标志 0未携带授权头或者密码错误 1注册成功 2注销成功
int registerFlag;
FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
AddressImpl address = (AddressImpl) fromHeader.getAddress();
SipUri uri = (SipUri) address.getURI();
String sipId = uri.getUser();
//取默认Sip配置
SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(sipId);
if (sipConfig != null) {
AuthorizationHeader authorhead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
// 校验密码是否正确
if (authorhead == null && !ObjectUtils.isEmpty(sipConfig.getPassword())) {
log.info("未携带授权头 回复401sipId"+ sipId);
response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
new DigestAuthUtil().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain());
getServerTransaction(evt).sendResponse(response);
return;
}
boolean pcheck = new DigestAuthUtil().doAuthenticatePlainTextPassword(request,
sipConfig.getPassword());
boolean syscheck = new DigestAuthUtil().doAuthenticatePlainTextPassword(request,
sysSipConfig.getPassword());
if (!pcheck && !syscheck) {
// 注册失败
response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
response.setReasonPhrase("wrong password");
log.info("[注册请求] 密码/SIP服务器ID错误, 回复403 sipId" + sipId);
getServerTransaction(evt).sendResponse(response);
return;
}
response = getMessageFactory().createResponse(Response.OK, request);
// 添加date头
SIPDateHeader dateHeader = new SIPDateHeader();
// 使用自己修改的
SipDate sipDate = new SipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
dateHeader.setDate(sipDate);
response.addHeader(dateHeader);
ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
// 添加Contact头
response.addHeader(request.getHeader(ContactHeader.NAME));
// 添加Expires头
response.addHeader(request.getExpires());
ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
String received = viaHeader.getReceived();
int rPort = viaHeader.getRPort();
// 本地模拟设备 received 为空 rPort 为 -1
// 解析本地地址替代
if (StringUtils.isEmpty(received) || rPort == -1) {
log.warn("本地地址替代! received:{},rPort:{} [{}:{}]", received, rPort, viaHeader.getHost(), viaHeader.getPort());
received = viaHeader.getHost();
rPort = viaHeader.getPort();
}
SipDevice device = new SipDevice();
;
device.setStreammode("UDP");
device.setDeviceSipId(sipId);
device.setIp(received);
device.setPort(rPort);
device.setHostaddress(received.concat(":").concat(String.valueOf(rPort)));
// 注销成功
if (expiresHeader != null && expiresHeader.getExpires() == 0) {
registerFlag = 2;
} else {
registerFlag = 1;
// 判断TCP还是UDP
boolean isTcp = false;
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
String transport = reqViaHeader.getTransport();
if (transport.equals("TCP")) {
isTcp = true;
}
device.setTransport(isTcp ? "TCP" : "UDP");
}
getServerTransaction(evt).sendResponse(response);
// 注册成功
if (registerFlag == 1) {
log.info("注册成功! sipId:" + device.getDeviceSipId());
device.setRegistertime(DateUtils.getNowDate());
sipDeviceService.updateDevice(device);
List<SipDeviceChannel> channels = sipDeviceChannelService.selectSipDeviceChannelByDeviceSipId(device.getDeviceSipId());
if (channels.size() > 0) {
Device iotdev = deviceService.selectDeviceBySerialNumber(device.getDeviceSipId());
if (iotdev == null) {
//添加到统一设备管理 默认添加到admin账号下
int result = deviceService.insertDeviceAuto(device.getDeviceSipId(), 1L, sipConfig.getProductId());
if (result == 1) {
log.info("-----------sip设备认证成功,并自动添加设备到系统SipId:" + device.getDeviceSipId() + "---------------");
}
iotdev = new Device();
}
if (iotdev.getStatus() != 3 && iotdev.getStatus() != 2) {
iotdev.setStatus(3);
deviceService.updateDeviceStatusAndLocation(iotdev,device.getIp());
}
// 重新注册更新设备和通道,以免设备替换或更新后信息无法更新
onRegister(device);
} else {
log.info("未找到改设备! deviceId:" + device.getDeviceId());
}
} else if (registerFlag == 2) {
log.info("注销成功! deviceId:" + device.getDeviceId());
mqttService.publishStatus(device, 4);
}
}
} catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) {
e.printStackTrace();
}
}
public void onRegister(SipDevice device) {
// TODO 查询设备信息和下挂设备信息 自动拉流
messageInvoker.deviceInfoQuery(device);
messageInvoker.catalogQuery(device);
messageInvoker.subscribe(device,0,0,3600,"0",null,null);
}
@Override
public void afterPropertiesSet() throws Exception {
String method = "REGISTER";
sipListener.addRequestProcessor(method, this);
}
}

View File

@ -0,0 +1,162 @@
package com.fastbee.sip.handler.req;
import gov.nist.javax.sip.SipStackImpl;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;
import gov.nist.javax.sip.stack.SIPServerTransaction;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.sip.*;
import javax.sip.header.FromHeader;
import javax.sip.header.HeaderFactory;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Slf4j
public abstract class ReqAbstractHandler {
@Autowired
@Qualifier(value = "udpSipServer")
private SipProvider udpSipServer;
public ServerTransaction getServerTransaction(RequestEvent evt) {
Request request = evt.getRequest();
ServerTransaction serverTransaction = evt.getServerTransaction();
// 判断TCP还是UDP
boolean isTcp = false;
ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
String transport = reqViaHeader.getTransport();
if (transport.equals("TCP")) {
isTcp = true;
}
if (serverTransaction == null) {
try {
if (!isTcp) {
SipStackImpl stack = (SipStackImpl)udpSipServer.getSipStack();
serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
if (serverTransaction == null) {
serverTransaction = udpSipServer.getNewServerTransaction(request);
}
}
} catch (TransactionAlreadyExistsException | TransactionUnavailableException e) {
e.printStackTrace();
}
}
return serverTransaction;
}
public HeaderFactory getHeaderFactory() {
try {
return SipFactory.getInstance().createHeaderFactory();
} catch (PeerUnavailableException e) {
e.printStackTrace();
}
return null;
}
public MessageFactory getMessageFactory() {
try {
return SipFactory.getInstance().createMessageFactory();
} catch (PeerUnavailableException e) {
e.printStackTrace();
}
return null;
}
public void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException {
Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
getServerTransaction(evt).sendResponse(response);
}
public SIPResponse responseAck(ServerTransaction serverTransaction, int statusCode) throws SipException, InvalidArgumentException, ParseException {
return responseAck(serverTransaction, statusCode, null);
}
public SIPResponse responseAck(ServerTransaction serverTransaction, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException {
ToHeader toHeader = (ToHeader) serverTransaction.getRequest().getHeader(ToHeader.NAME);
if (toHeader.getTag() == null) {
toHeader.setTag(String.valueOf(System.currentTimeMillis()));
}
SIPResponse response = (SIPResponse)getMessageFactory().createResponse(statusCode, serverTransaction.getRequest());
if (msg != null) {
response.setReasonPhrase(msg);
}
serverTransaction.sendResponse(response);
if (statusCode >= 200 && !"NOTIFY".equalsIgnoreCase(serverTransaction.getRequest().getMethod())) {
if (serverTransaction.getDialog() != null) {
serverTransaction.getDialog().delete();
}
}
return response;
}
public Element getRootElement(RequestEvent evt) throws DocumentException {
return getRootElement(evt, "gb2312");
}
public Element getRootElement(RequestEvent evt, String charset) throws DocumentException {
if (charset == null) {
charset = "gb2312";
}
Request request = evt.getRequest();
SAXReader reader = new SAXReader();
reader.setEncoding(charset);
// 对海康出现的未转义字符做处理。
String[] destStrArray = new String[]{"&lt;","&gt;","&amp;","&apos;","&quot;"};
char despChar = '&'; // 或许可扩展兼容其他字符
byte destBye = (byte) despChar;
List<Byte> result = new ArrayList<>();
byte[] rawContent = request.getRawContent();
if (rawContent == null) {
FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
log.error("rawContent is null,from:{} length:{}",fromHeader.toString(),request.getContentLength());
return null;
}
for (int i = 0; i < rawContent.length; i++) {
if (rawContent[i] == destBye) {
boolean resul = false;
for (String destStr : destStrArray) {
if (i + destStr.length() <= rawContent.length) {
byte[] bytes = Arrays.copyOfRange(rawContent, i, i + destStr.length());
resul = resul || (Arrays.equals(bytes,destStr.getBytes()));
}
}
if (resul) {
result.add(rawContent[i]);
}
} else {
result.add(rawContent[i]);
}
}
Byte[] bytes = new Byte[0];
byte[] bytesResult = ArrayUtils.toPrimitive(result.toArray(bytes));
String content = new String(bytesResult);
//过滤掉非法字符
String ret = XMLChars(content);
Document xml = reader.read(new ByteArrayInputStream(bytesResult));
return xml.getRootElement();
}
public String XMLChars(String s) {
if (s == null || "".equals(s)) {
return s;
}
return s.replaceAll("[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]", "");
}
}

View File

@ -0,0 +1,17 @@
package com.fastbee.sip.handler.req;
import com.fastbee.sip.handler.IReqHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.sip.RequestEvent;
@Slf4j
@Component
public class UnknowReqHandler extends ReqAbstractHandler implements IReqHandler {
@Override
public void processMsg(RequestEvent evt) {
log.warn("Unknow the method! Method:" + evt.getRequest().getMethod());
}
}

View File

@ -0,0 +1,15 @@
package com.fastbee.sip.handler.req.message;
import com.fastbee.sip.domain.SipDevice;
import org.dom4j.Element;
import javax.sip.RequestEvent;
public interface IMessageHandler {
/**
* 处理来自设备的信息
* @param evt
* @param device
*/
void handlerCmdType(RequestEvent evt, SipDevice device, Element element);
}

View File

@ -0,0 +1,27 @@
package com.fastbee.sip.handler.req.message;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.util.XmlUtil;
import org.dom4j.Element;
import javax.sip.RequestEvent;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public abstract class MessageHandlerAbstract implements IMessageHandler {
public Map<String, IMessageHandler> messageHandlerMap = new ConcurrentHashMap<>();
public void addHandler(String cmdType, IMessageHandler messageHandler) {
messageHandlerMap.put(cmdType, messageHandler);
}
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
String cmd = XmlUtil.getText(element, "CmdType");
IMessageHandler messageHandler = messageHandlerMap.get(cmd);
if (messageHandler != null) {
messageHandler.handlerCmdType(evt, device, element);
}
}
}

View File

@ -0,0 +1,92 @@
package com.fastbee.sip.handler.req.message;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.IReqHandler;
import com.fastbee.sip.handler.req.ReqAbstractHandler;
import com.fastbee.sip.server.IGBListener;
import com.fastbee.sip.service.ISipDeviceService;
import com.fastbee.sip.util.SipUtil;
import gov.nist.javax.sip.message.SIPRequest;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.message.Response;
import java.text.ParseException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class MessageRequestProcessor extends ReqAbstractHandler implements InitializingBean, IReqHandler {
@Autowired
private IGBListener sipListener;
@Autowired
private ISipDeviceService sipDeviceService;
private static Map<String, IMessageHandler> messageHandlerMap = new ConcurrentHashMap<>();
public void addHandler(String name, IMessageHandler handler) {
messageHandlerMap.put(name, handler);
}
@Override
public void afterPropertiesSet() throws Exception {
String method = "MESSAGE";
sipListener.addRequestProcessor(method, this);
}
@Override
public void processMsg(RequestEvent evt) {
SIPRequest sipRequest = (SIPRequest)evt.getRequest();
String deviceId = SipUtil.getUserIdFromFromHeader(sipRequest);
ServerTransaction serverTransaction = getServerTransaction(evt);
// 查询设备是否存在
SipDevice device = sipDeviceService.selectSipDeviceBySipId(deviceId);
try {
if (device == null ) {
// 不存在则回复404
responseAck(serverTransaction, Response.NOT_FOUND, "device "+ deviceId +" not found");
log.warn("[设备未找到 ] {}", deviceId);
} else {
Element rootElement = null;
try {
rootElement = getRootElement(evt);
if (rootElement == null) {
log.error("处理MESSAGE请求 未获取到消息体{}", evt.getRequest());
responseAck(serverTransaction, Response.BAD_REQUEST, "content is null");
return;
}
} catch (DocumentException e) {
log.warn("解析XML消息内容异常", e);
// 不存在则回复404
responseAck(serverTransaction, Response.BAD_REQUEST, e.getMessage());
}
assert rootElement != null;
String name = rootElement.getName();
//log.debug("接收到消息:" + evt.getRequest());
IMessageHandler messageHandler = messageHandlerMap.get(name);
if ( messageHandler != null) {
messageHandler.handlerCmdType(evt, device, rootElement);
} else {
// 不支持的message
// 不存在则回复415
responseAck(serverTransaction, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response");
}
}
} catch (SipException e) {
log.warn("SIP 回复错误", e);
} catch (InvalidArgumentException e) {
log.warn("参数无效", e);
} catch (ParseException e) {
log.warn("SIP回复时解析异常", e);
}
}
}

View File

@ -0,0 +1,20 @@
package com.fastbee.sip.handler.req.message.notify;
import com.fastbee.sip.handler.req.message.MessageHandlerAbstract;
import com.fastbee.sip.handler.req.message.MessageRequestProcessor;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class NotifyMessageHandler extends MessageHandlerAbstract implements InitializingBean {
@Autowired
private MessageRequestProcessor messageRequestProcessor;
@Override
public void afterPropertiesSet() throws Exception {
String messageType = "Notify";
messageRequestProcessor.addHandler(messageType, this);
}
}

View File

@ -0,0 +1,45 @@
package com.fastbee.sip.handler.req.message.notify.cmdType;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.notify.NotifyMessageHandler;
import com.fastbee.sip.server.MessageInvoker;
import com.fastbee.sip.server.SipMessage;
import com.fastbee.sip.server.msg.Alarm;
import com.fastbee.sip.service.IMqttService;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.RequestEvent;
@Slf4j
@Component
public class AlarmHandler implements InitializingBean, IMessageHandler {
@Autowired
private NotifyMessageHandler notifyMessageHandler;
@Autowired
private MessageInvoker messageInvoker;
@Autowired
private IMqttService mqttService;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
log.info("设备报警:{}", device.getDeviceId());
SipMessage message = messageInvoker.messageToObj(evt);
if (message instanceof Alarm) {
log.info("报警内容:{}", message);
mqttService.publishEvent((Alarm) message);
}
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "Alarm";
notifyMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,81 @@
package com.fastbee.sip.handler.req.message.notify.cmdType;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.req.ReqAbstractHandler;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.notify.NotifyMessageHandler;
import com.fastbee.sip.server.MessageInvoker;
import com.fastbee.sip.service.IMqttService;
import com.fastbee.sip.service.ISipDeviceService;
import com.fastbee.sip.util.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import javax.sip.header.ViaHeader;
import java.text.ParseException;
@Slf4j
@Component
public class KeepaliveHandler extends ReqAbstractHandler implements InitializingBean, IMessageHandler {
@Autowired
private NotifyMessageHandler notifyMessageHandler;
@Autowired
private ISipDeviceService sipDeviceService;
@Autowired
private MessageInvoker messageInvoker;
@Autowired
private IMqttService mqttService;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
try {
Element rootElement = getRootElement(evt);
String deviceId = XmlUtil.getText(rootElement, "DeviceID");
if (sipDeviceService.exists(deviceId)) {
ViaHeader viaHeader = (ViaHeader) evt.getRequest().getHeader(ViaHeader.NAME);
String received = viaHeader.getReceived();
int rPort = viaHeader.getRPort();
if (StringUtils.isEmpty(received) || rPort == -1) {
log.warn("本地地址替代! received:{},rPort:{} [{}:{}]", received, rPort, viaHeader.getHost(), viaHeader.getPort());
received = viaHeader.getHost();
rPort = viaHeader.getPort();
}
device.setLastconnecttime(DateUtils.getNowDate());
device.setIp(received);
device.setPort(rPort);
device.setHostaddress(received.concat(":").concat(String.valueOf(rPort)));
log.info("设备:{} 心跳上报时间:{}",deviceId,device.getLastconnecttime());
//log.warn("设备:{} 心跳上报时间:{}",deviceId,device.getLastconnecttime());
// 更新在线状态
sipDeviceService.updateSipDeviceStatus(device);
// 更新在线状态到emqx
// mqttService.publishStatus(device, 3);
// 更新通道状态
messageInvoker.catalogQuery(device);
// 回复200 OK
responseAck(evt);
}
} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
e.printStackTrace();
}
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "Keepalive";
notifyMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,27 @@
package com.fastbee.sip.handler.req.message.notify.cmdType;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.notify.NotifyMessageHandler;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import javax.sip.RequestEvent;
public class MediaStatusHandler implements InitializingBean, IMessageHandler {
@Autowired
private NotifyMessageHandler notifyMessageHandler;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "MediaStatus";
notifyMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,27 @@
package com.fastbee.sip.handler.req.message.notify.cmdType;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.notify.NotifyMessageHandler;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import javax.sip.RequestEvent;
public class MobilePositionHandler implements InitializingBean, IMessageHandler {
@Autowired
private NotifyMessageHandler notifyMessageHandler;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "MobilePosition";
notifyMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,20 @@
package com.fastbee.sip.handler.req.message.response;
import com.fastbee.sip.handler.req.message.MessageHandlerAbstract;
import com.fastbee.sip.handler.req.message.MessageRequestProcessor;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ResponseMessageHandler extends MessageHandlerAbstract implements InitializingBean {
@Autowired
private MessageRequestProcessor messageRequestProcessor;
@Override
public void afterPropertiesSet() throws Exception {
String messageType = "Response";
messageRequestProcessor.addHandler(messageType, this);
}
}

View File

@ -0,0 +1,28 @@
package com.fastbee.sip.handler.req.message.response.cmdType;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.response.ResponseMessageHandler;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.RequestEvent;
@Component
public class AlarmRHandler implements InitializingBean, IMessageHandler {
@Autowired
private ResponseMessageHandler responseMessageHandler;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "Alarm";
responseMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,212 @@
package com.fastbee.sip.handler.req.message.response.cmdType;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.domain.SipDeviceChannel;
import com.fastbee.sip.enums.ChannelType;
import com.fastbee.sip.enums.DeviceChannelStatus;
import com.fastbee.sip.handler.req.ReqAbstractHandler;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.response.ResponseMessageHandler;
import com.fastbee.sip.service.ISipDeviceChannelService;
import com.fastbee.sip.util.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.Iterator;
import java.util.Objects;
@Slf4j
@Component
public class CatalogHandler extends ReqAbstractHandler implements InitializingBean, IMessageHandler {
@Autowired
private ResponseMessageHandler responseMessageHandler;
@Autowired
private ISipDeviceChannelService sipDeviceChannelService;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
try {
Element rootElement = getRootElement(evt);
Element deviceListElement = rootElement.element("DeviceList");
if (deviceListElement == null) {
return;
}
Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
//List<SipDeviceChannel> channels = new ArrayList<>();
if (deviceListIterator != null) {
// 遍历DeviceList
while (deviceListIterator.hasNext()) {
SipDeviceChannel deviceChannel = new SipDeviceChannel();
Element itemDevice = deviceListIterator.next();
Element channelDeviceElement = itemDevice.element("DeviceID");
if (channelDeviceElement == null) {
log.warn("缺少 DeviceID");
continue;
}
String channelId = channelDeviceElement.getTextTrim();
if (ObjectUtils.isEmpty(channelId)) {
log.warn("缺少 DeviceID");
continue;
}
//Status
Element statusElement = itemDevice.element("Status");
if (statusElement != null) {
String status = statusElement.getTextTrim().trim();
if (status.equals("ON") || status.equals("On") || status.equals("ONLINE") || status.equals("OK")) {
deviceChannel.setStatus(DeviceChannelStatus.online.getValue());
}
if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) {
deviceChannel.setStatus(DeviceChannelStatus.offline.getValue());
}
} else {
deviceChannel.setStatus(DeviceChannelStatus.offline.getValue());
}
ChannelType channelType = ChannelType.Other;
if (channelId.length() <= 8) {
channelType = ChannelType.CivilCode;
deviceChannel.setHasaudio(0);
}else {
if (channelId.length() == 20) {
int code = Integer.parseInt(channelId.substring(10, 13));
switch (code){
case 215:
channelType = ChannelType.BusinessGroup;
deviceChannel.setHasaudio(0);
break;
case 216:
channelType = ChannelType.VirtualOrganization;
deviceChannel.setHasaudio(0);
break;
case 136:
case 137:
case 138:
deviceChannel.setHasaudio(1);
break;
default:
deviceChannel.setHasaudio(0);
break;
}
}
}
deviceChannel.setDeviceSipId(device.getDeviceSipId());
deviceChannel.setChannelSipId(channelId);
//name
Element channdelNameElement = itemDevice.element("Name");
String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim() : "";
deviceChannel.setChannelName(channelName);
//CivilCode
String civilCode = XmlUtil.getText(itemDevice, "CivilCode");
deviceChannel.setCivilcode(civilCode);
if (channelType == ChannelType.CivilCode && civilCode == null) {
deviceChannel.setParental(1);
// 行政区划如果没有传递具体值,则推测一个
if (channelId.length() > 2) {
deviceChannel.setCivilcode(channelId.substring(0, channelId.length() - 2));
}
}
if (channelType.equals(ChannelType.CivilCode)) {
// 行政区划其他字段没必要识别了,默认在线即可
deviceChannel.setStatus(1);
deviceChannel.setParental(1);
sipDeviceChannelService.updateChannel(device.getDeviceSipId(), deviceChannel);
continue;
}
//parentID
String parentId = XmlUtil.getText(itemDevice, "ParentID");
//String businessGroupID = XmlUtil.getText(itemDevice, "BusinessGroupID");
if (parentId != null) {
if (parentId.contains("/")) {
String lastParentId = parentId.substring(parentId.lastIndexOf("/") + 1);
deviceChannel.setParentid(lastParentId);
}else {
deviceChannel.setParentid(parentId);
}
// 兼容设备通道信息中自己为自己父节点的情况
if (deviceChannel.getParentid().equals(deviceChannel.getChannelSipId())) {
deviceChannel.setParentid(null);
}
}
// Parental
String parental = XmlUtil.getText(itemDevice, "Parental");
// 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1
if (!ObjectUtils.isEmpty(parental) && parental.length() == 1 && Integer.parseInt(parental) == 0) {
deviceChannel.setParental(0);
}else {
deviceChannel.setParental(1);
}
if (deviceChannel.getRegisterTime() == null) {
deviceChannel.setRegisterTime(DateUtils.getNowDate());
}
deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer"));
deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
deviceChannel.setIpaddress(XmlUtil.getText(itemDevice, "IPAddress"));
if (XmlUtil.getText(itemDevice, "Port") == null || Objects.equals(XmlUtil.getText(itemDevice, "Port"), "")) {
deviceChannel.setPort(0);
} else {
deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port")));
}
deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
if (XmlUtil.getText(itemDevice, "Longitude") == null || Objects.equals(XmlUtil.getText(itemDevice, "Longitude"), "")) {
deviceChannel.setLongitude(BigDecimal.valueOf(0.00));
} else {
deviceChannel.setLongitude(BigDecimal.valueOf(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude"))));
}
if (XmlUtil.getText(itemDevice, "Latitude") == null || Objects.equals(XmlUtil.getText(itemDevice, "Latitude"), "")) {
deviceChannel.setLatitude(BigDecimal.valueOf(0.00));
} else {
deviceChannel.setLatitude(BigDecimal.valueOf(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude"))));
}
if (XmlUtil.getText(itemDevice, "PTZType") == null || "".equals(XmlUtil.getText(itemDevice, "PTZType"))) {
//兼容INFO中的信息
Element info = itemDevice.element("Info");
if(XmlUtil.getText(info, "PTZType") == null || "".equals(XmlUtil.getText(info, "PTZType"))){
deviceChannel.setPtztype(0L);
} else {
deviceChannel.setPtztype(Long.parseLong(XmlUtil.getText(info, "PTZType")));
}
} else {
deviceChannel.setPtztype(Long.parseLong(XmlUtil.getText(itemDevice, "PTZType")));
}
//更新到数据库
sipDeviceChannelService.updateChannel(device.getDeviceSipId(), deviceChannel);
//channels.add(deviceChannel);
}
// 发布设备property到emqx
//mqttService.publishChannelsProperty(device.getDeviceSipId(),channels);
// 回复200 OK
responseAck(evt);
}
} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
e.printStackTrace();
}
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "Catalog";
responseMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,28 @@
package com.fastbee.sip.handler.req.message.response.cmdType;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.response.ResponseMessageHandler;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.RequestEvent;
@Component
public class ConfigDownloadHandler implements InitializingBean, IMessageHandler {
@Autowired
private ResponseMessageHandler responseMessageHandler;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "ConfigDownload";
responseMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,28 @@
package com.fastbee.sip.handler.req.message.response.cmdType;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.response.ResponseMessageHandler;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.RequestEvent;
@Component
public class DeviceConfigHandler implements InitializingBean, IMessageHandler {
@Autowired
private ResponseMessageHandler responseMessageHandler;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "DeviceConfig";
responseMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,28 @@
package com.fastbee.sip.handler.req.message.response.cmdType;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.response.ResponseMessageHandler;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.RequestEvent;
@Component
public class DeviceControlHandler implements InitializingBean, IMessageHandler {
@Autowired
private ResponseMessageHandler responseMessageHandler;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "DeviceConfig";
responseMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,61 @@
package com.fastbee.sip.handler.req.message.response.cmdType;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.req.ReqAbstractHandler;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.response.ResponseMessageHandler;
import com.fastbee.sip.service.IMqttService;
import com.fastbee.sip.service.ISipDeviceService;
import com.fastbee.sip.util.XmlUtil;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import java.text.ParseException;
@Component
public class DeviceInfoHandler extends ReqAbstractHandler implements InitializingBean, IMessageHandler {
@Autowired
private ResponseMessageHandler responseMessageHandler;
@Autowired
private ISipDeviceService sipDeviceService;
@Autowired
private IMqttService mqttService;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
try {
Element rootElement = getRootElement(evt);
device.setDeviceName(XmlUtil.getText(rootElement, "DeviceName"));
device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer"));
device.setModel(XmlUtil.getText(rootElement, "Model"));
device.setFirmware(XmlUtil.getText(rootElement, "Firmware"));
if (StringUtils.isEmpty(device.getStreammode())) {
device.setStreammode("UDP");
}
// 更新到数据库
sipDeviceService.updateDevice(device);
// 发布设备info到emqx
mqttService.publishInfo(device);
// 回复200 OK
responseAck(evt);
} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
e.printStackTrace();
}
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "DeviceInfo";
responseMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,28 @@
package com.fastbee.sip.handler.req.message.response.cmdType;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.response.ResponseMessageHandler;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.RequestEvent;
@Component
public class DeviceStatusHandler implements InitializingBean, IMessageHandler {
@Autowired
private ResponseMessageHandler responseMessageHandler;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "DeviceStatus";
responseMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,28 @@
package com.fastbee.sip.handler.req.message.response.cmdType;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.response.ResponseMessageHandler;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.RequestEvent;
@Component
public class MobilePositionRHandler implements InitializingBean, IMessageHandler {
@Autowired
private ResponseMessageHandler responseMessageHandler;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "MobilePosition";
responseMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,93 @@
package com.fastbee.sip.handler.req.message.response.cmdType;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.handler.req.ReqAbstractHandler;
import com.fastbee.sip.handler.req.message.IMessageHandler;
import com.fastbee.sip.handler.req.message.response.ResponseMessageHandler;
import com.fastbee.sip.model.RecordItem;
import com.fastbee.sip.model.RecordList;
import com.fastbee.sip.server.RecordCacheManager;
import com.fastbee.sip.service.ISipCacheService;
import com.fastbee.sip.util.SipUtil;
import com.fastbee.sip.util.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.SipException;
import java.text.ParseException;
import java.util.Iterator;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
@Component
public class RecordInfoHandler extends ReqAbstractHandler implements InitializingBean, IMessageHandler {
@Autowired
private ResponseMessageHandler responseMessageHandler;
@Autowired
private ISipCacheService sipCacheService;
@Autowired
private RecordCacheManager recordCacheManager;
@Override
public void handlerCmdType(RequestEvent evt, SipDevice device, Element element) {
try {
// 回复200 OK
responseAck(evt);
Element rootElement = getRootElement(evt);
String deviceId = rootElement.element("DeviceID").getText();
String sn = XmlUtil.getText(rootElement, "SN");
String sumNum = XmlUtil.getText(rootElement, "SumNum");
String recordkey = deviceId + ":" + sn;
int recordnum = 0;
RecordList recordList = recordCacheManager.get(recordkey);
recordList.setDeviceID(deviceId);
Element recordListElement = rootElement.element("RecordList");
if (recordListElement == null || sumNum == null || sumNum.equals("")) {
log.info("无录像数据");
} else {
Iterator<Element> recordListIterator = recordListElement.elementIterator();
if (recordListIterator != null) {
while (recordListIterator.hasNext()) {
Element itemRecord = recordListIterator.next();
Element recordElement = itemRecord.element("DeviceID");
if (recordElement == null) {
continue;
}
RecordItem item = new RecordItem();
item.setStart(SipUtil.ISO8601Totimestamp(XmlUtil.getText(itemRecord, "StartTime")));
item.setEnd(SipUtil.ISO8601Totimestamp(XmlUtil.getText(itemRecord, "EndTime")));
recordList.addItem(item);
recordnum++;
}
}
}
log.info("getSumNum:{}", recordList.getSumNum());
if (recordList.getSumNum() + recordnum == Integer.parseInt(sumNum)) {
//时间合并 保存到redia
recordList.mergeItems();
log.info("mergeItems recordList:{}", recordList);
recordCacheManager.remove(recordkey);
sipCacheService.setRecordList(recordkey, recordList);
//发布设备property到emqx
} else {
recordList.setSumNum(recordList.getSumNum() + recordnum);
recordCacheManager.put(recordkey, recordList);
}
} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
e.printStackTrace();
}
}
@Override
public void afterPropertiesSet() throws Exception {
String cmdType = "RecordInfo";
responseMessageHandler.addHandler(cmdType, this);
}
}

View File

@ -0,0 +1,29 @@
package com.fastbee.sip.handler.res;
import com.fastbee.sip.handler.IResHandler;
import com.fastbee.sip.server.IGBListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.ResponseEvent;
import java.text.ParseException;
@Slf4j
@Component
public class ByeResHandler implements InitializingBean,IResHandler {
@Autowired
private IGBListener sipListener;
@Override
public void processMsg(ResponseEvent evt) throws ParseException {
}
@Override
public void afterPropertiesSet() throws Exception {
String method = "BYE";
sipListener.addResponseProcessor(method,this);
}
}

View File

@ -0,0 +1,28 @@
package com.fastbee.sip.handler.res;
import com.fastbee.sip.handler.IResHandler;
import com.fastbee.sip.server.IGBListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.ResponseEvent;
import java.text.ParseException;
@Slf4j
@Component
public class CancelResHandler implements InitializingBean,IResHandler {
@Autowired
private IGBListener sipListener;
@Override
public void processMsg(ResponseEvent evt) throws ParseException {
}
@Override
public void afterPropertiesSet() throws Exception {
String method = "CANCEL";
sipListener.addResponseProcessor(method,this);
}
}

View File

@ -0,0 +1,52 @@
package com.fastbee.sip.handler.res;
import com.fastbee.sip.handler.IResHandler;
import com.fastbee.sip.server.IGBListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.ResponseEvent;
import javax.sip.SipException;
import javax.sip.address.SipURI;
import javax.sip.header.CSeqHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.text.ParseException;
@Slf4j
@Component
public class InviteResHandler implements InitializingBean,IResHandler {
@Autowired
private IGBListener sipListener;
@Override
public void processMsg(ResponseEvent evt) throws ParseException {
Response response = evt.getResponse();
Dialog dialog = evt.getDialog();
CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
Request reqAck = null;
try {
reqAck = dialog.createAck(cseq.getSeqNumber());
SipURI requestURI = (SipURI) reqAck.getRequestURI();
ViaHeader viaHeader = (ViaHeader) response.getHeader(ViaHeader.NAME);
requestURI.setHost(viaHeader.getHost());
requestURI.setPort(viaHeader.getPort());
reqAck.setRequestURI(requestURI);
dialog.sendAck(reqAck);
} catch (InvalidArgumentException | SipException | ParseException e) {
e.printStackTrace();
}
}
@Override
public void afterPropertiesSet() throws Exception {
String method = "INVITE";
sipListener.addResponseProcessor(method,this);
}
}

View File

@ -0,0 +1,17 @@
package com.fastbee.sip.handler.res;
import com.fastbee.sip.handler.IResHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.sip.ResponseEvent;
import java.text.ParseException;
@Slf4j
@Component
public class UnknowResHandler implements IResHandler {
@Override
public void processMsg(ResponseEvent evt) throws ParseException {
log.warn("Unknow Response! ReasonPhrase:" + evt.getResponse().getReasonPhrase());
}
}

View File

@ -0,0 +1,64 @@
package com.fastbee.sip.mapper;
import com.fastbee.sip.domain.MediaServer;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 流媒体服务器配置Mapper接口
*
* @author zhuangpeng.li
* @date 2022-11-30
*/
@Repository
public interface MediaServerMapper
{
public MediaServer selectMediaServerById(Long id);
/**
* 查询流媒体服务器配置
*
* @return 流媒体服务器配置
*/
public List<MediaServer> selectMediaServer(MediaServer mediaServer);
public List<MediaServer> selectMediaServerBytenantId(Long tenantId);
/**
* 查询流媒体服务器配置列表
*
* @param mediaServer 流媒体服务器配置
* @return 流媒体服务器配置集合
*/
public List<MediaServer> selectMediaServerList(MediaServer mediaServer);
/**
* 新增流媒体服务器配置
*
* @param mediaServer 流媒体服务器配置
* @return 结果
*/
public int insertMediaServer(MediaServer mediaServer);
/**
* 修改流媒体服务器配置
*
* @param mediaServer 流媒体服务器配置
* @return 结果
*/
public int updateMediaServer(MediaServer mediaServer);
/**
* 删除流媒体服务器配置
*
* @param id 流媒体服务器配置主键
* @return 结果
*/
public int deleteMediaServerById(Long id);
/**
* 批量删除流媒体服务器配置
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteMediaServerByIds(Long[] ids);
}

View File

@ -0,0 +1,66 @@
package com.fastbee.sip.mapper;
import com.fastbee.sip.domain.SipConfig;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* sip系统配置Mapper接口
*
* @author zhuangpeng.li
* @date 2022-11-30
*/
@Repository
public interface SipConfigMapper
{
/**
* 查询产品下第一条sip系统配置
*
* @return sip系统配置
*/
public SipConfig selectSipConfigByProductId(Long productId);
/**
* 查询sip系统配置列表
*
* @param sipConfig sip系统配置
* @return sip系统配置集合
*/
public List<SipConfig> selectSipConfigList(SipConfig sipConfig);
/**
* 新增sip系统配置
*
* @param sipConfig sip系统配置
* @return 结果
*/
public int insertSipConfig(SipConfig sipConfig);
/**
* 修改sip系统配置
*
* @param sipConfig sip系统配置
* @return 结果
*/
public int updateSipConfig(SipConfig sipConfig);
public int resetDefaultSipConfig();
/**
* 删除sip系统配置
*
* @param id sip系统配置主键
* @return 结果
*/
public int deleteSipConfigById(Long id);
/**
* 批量删除sip系统配置
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteSipConfigByIds(Long[] ids);
public int deleteSipConfigByProductId(Long[] productIds);
}

View File

@ -0,0 +1,99 @@
package com.fastbee.sip.mapper;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.sip.domain.BindingChannel;
import com.fastbee.sip.domain.SipDeviceChannel;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 监控设备通道信息Mapper接口
*
* @author zhuangpeng.li
* @date 2022-10-07
*/
@Repository
public interface SipDeviceChannelMapper {
/**
* 查询监控设备通道信息
*
* @param channelId 监控设备通道信息主键
* @return 监控设备通道信息
*/
public SipDeviceChannel selectSipDeviceChannelById(Long channelId);
public SipDeviceChannel selectSipDeviceChannelByChannelSipId(String channelSipId);
public List<SipDeviceChannel> selectSipDeviceChannelByDeviceSipId(String deviceSipId);
/**
* 查询监控设备通道信息列表
*
* @param sipDeviceChannel 监控设备通道信息
* @return 监控设备通道信息集合
*/
public List<SipDeviceChannel> selectSipDeviceChannelList(SipDeviceChannel sipDeviceChannel);
List<SipDeviceChannel> selectChannelWithCivilCodeAndLength(@Param("deviceSipId") String deviceSipId, @Param("parentId") String parentId, @Param("length")Integer length);
public List<SipDeviceChannel> selectChannelByCivilCode(@Param("deviceSipId") String deviceSipId, @Param("parentId") String parentId);
List<SipDeviceChannel> selectChannelWithoutCiviCode(String deviceId);
Integer getChannelMinLength(String deviceSipId);
/**
* 新增监控设备通道信息
*
* @param sipDeviceChannel 监控设备通道信息
* @return 结果
*/
public int insertSipDeviceChannel(SipDeviceChannel sipDeviceChannel);
/**
* 修改监控设备通道信息
*
* @param sipDeviceChannel 监控设备通道信息
* @return 结果
*/
public int updateSipDeviceChannel(SipDeviceChannel sipDeviceChannel);
public int updateChannelStreamId(SipDeviceChannel sipDeviceChannel);
/**
* 删除监控设备通道信息
*
* @param channelId 监控设备通道信息主键
* @return 结果
*/
public int deleteSipDeviceChannelById(Long channelId);
/**
* 批量删除监控设备通道信息
*
* @param channelIds 需要删除的数据主键集合
* @return 结果
*/
public int deleteSipDeviceChannelByIds(Long[] channelIds);
public int deleteSipDeviceChannelByDeviceId(String deviceSipId);
/**
* 根据channelId获取绑定的设备或场景
* @param channelId
* @return
*/
public BindingChannel getBindingChannel(String channelId);
/**
* @description: 查询设备关联通道
* @param: serialNumber 设备编号
* @return: java.util.List<com.fastbee.sip.domain.SipDeviceChannel>
*/
List<SipDeviceChannel> selectDeviceRelSipDeviceChannelList(String serialNumber);
/**
* @description: 查询场景关联通道
* @param: sceneModelId 场景id
* @return: java.util.List<com.fastbee.sip.domain.SipDeviceChannel>
*/
List<SipDeviceChannel> selectSceneRelSipDeviceChannelList(Long sceneModelId);
}

View File

@ -0,0 +1,77 @@
package com.fastbee.sip.mapper;
import com.fastbee.sip.domain.SipDevice;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 监控设备Mapper接口
*
* @author zhuangpeng.li
* @date 2022-10-07
*/
@Repository
public interface SipDeviceMapper
{
/**
* 查询监控设备
*
* @param deviceId 监控设备主键
* @return 监控设备
*/
public SipDevice selectSipDeviceByDeviceId(Long deviceId);
public SipDevice selectSipDeviceBySipId(String sipId);
/**
* 查询监控设备列表
*
* @param sipDevice 监控设备
* @return 监控设备集合
*/
public List<SipDevice> selectSipDeviceList(SipDevice sipDevice);
public List<SipDevice> selectOfflineSipDevice(Integer timeout);
/**
* 新增监控设备
*
* @param sipDevice 监控设备
* @return 结果
*/
int insertSipDevice(SipDevice sipDevice);
/**
* 修改监控设备
*
* @param sipDevice 监控设备
* @return 结果
*/
int updateSipDevice(SipDevice sipDevice);
/**
* 更新设备状态
*
* @param sipDevice 设备
* @return 结果
*/
public int updateSipDeviceStatus(SipDevice sipDevice);
/**
* 删除监控设备
*
* @param deviceId 监控设备主键
* @return 结果
*/
public int deleteSipDeviceByDeviceId(Long deviceId);
/**
* 批量删除监控设备
*
* @param deviceIds 需要删除的数据主键集合
* @return 结果
*/
public int deleteSipDeviceByDeviceIds(Long[] deviceIds);
public int deleteSipDeviceByByDeviceSipId(String deviceSipId);
}

View File

@ -0,0 +1,35 @@
package com.fastbee.sip.model;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.text.Collator;
import java.util.Comparator;
@Data
public class BaseTree<T> implements Comparable<BaseTree>{
private String id;
private String deviceId;
private String pid;
private String name;
private boolean parent;
private T basicData;
@Override
public int compareTo(@NotNull BaseTree treeNode) {
if (this.parent || treeNode.isParent()) {
if (!this.parent && !treeNode.isParent()) {
Comparator<Object> cmp = Collator.getInstance(java.util.Locale.CHINA);
return cmp.compare(treeNode.getName(), this.getName());
}else {
if (this.isParent()) {
return 1;
}else {
return -1;
}
}
}else{
Comparator<Object> cmp = Collator.getInstance(java.util.Locale.CHINA);
return cmp.compare(treeNode.getName(), this.getName());
}
}
}

View File

@ -0,0 +1,209 @@
package com.fastbee.sip.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fastbee.sip.enums.ChannelStatus;
import com.fastbee.sip.enums.PTZType;
import com.fastbee.sip.util.SipUtil;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.util.StringJoiner;
@Data
public class GB28181DeviceChannel {
/**
* 通道对应的设备ID
*/
@JsonIgnore
private String deviceId;
/**
* 通道类型
*/
@JacksonXmlProperty(isAttribute = true, localName = "itemType")
private String type;
/**
* 通道id
*/
@JacksonXmlProperty(localName = "DeviceID")
private String channelId;
/**
* 通道名
*/
@JacksonXmlProperty(localName = "Name")
private String name;
/**
* 生产厂商
*/
@JacksonXmlProperty(localName = "Manufacturer")
private String manufacturer;
/**
* 型号
*/
@JacksonXmlProperty(localName = "Model")
private String model;
/**
* 设备归属
*/
@JacksonXmlProperty(localName = "Owner")
private String owner;
/**
* 行政区域
*/
@JacksonXmlProperty(localName = "CivilCode")
private String civilCode;
/**
* 警区
*/
@JacksonXmlProperty(localName = "Block")
private String block;
/**
* 安装地址
*/
@JacksonXmlProperty(localName = "Address")
private String address;
/**
* 是否有子设备 1有, 0没有
*/
@JacksonXmlProperty(localName = "Parental")
private int parental;
/**
* 父级id
*/
@JacksonXmlProperty(localName = "ParentID")
private String parentId;
/**
* 信令安全模式 缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式
*/
@JacksonXmlProperty(localName = "SafetyWay")
private int safetyWay;
/**
* 注册方式 缺省为1;1:符合IETF RFC3261标准的认证注册模 式; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式
*/
@JacksonXmlProperty(localName = "RegisterWay")
private String registerWay;
/**
* 证书序列号
*/
@JacksonXmlProperty(localName = "CertNum")
private String certNum;
/**
* 证书有效标识 缺省为0;证书有效标识:0:无效1: 有效
*/
@JacksonXmlProperty(localName = "Certifiable")
private int certifiable;
/**
* 证书无效原因码
*/
@JacksonXmlProperty(localName = "ErrCode")
private int errCode;
/**
* 证书终止有效期
*/
@JacksonXmlProperty(localName = "EndTime")
private String endTime;
/**
* 保密属性 缺省为0; 0:不涉密, 1:涉密
*/
@JacksonXmlProperty(localName = "Secrecy")
private String secrecy;
/**
* IP地址
*/
@JacksonXmlProperty(localName = "IpAddress")
private String ipAddress;
/**
* 端口号
*/
@JacksonXmlProperty(localName = "Port")
private int port;
/**
* 密码
*/
@JacksonXmlProperty(localName = "Password")
private String password;
/**
* 详情信息
*/
@JacksonXmlProperty(localName = "Info")
private Info info;
/**
* 经度
*/
@JacksonXmlProperty(localName = "Longitude")
private double longitude;
/**
* 纬度
*/
@JacksonXmlProperty(localName = "Latitude")
private double latitude;
/**
* 子设备数
*/
@JacksonXmlProperty(localName = "SubCount")
private int subCount;
@JacksonXmlProperty(localName = "Status")
private ChannelStatus status;
//目录类型: 0目录,1设备通道?
@JacksonXmlProperty(localName = "CatalogType")
private String catalogType;
@JacksonXmlProperty(localName = "Event")
private String event;
@JacksonXmlProperty(localName = "BusinessGroupID")
private String businessGroupId;
@Getter
@Setter
public static class Info {
/**
* 云台类型
*/
@JacksonXmlProperty(localName = "PTZType")
private PTZType PTZType;
@JacksonXmlProperty(localName = "DownloadSpeed")
private String downloadSpeed;
public String toXML() {
StringJoiner joiner = new StringJoiner("");
joiner
.add("<PTZType>")
.add(PTZType == null ? "0" : String.valueOf(getPTZType().getValue()))
.add("</PTZType>\n");
joiner.add("<DownloadSpeed>").add(SipUtil.safeString(downloadSpeed)).add("</DownloadSpeed>\n");
return joiner.toString();
}
}
}

View File

@ -0,0 +1,14 @@
package com.fastbee.sip.model;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class InviteInfo {
private String ssrc;
private String callId;
private String fromTag;
private String viaTag;
private int port;
}

View File

@ -0,0 +1,203 @@
package com.fastbee.sip.model;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
@Data
public class MediaServerConfig {
@JSONField(name = "api.apiDebug")
private String apiDebug;
@JSONField(name = "api.secret")
private String apiSecret;
@JSONField(name = "ffmpeg.bin")
private String ffmpegBin;
@JSONField(name = "ffmpeg.cmd")
private String ffmpegCmd;
@JSONField(name = "ffmpeg.log")
private String ffmpegLog;
@JSONField(name = "general.enableVhost")
private String generalEnableVhost;
@JSONField(name = "general.flowThreshold")
private String generalFlowThreshold;
@JSONField(name = "general.maxStreamWaitMS")
private String generalMaxStreamWaitMS;
@JSONField(name = "general.streamNoneReaderDelayMS")
private String generalStreamNoneReaderDelayMS;
private String localIP;
private String wanIp;
@JSONField(name = "hls.fileBufSize")
private String hlsFileBufSize;
@JSONField(name = "hls.filePath")
private String hlsFilePath;
@JSONField(name = "hls.segDur")
private String hlsSegDur;
@JSONField(name = "hls.segNum")
private String hlsSegNum;
@JSONField(name = "hook.access_file_except_hls")
private String hookAccessFileExceptHLS;
@JSONField(name = "hook.admin_params")
private String hookAdminParams;
@JSONField(name = "hook.enable")
private String hookEnable;
@JSONField(name = "hook.on_flow_report")
private String hookOnFlowReport;
@JSONField(name = "hook.on_http_access")
private String hookOnHttpAccess;
@JSONField(name = "hook.on_play")
private String hookOnPlay;
@JSONField(name = "hook.on_publish")
private String hookOnPublish;
@JSONField(name = "hook.on_record_mp4")
private String hookOnRecordMp4;
@JSONField(name = "hook.on_rtsp_auth")
private String hookOnRtspAuth;
@JSONField(name = "hook.on_rtsp_realm")
private String hookOnRtspRealm;
@JSONField(name = "hook.on_shell_login")
private String hookOnShellLogin;
@JSONField(name = "hook.on_stream_changed")
private String hookOnStreamChanged;
@JSONField(name = "hook.on_stream_none_reader")
private String hookOnStreamNoneReader;
@JSONField(name = "hook.on_stream_not_found")
private String hookOnStreamNotFound;
@JSONField(name = "hook.timeoutSec")
private String hookTimeoutSec;
@JSONField(name = "http.charSet")
private String httpCharSet;
@JSONField(name = "http.keepAliveSecond")
private String httpKeepAliveSecond;
@JSONField(name = "http.maxReqCount")
private String httpMaxReqCount;
@JSONField(name = "http.maxReqSize")
private String httpMaxReqSize;
@JSONField(name = "http.notFound")
private String httpNotFound;
@JSONField(name = "http.port")
private String httpPort;
@JSONField(name = "http.rootPath")
private String httpRootPath;
@JSONField(name = "http.sendBufSize")
private String httpSendBufSize;
@JSONField(name = "http.sslport")
private String httpSSLport;
@JSONField(name = "multicast.addrMax")
private String multicastAddrMax;
@JSONField(name = "multicast.addrMin")
private String multicastAddrMin;
@JSONField(name = "multicast.udpTTL")
private String multicastUdpTTL;
@JSONField(name = "record.appName")
private String recordAppName;
@JSONField(name = "record.filePath")
private String recordFilePath;
@JSONField(name = "record.fileSecond")
private String recordFileSecond;
@JSONField(name = "record.sampleMS")
private String recordFileSampleMS;
@JSONField(name = "rtmp.handshakeSecond")
private String rtmpHandshakeSecond;
@JSONField(name = "rtmp.keepAliveSecond")
private String rtmpKeepAliveSecond;
@JSONField(name = "rtmp.modifyStamp")
private String rtmpModifyStamp;
@JSONField(name = "rtmp.port")
private String rtmpPort;
@JSONField(name = "rtp.audioMtuSize")
private String rtpAudioMtuSize;
@JSONField(name = "rtp.clearCount")
private String rtpClearCount;
@JSONField(name = "rtp.cycleMS")
private String rtpCycleMS;
@JSONField(name = "rtp.maxRtpCount")
private String rtpMaxRtpCount;
@JSONField(name = "rtp.videoMtuSize")
private String rtpVideoMtuSize;
@JSONField(name = "rtp_proxy.checkSource")
private String rtpProxyCheckSource;
@JSONField(name = "rtp_proxy.dumpDir")
private String rtpProxyDumpDir;
@JSONField(name = "rtp_proxy.port")
private String rtpProxyPort;
@JSONField(name = "rtp_proxy.timeoutSec")
private String rtpProxyTimeoutSec;
@JSONField(name = "rtsp.authBasic")
private String rtspAuthBasic;
@JSONField(name = "rtsp.handshakeSecond")
private String rtspHandshakeSecond;
@JSONField(name = "rtsp.keepAliveSecond")
private String rtspKeepAliveSecond;
@JSONField(name = "rtsp.port")
private String rtspPort;
@JSONField(name = "rtsp.sslport")
private String rtspSSlport;
@JSONField(name = "shell.maxReqSize")
private String shellMaxReqSize;
@JSONField(name = "shell.shell")
private String shellPhell;
}

View File

@ -0,0 +1,10 @@
package com.fastbee.sip.model;
import lombok.Data;
@Data
public class PtzDirectionInput {
int leftRight;
int upDown;
int moveSpeed;
}

View File

@ -0,0 +1,9 @@
package com.fastbee.sip.model;
import lombok.Data;
@Data
public class PtzscaleInput{
int inOut;
int scaleSpeed;
}

View File

@ -0,0 +1,11 @@
package com.fastbee.sip.model;
import lombok.Data;
@Data
public class RecordInput {
String deviceId;
String channelId;
String startTime;
String endTime;
}

View File

@ -0,0 +1,23 @@
package com.fastbee.sip.model;
import lombok.Data;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Data
public class RecordItem implements Comparable<RecordItem>{
long start;
long end;
@Override
public int compareTo(RecordItem item) {
Date startTime_now = new Date(start*1000);
Date startTime_param = new Date(item.getStart()*1000);
if (startTime_param.compareTo(startTime_now) > 0) {
return -1;
}else {
return 1;
}
}
}

View File

@ -0,0 +1,56 @@
package com.fastbee.sip.model;
import lombok.Data;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@Data
public class RecordList {
String deviceID;
int sumNum;
private List<RecordItem> recordItems = new ArrayList<>();
public void addItem(RecordItem item) {
this.recordItems.add(item);
}
public void mergeItems() {
recordItems.sort(Comparator.naturalOrder());
List<RecordItem> temp = new ArrayList<>();
long start = 0;
long end = 0;
for (int i = 0; i < recordItems.size(); i++) {
RecordItem item = recordItems.get(i);
if (i == recordItems.size() - 1) {
if (end >= item.getStart()) {
RecordItem titem = new RecordItem();
titem.setStart(start);
titem.setEnd(item.getEnd());
temp.add(titem);
} else {
RecordItem titem = new RecordItem();
titem.setStart(start);
titem.setEnd(end);
temp.add(titem);
temp.add(item);
}
}
if (start == 0 && end == 0) {
start = item.getStart();
end = item.getEnd();
} else if (end >= item.getStart()) {
end = item.getEnd();
} else {
RecordItem titem = new RecordItem();
titem.setStart(start);
titem.setEnd(end);
temp.add(titem);
start = item.getStart();
end = item.getEnd();
}
}
this.recordItems = temp;
}
}

View File

@ -0,0 +1,11 @@
package com.fastbee.sip.model;
import lombok.Data;
@Data
public class RequestMessage {
private String id;
private String deviceId;
private String type;
private Object data;
}

View File

@ -0,0 +1,141 @@
package com.fastbee.sip.model;
import gov.nist.core.InternalErrorHandler;
import gov.nist.javax.sip.header.SIPDate;
import java.util.*;
public class SipDate extends SIPDate {
private final Calendar javaCal;
public SipDate(long timeMillis) {
this.javaCal = new GregorianCalendar(TimeZone.getDefault(), Locale.getDefault());
Date date = new Date(timeMillis);
this.javaCal.setTime(date);
this.wkday = this.javaCal.get(Calendar.DAY_OF_WEEK);
switch(this.wkday) {
case 1:
this.sipWkDay = "Sun";
break;
case 2:
this.sipWkDay = "Mon";
break;
case 3:
this.sipWkDay = "Tue";
break;
case 4:
this.sipWkDay = "Wed";
break;
case 5:
this.sipWkDay = "Thu";
break;
case 6:
this.sipWkDay = "Fri";
break;
case 7:
this.sipWkDay = "Sat";
break;
default:
InternalErrorHandler.handleException("No date map for wkday " + this.wkday);
}
this.day = this.javaCal.get(Calendar.DATE);
this.month = this.javaCal.get(Calendar.MONTH);
switch(this.month) {
case 0:
this.sipMonth = "Jan";
break;
case 1:
this.sipMonth = "Feb";
break;
case 2:
this.sipMonth = "Mar";
break;
case 3:
this.sipMonth = "Apr";
break;
case 4:
this.sipMonth = "May";
break;
case 5:
this.sipMonth = "Jun";
break;
case 6:
this.sipMonth = "Jul";
break;
case 7:
this.sipMonth = "Aug";
break;
case 8:
this.sipMonth = "Sep";
break;
case 9:
this.sipMonth = "Oct";
break;
case 10:
this.sipMonth = "Nov";
break;
case 11:
this.sipMonth = "Dec";
break;
default:
InternalErrorHandler.handleException("No date map for month " + this.month);
}
this.year = this.javaCal.get(Calendar.YEAR);
this.hour = this.javaCal.get(Calendar.HOUR_OF_DAY);
this.minute = this.javaCal.get(Calendar.MINUTE);
this.second = this.javaCal.get(Calendar.SECOND);
}
@Override
public StringBuilder encode(StringBuilder var1) {
String var2;
if (this.month < 9) {
var2 = "0" + (this.month + 1);
} else {
var2 = "" + (this.month + 1);
}
String var3;
if (this.day < 10) {
var3 = "0" + this.day;
} else {
var3 = "" + this.day;
}
String var4;
if (this.hour < 10) {
var4 = "0" + this.hour;
} else {
var4 = "" + this.hour;
}
String var5;
if (this.minute < 10) {
var5 = "0" + this.minute;
} else {
var5 = "" + this.minute;
}
String var6;
if (this.second < 10) {
var6 = "0" + this.second;
} else {
var6 = "" + this.second;
}
int var8 = this.javaCal.get(Calendar.MILLISECOND);
String var7;
if (var8 < 10) {
var7 = "00" + var8;
} else if (var8 < 100) {
var7 = "0" + var8;
} else {
var7 = "" + var8;
}
return var1.append(this.year).append("-").append(var2).append("-").append(var3).append("T").append(var4).append(":").append(var5).append(":").append(var6).append(".").append(var7);
}
}

View File

@ -0,0 +1,25 @@
package com.fastbee.sip.model;
import com.fastbee.sip.domain.SipDevice;
import lombok.Data;
@Data
public class SipDeviceSummary {
public SipDeviceSummary(SipDevice device) {
this.manufacturer = device.getManufacturer();
this.firmware = device.getFirmware();
this.transport = device.getTransport();
this.streammode = device.getStreammode();
this.port = device.getPort();
this.hostaddress = device.getHostaddress();
}
public SipDeviceSummary() {
}
private String manufacturer;
private String firmware;
private String transport;
private String streammode;
private Integer port;
private String hostaddress;
}

View File

@ -0,0 +1,44 @@
package com.fastbee.sip.model;
import com.alibaba.fastjson.JSONArray;
import lombok.Data;
@Data
public class Stream {
private String app;
private String deviceID;
private String channelId;
private String ssrc;
private String streamId;
private String ip;
private String playurl;
private String mediaServerId;
private JSONArray tracks;
private String startTime;
private String endTime;
private boolean pause;
private String flv;
private String https_flv;
private String ws_flv;
private String wss_flv;
private String fmp4;
private String https_fmp4;
private String ws_fmp4;
private String wss_fmp4;
private String hls;
private String https_hls;
private String ws_hls;
private String wss_hls;
private String rtmp;
private String rtsp;
private String rtc;
public Stream(String deviceSipId, String channelId, String streamId, String ssrc) {
this.deviceID = deviceSipId;
this.channelId = channelId;
this.streamId = streamId;
this.ssrc = ssrc;
}
public Stream() {}
}

View File

@ -0,0 +1,31 @@
package com.fastbee.sip.model;
import lombok.Data;
@Data
public class StreamURL {
private String protocol;
private String host;
private int port = -1;
private String file;
private String url;
public StreamURL() {
}
public StreamURL(String protocol, String host, int port, String file) {
this.protocol = protocol;
this.host = host;
this.port = port;
this.file = file;
}
@Override
public String toString() {
if (protocol != null && host != null && port != -1 ) {
return String.format("%s://%s:%s/%s", protocol, host, port, file);
}else {
return null;
}
}
}

View File

@ -0,0 +1,34 @@
package com.fastbee.sip.model;
import com.fastbee.sip.enums.SessionType;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class VideoSessionInfo {
//流基本信息
private String mediaServerId;
private String deviceId;
private String channelId;
private String stream;
private String ssrc;
private int port;
private String streamMode;
private SessionType type;
//流状态
private boolean pushing;
private boolean recording;
private int onPlaytime;
private int player;
private boolean videoRecord;
private boolean enable_fmp4;
private boolean enable_hls;
private boolean enable_rtmp;
private boolean enable_rtsp;
//录像信息
String startTime;
String endTime;
int downloadSpeed;
}

View File

@ -0,0 +1,206 @@
package com.fastbee.sip.model;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
@Data
public class ZlmMediaServer {
@JSONField(name = "api.apiDebug")
private String apiDebug;
@JSONField(name = "api.secret")
private String apiSecret;
@JSONField(name = "ffmpeg.bin")
private String ffmpegBin;
@JSONField(name = "ffmpeg.cmd")
private String ffmpegCmd;
@JSONField(name = "ffmpeg.log")
private String ffmpegLog;
@JSONField(name = "general.mediaServerId")
private String mediaServerId;
@JSONField(name = "general.enableVhost")
private String generalEnableVhost;
@JSONField(name = "general.flowThreshold")
private String generalFlowThreshold;
@JSONField(name = "general.maxStreamWaitMS")
private String generalMaxStreamWaitMS;
@JSONField(name = "general.streamNoneReaderDelayMS")
private String generalStreamNoneReaderDelayMS;
private String localIP;
private String wanIp;
@JSONField(name = "hls.fileBufSize")
private String hlsFileBufSize;
@JSONField(name = "hls.filePath")
private String hlsFilePath;
@JSONField(name = "hls.segDur")
private String hlsSegDur;
@JSONField(name = "hls.segNum")
private String hlsSegNum;
@JSONField(name = "hook.access_file_except_hls")
private String hookAccessFileExceptHLS;
@JSONField(name = "hook.admin_params")
private String hookAdminParams;
@JSONField(name = "hook.enable")
private String hookEnable;
@JSONField(name = "hook.on_flow_report")
private String hookOnFlowReport;
@JSONField(name = "hook.on_http_access")
private String hookOnHttpAccess;
@JSONField(name = "hook.on_play")
private String hookOnPlay;
@JSONField(name = "hook.on_publish")
private String hookOnPublish;
@JSONField(name = "hook.on_record_mp4")
private String hookOnRecordMp4;
@JSONField(name = "hook.on_rtsp_auth")
private String hookOnRtspAuth;
@JSONField(name = "hook.on_rtsp_realm")
private String hookOnRtspRealm;
@JSONField(name = "hook.on_shell_login")
private String hookOnShellLogin;
@JSONField(name = "hook.on_stream_changed")
private String hookOnStreamChanged;
@JSONField(name = "hook.on_stream_none_reader")
private String hookOnStreamNoneReader;
@JSONField(name = "hook.on_stream_not_found")
private String hookOnStreamNotFound;
@JSONField(name = "hook.timeoutSec")
private String hookTimeoutSec;
@JSONField(name = "http.charSet")
private String httpCharSet;
@JSONField(name = "http.keepAliveSecond")
private String httpKeepAliveSecond;
@JSONField(name = "http.maxReqCount")
private String httpMaxReqCount;
@JSONField(name = "http.maxReqSize")
private String httpMaxReqSize;
@JSONField(name = "http.notFound")
private String httpNotFound;
@JSONField(name = "http.port")
private String httpPort;
@JSONField(name = "http.rootPath")
private String httpRootPath;
@JSONField(name = "http.sendBufSize")
private String httpSendBufSize;
@JSONField(name = "http.sslport")
private String httpSSLport;
@JSONField(name = "multicast.addrMax")
private String multicastAddrMax;
@JSONField(name = "multicast.addrMin")
private String multicastAddrMin;
@JSONField(name = "multicast.udpTTL")
private String multicastUdpTTL;
@JSONField(name = "record.appName")
private String recordAppName;
@JSONField(name = "record.filePath")
private String recordFilePath;
@JSONField(name = "record.fileSecond")
private String recordFileSecond;
@JSONField(name = "record.sampleMS")
private String recordFileSampleMS;
@JSONField(name = "rtmp.handshakeSecond")
private String rtmpHandshakeSecond;
@JSONField(name = "rtmp.keepAliveSecond")
private String rtmpKeepAliveSecond;
@JSONField(name = "rtmp.modifyStamp")
private String rtmpModifyStamp;
@JSONField(name = "rtmp.port")
private String rtmpPort;
@JSONField(name = "rtp.audioMtuSize")
private String rtpAudioMtuSize;
@JSONField(name = "rtp.clearCount")
private String rtpClearCount;
@JSONField(name = "rtp.cycleMS")
private String rtpCycleMS;
@JSONField(name = "rtp.maxRtpCount")
private String rtpMaxRtpCount;
@JSONField(name = "rtp.videoMtuSize")
private String rtpVideoMtuSize;
@JSONField(name = "rtp_proxy.checkSource")
private String rtpProxyCheckSource;
@JSONField(name = "rtp_proxy.dumpDir")
private String rtpProxyDumpDir;
@JSONField(name = "rtp_proxy.port")
private String rtpProxyPort;
@JSONField(name = "rtp_proxy.timeoutSec")
private String rtpProxyTimeoutSec;
@JSONField(name = "rtsp.authBasic")
private String rtspAuthBasic;
@JSONField(name = "rtsp.handshakeSecond")
private String rtspHandshakeSecond;
@JSONField(name = "rtsp.keepAliveSecond")
private String rtspKeepAliveSecond;
@JSONField(name = "rtsp.port")
private String rtspPort;
@JSONField(name = "rtsp.sslport")
private String rtspSSlport;
@JSONField(name = "shell.maxReqSize")
private String shellMaxReqSize;
@JSONField(name = "shell.shell")
private String shellPhell;
}

View File

@ -0,0 +1,11 @@
package com.fastbee.sip.server;
import com.fastbee.sip.handler.IReqHandler;
import com.fastbee.sip.handler.IResHandler;
import javax.sip.SipListener;
public interface IGBListener extends SipListener {
public void addRequestProcessor(String method, IReqHandler processor);
public void addResponseProcessor(String method, IResHandler processor);
}

View File

@ -0,0 +1,11 @@
package com.fastbee.sip.server;
import com.fastbee.sip.domain.SipDevice;
public interface IRtspCmd {
void playPause(SipDevice device, String channelId, String streamId);
void playReplay(SipDevice device, String channelId, String streamId);
void playBackSeek(SipDevice device, String channelId, String streamId, long seektime);
void playBackSpeed(SipDevice device, String channelId, String streamId, Integer speed);
void setCseq(String streamId);
}

View File

@ -0,0 +1,12 @@
package com.fastbee.sip.server;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.model.VideoSessionInfo;
public interface ISipCmd {
VideoSessionInfo playStreamCmd(SipDevice device, String channelId, boolean record);
VideoSessionInfo playbackStreamCmd(SipDevice device, String channelId, String startTime, String endTime);
VideoSessionInfo downloadStreamCmd(SipDevice device, String channelId, String startTime, String endTime, int downloadSpeed);
void streamByeCmd(SipDevice device, String channelId, String stream, String ssrc);
void streamByeCmd(String deviceId, String channelId, String stream, String ssrc);
}

View File

@ -0,0 +1,64 @@
package com.fastbee.sip.server;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.server.msg.DeviceControl;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.sip.RequestEvent;
import java.util.Date;
public interface MessageInvoker {
/**
* 发送订阅报警指令
*
* @param device 设备信息
* @param startAlarmPriority 开始报警级别
* @param endAlarmPriority 结束报警级别
* @param expires 过期时间
* @param alarmMethod 报警方式
* @param startAlarmTime 开始报警时间
* @param endAlarmTime 结束报警时间
* @return void
*/
void subscribe(@Nonnull SipDevice device,
int startAlarmPriority,
int endAlarmPriority,
int expires,
@Nonnull String alarmMethod,
@Nullable Date startAlarmTime,
@Nullable Date endAlarmTime);
/**
* 发送设备控制指令,{@link DeviceControl#getDeviceId()}为通道ID
*
* @param device 设备信息
* @param command 控制指令
* @return void
*/
void deviceControl(SipDevice device, DeviceControl command);
/**
* 获取设备详细信息
*
* @param device 设备信息
* @return 详细信息
*/
boolean deviceInfoQuery(SipDevice device);
/**
* 获取通道列表
*
* @param device 设备信息
* @return 通道列表
*/
boolean catalogQuery(SipDevice device);
boolean recordInfoQuery(SipDevice device, String sn, String channelId, Date start, Date end);
SipMessage messageToObj(RequestEvent event);
<T> T getExecResult(String key, long timeout);
}

View File

@ -0,0 +1,89 @@
package com.fastbee.sip.server;
import javax.sip.*;
import javax.sip.header.CallIdHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.util.TooManyListenersException;
public class NullSipProvider implements SipProvider {
@Override
public void addSipListener(SipListener sipListener) throws TooManyListenersException {
}
@Override
public void removeSipListener(SipListener sipListener) {
}
@Override
public SipStack getSipStack() {
return null;
}
@Override
public ListeningPoint getListeningPoint() {
return null;
}
@Override
public ListeningPoint[] getListeningPoints() {
return new ListeningPoint[0];
}
@Override
public void setListeningPoint(ListeningPoint listeningPoint) throws ObjectInUseException {
}
@Override
public void addListeningPoint(ListeningPoint listeningPoint) throws ObjectInUseException, TransportAlreadySupportedException {
}
@Override
public ListeningPoint getListeningPoint(String s) {
return null;
}
@Override
public void removeListeningPoint(ListeningPoint listeningPoint) throws ObjectInUseException {
}
@Override
public CallIdHeader getNewCallId() {
return null;
}
@Override
public ClientTransaction getNewClientTransaction(Request request) throws TransactionUnavailableException {
return null;
}
@Override
public ServerTransaction getNewServerTransaction(Request request) throws TransactionAlreadyExistsException, TransactionUnavailableException {
return null;
}
@Override
public void sendRequest(Request request) throws SipException {
}
@Override
public void sendResponse(Response response) throws SipException {
}
@Override
public Dialog getNewDialog(Transaction transaction) throws SipException {
return null;
}
@Override
public void setAutomaticDialogSupportEnabled(boolean b) {
}
}

View File

@ -0,0 +1,41 @@
package com.fastbee.sip.server;
import com.fastbee.sip.model.RecordList;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
@Component
public class RecordCacheManager {
private final ConcurrentHashMap<String, RecordList> recordMap = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();
public void put(String key,RecordList list){
recordMap.putIfAbsent(key, list);
}
public RecordList get(String key){
RecordList ret = recordMap.get(key);
if (ret == null) {
ret = new RecordList();
recordMap.putIfAbsent(key, ret);
}
return ret;
}
public void remove(String key) {
recordMap.remove(key);
lockMap.remove(key);
}
public void addlock(String key){
lockMap.put(key,new ReentrantLock());
}
public ReentrantLock getlock(String key){
return lockMap.get(key);
}
}

View File

@ -0,0 +1,267 @@
package com.fastbee.sip.server;
import com.fastbee.sip.domain.SipConfig;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.model.InviteInfo;
import com.fastbee.sip.model.VideoSessionInfo;
import com.fastbee.sip.service.IInviteService;
import com.fastbee.sip.service.ISipCacheService;
import com.fastbee.sip.util.SipUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import javax.sip.*;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.Request;
import java.text.ParseException;
import java.util.ArrayList;
@Component
public class ReqMsgHeaderBuilder {
@Autowired
private SipFactory sipFactory;
@Autowired
@Qualifier(value = "udpSipServer")
private SipProvider sipserver;
@Autowired
private VideoSessionManager streamSession;
@Autowired
private ISipCacheService sipCacheService;
@Autowired
private IInviteService inviteService;
/**
* 创建请求消息
*
* @param device
* @param content
* @param fromTag
* @return
* @throws ParseException
* @throws InvalidArgumentException
* @throws PeerUnavailableException
*/
public Request createMessageRequest(SipDevice device, SipConfig sipConfig, String content, String fromTag)
throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null;
// sipuri
SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceSipId(),
device.getHostaddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(),
sipConfig.getPort(), device.getTransport(), SipUtil.getNewViaTag());
viaHeader.setRPort();
viaHeaders.add(viaHeader);
// from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getServerSipid(),
sipConfig.getIp() + ":" + sipConfig.getPort());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
// to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceSipId(),
sipConfig.getDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, SipUtil.getNewTag());
// callid
CallIdHeader callIdHeader = sipserver.getNewCallId();
// Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
// ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(sipCacheService.getCSEQ(sipConfig.getServerSipid()), Request.MESSAGE);
request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader,
fromHeader, toHeader, viaHeaders, maxForwards);
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION",
"MANSCDP+xml");
request.setContent(content, contentTypeHeader);
return request;
}
public Request createInviteRequest(SipDevice device, SipConfig sipConfig, String channelId, String content, String ssrc, String fromTag) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null;
// 请求行
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostaddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(),
device.getTransport(), SipUtil.getNewViaTag());
viaHeader.setRPort();
viaHeaders.add(viaHeader);
// from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getServerSipid(),
sipConfig.getDomain());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); // 必须要有标记否则无法创建会话无法回应ack
// to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, sipConfig.getDomain());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, null);
// callid
CallIdHeader callIdHeader = sipserver.getNewCallId();
// Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
// ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(sipCacheService.getCSEQ(sipConfig.getServerSipid()), Request.INVITE);
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,
fromHeader, toHeader, viaHeaders, maxForwards);
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
.createSipURI(sipConfig.getServerSipid(), sipConfig.getIp() + ":" + sipConfig.getPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
// Subject
SubjectHeader subjectHeader = sipFactory.createHeaderFactory()
.createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getIp(), 0));
request.addHeader(subjectHeader);
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION",
"SDP");
request.setContent(content, contentTypeHeader);
return request;
}
public Request createPlaybackInviteRequest(SipDevice device, SipConfig sipConfig, String channelId, String content, String viaTag, String fromTag) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null;
// 请求行
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId,
device.getHostaddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(),
device.getTransport(), viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
// from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getServerSipid(),
sipConfig.getDomain());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); // 必须要有标记否则无法创建会话无法回应ack
// to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostaddress());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, null);
// callid
CallIdHeader callIdHeader = sipserver.getNewCallId();
// Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
// ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(sipCacheService.getCSEQ(sipConfig.getServerSipid()), Request.INVITE);
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,
fromHeader, toHeader, viaHeaders, maxForwards);
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
.createSipURI(sipConfig.getServerSipid(), sipConfig.getIp() + ":" + sipConfig.getPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION",
"SDP");
request.setContent(content, contentTypeHeader);
return request;
}
public Request createByeRequest(SipDevice device, SipConfig sipConfig, String channelId, InviteInfo invite) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null;
//请求行
SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostaddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(),
device.getTransport(), SipUtil.getNewViaTag());
viaHeader.setRPort();
viaHeaders.add(viaHeader);
//from
SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getServerSipid(), sipConfig.getDomain());
Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, invite.getFromTag());
//to
SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(channelId, device.getHostaddress());
Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, SipUtil.getNewTag());
//Forwards
MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
//ceq
CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(sipCacheService.getCSEQ(sipConfig.getServerSipid()), Request.BYE);
CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(invite.getCallId());
request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards);
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
.createSipURI(sipConfig.getServerSipid(), sipConfig.getIp() + ":" + sipConfig.getPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
return request;
}
public Request createRtspRequest(SipDevice device, SipConfig sipConfig, String channelId, String streamId, String content)
throws PeerUnavailableException, ParseException, InvalidArgumentException {
Request request = null;
VideoSessionInfo info = streamSession.getSessionInfo(device.getDeviceSipId(), channelId, streamId, null);
if (info == null) {
return null;
}
Dialog dialog = streamSession.getclientTransaction(info).getDialog();
if (dialog == null) {
return null;
}
InviteInfo invite = inviteService.getInviteInfoBySSRC(info.getSsrc());
if (invite == null) {
return null;
}
// 请求行
SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostaddress());
// via
ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(getLocalIp(sipConfig.getIp()), sipConfig.getPort(),
device.getTransport(), invite.getViaTag());
viaHeader.setRPort();
viaHeaders.add(viaHeader);
// from
SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getServerSipid(),
sipConfig.getDomain());
Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, invite.getFromTag());
// to
SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostaddress());
Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, dialog.getRemoteTag());
// callid
CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(invite.getCallId());;
// Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
// ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(sipCacheService.getCSEQ(sipConfig.getServerSipid()), Request.INFO);
request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,
fromHeader, toHeader, viaHeaders, maxForwards);
Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
.createSipURI(sipConfig.getServerSipid(), sipConfig.getIp() + ":" + sipConfig.getPort()));
request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application",
"MANSRTSP");
request.setContent(content, contentTypeHeader);
return request;
}
public String getLocalIp(String deviceLocalIp) {
if (!ObjectUtils.isEmpty(deviceLocalIp)) {
return deviceLocalIp;
}
return sipserver.getListeningPoint().getIPAddress();
}
}

View File

@ -0,0 +1,48 @@
package com.fastbee.sip.server;
import javax.sip.header.AuthorizationHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.message.Request;
public interface RequestBuilder {
RequestBuilder requestLine(String sipId, String host, int port);
RequestBuilder user(String user);
RequestBuilder via(String host, int port, String transport, String viaTag);
RequestBuilder from(String sipId, String domain, String fromTag);
RequestBuilder to(String user, String domain, String toTag);
RequestBuilder contact(String user, int port);
RequestBuilder subject(String subject);
RequestBuilder cSeq(int cSeq);
RequestBuilder method(String method);
RequestBuilder contentxml(byte[] content);
RequestBuilder contentsdp(byte[] content);
RequestBuilder authorization(AuthorizationHeader header);
Request build(CallIdHeader callId);
default RequestBuilder invite() {
return method(Request.INVITE);
}
default RequestBuilder message() {
return method(Request.MESSAGE);
}
default RequestBuilder subscribe() {
return method(Request.SUBSCRIBE);
}
}

View File

@ -0,0 +1,97 @@
package com.fastbee.sip.server;
import com.fastbee.sip.conf.SysSipConfig;
import com.fastbee.sip.domain.MediaServer;
import com.fastbee.sip.model.ZlmMediaServer;
import com.fastbee.sip.service.IMediaServerService;
import com.fastbee.sip.service.ISipCacheService;
import com.fastbee.sip.service.ISipConfigService;
import com.fastbee.sip.util.ZlmApiUtils;
import gov.nist.javax.sip.SipStackImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.annotation.Order;
import javax.sip.*;
import java.util.Properties;
@Slf4j
@Configuration
@Order(13)
public class SipLayer {
@Autowired
private SysSipConfig sipConfig;
@Autowired
private IGBListener gbSIPListener;
@Autowired
private ZlmApiUtils zlmApiUtils;
@Autowired
private ISipCacheService sipCacheService;
@Autowired
private ISipConfigService sipConfigService;
@Autowired
private IMediaServerService mediaServerService;
private SipStack sipStack;
private SipFactory sipFactory;
@Bean("sipFactory")
SipFactory createSipFactory() {
sipFactory = SipFactory.getInstance();
sipFactory.setPathName("gov.nist");
if (sipConfig.isEnabled()) {
//缓存zlm服务器配置
MediaServer media = mediaServerService.selectMediaServerBytenantId(1L);
if (media != null) {
ZlmMediaServer mediaServer = zlmApiUtils.getMediaServerConfig(media);
if (mediaServer != null) {
log.info("zlm配置获取成功成功...");
sipCacheService.updateMediaInfo(mediaServer);
mediaServerService.syncMediaServer(media, media.getSecret());
}
}
}
return sipFactory;
}
@Bean("sipStack")
@DependsOn("sipFactory")
SipStack createSipStack() throws PeerUnavailableException {
Properties properties = new Properties();
properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP");
properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.getIp());
properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "false");
properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "0");
properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "sip_server_log");
properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sip_debug_log");
sipStack = (SipStackImpl) sipFactory.createSipStack(properties);
return sipStack;
}
@Bean("udpSipServer")
@DependsOn("sipStack")
SipProvider startUdpListener() throws Exception {
if (sipConfig.isEnabled()) {
log.info("startUdpListener");
ListeningPoint udpListeningPoint = sipStack.createListeningPoint(sipConfig.getIp(), sipConfig.getPort(), "UDP");
SipProvider udpSipProvider = sipStack.createSipProvider(udpListeningPoint);
udpSipProvider.addSipListener(gbSIPListener);
log.info("Sip Server UDP 启动成功 port {}", sipConfig.getPort());
sipConfigService.syncSipConfig(sipConfig);
return udpSipProvider;
} else {
return new NullSipProvider();
}
}
}

View File

@ -0,0 +1,15 @@
package com.fastbee.sip.server;
public interface SipMessage {
String getDeviceId();
String getSn();
default int totalPart() {
return 1;
}
default int numberOfPart() {
return 1;
}
}

View File

@ -0,0 +1,195 @@
package com.fastbee.sip.server;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.core.redis.RedisKeyBuilder;
import com.fastbee.sip.enums.SessionType;
import com.fastbee.sip.model.InviteInfo;
import com.fastbee.sip.model.VideoSessionInfo;
import com.fastbee.sip.util.SipUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import javax.sip.ClientTransaction;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import static java.util.Collections.emptyList;
@Component
public class VideoSessionManager {
@Autowired
private RedisCache redisCache;
private final ConcurrentHashMap<String, ClientTransaction> sessionMap = new ConcurrentHashMap<>();
public String createPlaySsrc(String domain) {
return SipUtil.getPlaySsrc(domain);
}
public String createPlayBackSsrc(String domain) {
return SipUtil.getPlayBackSsrc(domain);
}
public void put(VideoSessionInfo info, ClientTransaction client) {
String ssrc = info.getSsrc();
if (info.getType() == SessionType.play || info.getType() == SessionType.playrecord) {
ssrc = info.getType().name();
}
String key = RedisKeyBuilder.buildStreamCacheKey(info.getDeviceId(), info.getChannelId(), info.getStream(), ssrc);
redisCache.setCacheObject(key, info);
if (!ObjectUtils.isEmpty(client)) {
key = RedisKeyBuilder.buildStreamCacheKey(info.getDeviceId(), info.getChannelId(), info.getStream(), info.getSsrc());
sessionMap.put(key, client);
}
}
public ClientTransaction getclientTransaction(VideoSessionInfo info) {
String key = RedisKeyBuilder.buildStreamCacheKey(info.getDeviceId(), info.getChannelId(), info.getStream(), info.getSsrc());
return sessionMap.get(key);
}
public ClientTransaction getclientTransaction(VideoSessionInfo info, InviteInfo invite) {
String key = RedisKeyBuilder.buildStreamCacheKey(info.getDeviceId(), info.getChannelId(), info.getStream(), invite.getSsrc());
return sessionMap.get(key);
}
public VideoSessionInfo getSessionInfo(String deviceId, String channelId, String stream, String callId) {
if (ObjectUtils.isEmpty(deviceId)) {
deviceId = "*";
}
if (ObjectUtils.isEmpty(channelId)) {
channelId = "*";
}
if (ObjectUtils.isEmpty(stream)) {
stream = "*";
}
if (ObjectUtils.isEmpty(callId)) {
callId = "*";
}
String key = RedisKeyBuilder.buildStreamCacheKey(deviceId, channelId, stream, callId);
List<Object> scanResult = redisCache.scan(key);
if (scanResult.size() == 0) {
return null;
}
return (VideoSessionInfo) redisCache.getCacheObject((String) scanResult.get(0));
}
public VideoSessionInfo getSessionInfoByCallId(String callId) {
if (ObjectUtils.isEmpty(callId)) {
return null;
}
String key = RedisKeyBuilder.buildStreamCacheKey("*", "*", "*", callId);
List<Object> scanResult = redisCache.scan(key);
if (!scanResult.isEmpty()) {
return (VideoSessionInfo) redisCache.getCacheObject((String) scanResult.get(0));
} else {
return null;
}
}
public VideoSessionInfo getSessionInfoBySSRC(String SSRC) {
if (ObjectUtils.isEmpty(SSRC)) {
return null;
}
String key = RedisKeyBuilder.buildStreamCacheKey("*", "*", SSRC, "*");
List<Object> scanResult = redisCache.scan(key);
if (!scanResult.isEmpty()) {
return (VideoSessionInfo) redisCache.getCacheObject((String) scanResult.get(0));
} else {
return null;
}
}
public List<VideoSessionInfo> getSessionInfoForAll(String deviceId, String channelId, String stream, String callId) {
if (ObjectUtils.isEmpty(deviceId)) {
deviceId = "*";
}
if (ObjectUtils.isEmpty(channelId)) {
channelId = "*";
}
if (ObjectUtils.isEmpty(stream)) {
stream = "*";
}
if (ObjectUtils.isEmpty(callId)) {
callId = "*";
}
String key = RedisKeyBuilder.buildStreamCacheKey(deviceId, channelId, stream, callId);
List<Object> scanResult = redisCache.scan(key);
if (scanResult.size() == 0) {
return emptyList();
}
List<VideoSessionInfo> result = new ArrayList<>();
for (Object keyObj : scanResult) {
result.add((VideoSessionInfo) redisCache.getCacheObject((String) keyObj));
}
return result;
}
public String getMediaServerId(String deviceId, String channelId, String stream) {
VideoSessionInfo ssrcTransaction = getSessionInfo(deviceId, channelId, null, stream);
if (ssrcTransaction == null) {
return null;
}
return ssrcTransaction.getMediaServerId();
}
public String getSSRC(String deviceId, String channelId, String stream) {
VideoSessionInfo ssrcTransaction = getSessionInfo(deviceId, channelId, null, stream);
if (ssrcTransaction == null) {
return null;
}
return ssrcTransaction.getSsrc();
}
public void remove(String deviceId, String channelId, String stream, String callId) {
String key = RedisKeyBuilder.buildStreamCacheKey(deviceId, channelId, stream, callId);
if (!Objects.equals(callId, "play")) {
redisCache.deleteObject(key);
}
sessionMap.remove(key);
}
public void remove(String deviceId, String channelId, String stream) {
List<VideoSessionInfo> sinfoList = getSessionInfoForAll(deviceId, channelId, stream, null);
if (sinfoList == null || sinfoList.isEmpty()) {
return;
}
for (VideoSessionInfo sinfo : sinfoList) {
String key = RedisKeyBuilder.buildStreamCacheKey(deviceId, channelId, stream, sinfo.getSsrc());
if (sinfo.getType() != SessionType.play) {
redisCache.deleteObject(key);
}
sessionMap.remove(key);
}
}
public void removeByCallId(String deviceId, String channelId, String callId) {
VideoSessionInfo sinfo = getSessionInfo(deviceId, channelId, null, callId);
if (sinfo == null) {
return;
}
String key = RedisKeyBuilder.buildStreamCacheKey(deviceId, channelId, sinfo.getStream(), sinfo.getSsrc());
if (sinfo.getType() != SessionType.play) {
redisCache.deleteObject(key);
}
sessionMap.remove(key);
}
public List<VideoSessionInfo> getAllSsrc() {
String allkey = RedisKeyBuilder.buildStreamCacheKey("*", "*", "*", "*");
List<Object> scanResult = redisCache.scan(allkey);
if (scanResult.size() == 0) {
return null;
}
List<VideoSessionInfo> result = new ArrayList<>();
for (Object ssrcTransactionKey : scanResult) {
String key = (String) ssrcTransactionKey;
result.add((VideoSessionInfo) redisCache.getCacheObject((String) key));
}
return result;
}
}

View File

@ -0,0 +1,90 @@
package com.fastbee.sip.server.impl;
import com.fastbee.sip.handler.IReqHandler;
import com.fastbee.sip.handler.IResHandler;
import com.fastbee.sip.server.IGBListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.sip.*;
import javax.sip.header.CSeqHeader;
import javax.sip.message.Response;
import java.text.ParseException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class GBListenerImpl implements IGBListener {
private static final Map<String, IReqHandler> requestProcessorMap = new ConcurrentHashMap<>();
private static final Map<String, IResHandler> responseProcessorMap = new ConcurrentHashMap<>();
public void addRequestProcessor(String method, IReqHandler processor) {
requestProcessorMap.put(method, processor);
}
public void addResponseProcessor(String method, IResHandler processor) {
responseProcessorMap.put(method, processor);
}
@Override
@Async("taskExecutor")
public void processRequest(RequestEvent evt) {
String method = evt.getRequest().getMethod();
IReqHandler sipRequestProcessor = requestProcessorMap.get(method);
if (sipRequestProcessor == null) {
log.warn("不支持方法{}的request", method);
return;
}
requestProcessorMap.get(method).processMsg(evt);
}
@Override
@Async("taskExecutor")
public void processResponse(ResponseEvent evt) {
//处理响应消息
Response response = evt.getResponse();
int status = response.getStatusCode();
// 响应成功
if ((status >= Response.OK) && (status < Response.MULTIPLE_CHOICES)) {
log.info("response{},",response.getReasonPhrase());
CSeqHeader cseqHeader = (CSeqHeader) evt.getResponse().getHeader(CSeqHeader.NAME);
String method = cseqHeader.getMethod();
log.info("接收response响应status{},message:{},method:{}",status,response.getReasonPhrase(),method);
IResHandler sipRequestProcessor = responseProcessorMap.get(method);
if (sipRequestProcessor != null) {
try {
sipRequestProcessor.processMsg(evt);
} catch (ParseException e) {
e.printStackTrace();
}
}
} else if ((status >= Response.TRYING) && (status < Response.OK)) {
log.info("接收response响应status{},,message:{}",status,response.getReasonPhrase());
} else {
log.warn("接收到失败的response响应status{},,message:{}",status,response.getReasonPhrase());
}
}
@Override
public void processTimeout(TimeoutEvent timeoutEvent) {
log.info("processTimeout{}", timeoutEvent);
}
@Override
public void processIOException(IOExceptionEvent ioExceptionEvent) {
//log.debug("processIOException{}", ioExceptionEvent);
}
@Override
public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
//log.debug("processTransactionTerminated{}", transactionTerminatedEvent);
}
@Override
public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
//log.debug("processDialogTerminated{}", dialogTerminatedEvent);
}
}

View File

@ -0,0 +1,275 @@
package com.fastbee.sip.server.impl;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.sip.domain.SipConfig;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.server.MessageInvoker;
import com.fastbee.sip.server.SipMessage;
import com.fastbee.sip.server.msg.*;
import com.fastbee.sip.service.ISipConfigService;
import com.fastbee.sip.util.SipUtil;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.sip.*;
import javax.sip.header.EventHeader;
import javax.sip.header.ExpiresHeader;
import javax.sip.header.Header;
import javax.sip.message.Request;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
@Slf4j
@Component
public class MessageInvokerImpl implements MessageInvoker {
@Autowired
private SipFactory sipFactory;
@Autowired
private ISipConfigService sipConfigService;
@Autowired
private RedisCache redisCache;
@Autowired
@Qualifier(value = "udpSipServer")
private SipProvider sipserver;
public final static XmlMapper mapper = new XmlMapper();
private void sendRequest(Function<Integer, String> bodyBuilder,
String requestMethod,
SipDevice device,
String viaTag,
String fromTag,
String toTag,
Header... headers) {
int id = (int) ((Math.random() * 9 + 1) * 100000);
createTransaction(requestMethod, bodyBuilder.apply(id), device.getDeviceSipId(), device, viaTag, fromTag, toTag, headers);
}
private void sendRequest(String xml,
String requestMethod,
SipDevice device,
String viaTag,
String fromTag,
String toTag,
Header... headers) {
createTransaction(requestMethod, xml, device.getDeviceSipId(), device, viaTag, fromTag, toTag, headers);
}
@SneakyThrows
private ClientTransaction createTransaction(String method,
String xml,
String deviceId,
SipDevice device,
String viaTag,
String fromTag,
String toTag,
Header... headers) {
SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
Request request = new RequestBuilderImpl(sipFactory)
.requestLine(deviceId, device.getIp(), device.getPort())
.method(method)
.user(device.getDeviceSipId())
.via(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag)
.from(sipConfig.getServerSipid(), sipConfig.getIp() + ":" + sipConfig.getPort(), fromTag)
.to(deviceId, device.getHostAndPort(), toTag)
.contentxml(xml.getBytes("GB2312"))
.contact(sipConfig.getServerSipid(), sipConfig.getPort())
.build(sipserver.getNewCallId());
log.debug("prepare SIP request \n{}", request);
for (Header header : headers) {
request.addHeader(header);
}
return transmitRequest(request);
}
static Map<String, Function<String, SipMessage>> valueConverter = new ConcurrentHashMap<>();
static {
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
mapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
mapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);
valueConverter.put("DeviceInfo", xml -> jsonNodeToObject(xml, GB28181Device.class));
valueConverter.put("Keepalive", xml -> jsonNodeToObject(xml, KeepaliveMessage.class));
valueConverter.put("Catalog", xml -> jsonNodeToObject(xml, CatalogInfo.class));
valueConverter.put("Alarm", xml -> jsonNodeToObject(xml, Alarm.class));
valueConverter.put("ConfigDownload", xml -> jsonNodeToObject(xml, ConfigDownload.class));
valueConverter.put("DeviceControl", xml -> jsonNodeToObject(xml, DeviceControl.class));
}
@SneakyThrows
private static <T> T jsonNodeToObject(String xml, Class<T> tClass) {
return mapper.readValue(xml.replace("\r\n", "").replace("\n", ""), tClass);
}
@SneakyThrows
public SipMessage messageToObj(RequestEvent event) {
try {
Request request = event.getRequest();
if ((!Request.MESSAGE.equals(request.getMethod())
&& !Request.NOTIFY.equals(request.getMethod())
&& !Request.SUBSCRIBE.equals(request.getMethod()))
|| request.getRawContent() == null) {
return null;
}
String content = new String(request.getRawContent(), "GB2312");
JsonNode jsonNode = mapper.readTree(content);
String cmdType = jsonNode.get("CmdType").asText();
Function<String, SipMessage> converter = valueConverter.get(cmdType);
if (null != converter) {
return converter.apply(content);
}
} catch (Throwable error) {
log.error("handle SIP message error \n{}", event.getRequest(), error);
}
return null;
}
private ClientTransaction transmitRequest(Request request) throws SipException {
ClientTransaction clientTransaction = sipserver.getNewClientTransaction(request);
clientTransaction.sendRequest();
return clientTransaction;
}
@Override
@SneakyThrows
public void subscribe(@Nonnull SipDevice device, int startAlarmPriority, int endAlarmPriority, int expires, @Nonnull String alarmMethod, @Nullable Date startAlarmTime, @Nullable Date endAlarmTime) {
String startAlarmTimeString = startAlarmTime == null ? "" : SipUtil.dateToISO8601(startAlarmTime);
String endAlarmTimeString = endAlarmTime == null ? "" : SipUtil.dateToISO8601(endAlarmTime);
ExpiresHeader expiresHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires);
EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader("presemce");
this.sendRequest(sn ->
"<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n" +
"<Query>\r\n" +
"<CmdType>Alarm</CmdType>\r\n" +
"<SN>" + sn + "</SN>\r\n" +
"<DeviceID>" + device.getDeviceSipId() + "</DeviceID>\r\n" +
"<StartAlarmPriority>" + startAlarmPriority + "</StartAlarmPriority>\n" +
"<EndAlarmPriority>" + endAlarmPriority + "</EndAlarmPriority>\n" +
"<AlarmMethod>" + alarmMethod + "</AlarmMethod>\n" +
"<StartTime>" + startAlarmTimeString + "</StartTime>\n" +
"<EndTime>" + endAlarmTimeString + "</EndTime>\n" +
"</Query>\r\n",
Request.SUBSCRIBE,
device,
null,
"AK32B1U8DKDrA",
null,
expiresHeader,
eventHeader
);
}
@Override
public void deviceControl(SipDevice device, DeviceControl command) {
this.sendRequest(sn -> command.toXml(sn, "GB2312"),
Request.MESSAGE,
device,
"ViaPtzBranch",
"FromPtzTag",
null
);
}
@Override
public boolean deviceInfoQuery(SipDevice device) {
this.sendRequest(sn ->
"<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n" +
"<Query>\r\n" +
"<CmdType>DeviceInfo</CmdType>\r\n" +
"<SN>" + sn + "</SN>\r\n" +
"<DeviceID>" + device.getDeviceSipId() + "</DeviceID>\r\n" +
"</Query>\r\n",
Request.MESSAGE,
device,
"ViaDeviceInfoBranch",
"FromDeviceInfoTag",
"ToDeviceInfoTag"
);
return true;
}
@Override
public boolean catalogQuery(SipDevice device) {
this.sendRequest(sn ->
"<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n" +
"<Query>\r\n" +
"<CmdType>Catalog</CmdType>\r\n" +
"<SN>" + sn + "</SN>\r\n" +
"<DeviceID>" + device.getDeviceSipId() + "</DeviceID>\r\n" +
"</Query>\r\n",
Request.MESSAGE,
device,
"ViaCatalogBranch",
"FromCatalogTag",
null
);
return true;
}
@Override
public boolean recordInfoQuery(SipDevice device, String sn, String channelId, Date start, Date end) {
String startTimeString = SipUtil.dateToISO8601(start);
String endTimeString = SipUtil.dateToISO8601(end);
;
this.sendRequest("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n" +
"<Query>\r\n" +
"<CmdType>RecordInfo</CmdType>\r\n" +
"<SN>" + sn + "</SN>\r\n" +
"<DeviceID>" + channelId + "</DeviceID>\r\n" +
"<StartTime>" + startTimeString + "</StartTime>\r\n" +
"<EndTime>" + endTimeString + "</EndTime>\r\n" +
"<Secrecy>0</Secrecy>\r\n" +
"<Type>all</Type>\r\n" +
"</Query>\r\n",
Request.MESSAGE,
device,
"ViarecordInfoBranch",
"FromrecordInfoTag",
null
);
return true;
}
public <T> T getExecResult(String key, long timeout) {
long time = 0;
while (true) {
try {
T instance = redisCache.getCacheObject(key);
if (null == instance) {
if (time >= timeout) {
log.error("key:{} get Response timeout", key);
return null;
}
time += 1000;
TimeUnit.MILLISECONDS.sleep(1000L);
} else {
return instance;
}
} catch (Exception e) {
log.error("", e);
Thread.currentThread().interrupt();
break;
}
}
log.error("key:{} can't get Response", key);
return null;
}
}

View File

@ -0,0 +1,186 @@
package com.fastbee.sip.server.impl;
import com.fastbee.sip.server.RequestBuilder;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import javax.sip.SipFactory;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.Request;
import java.util.ArrayList;
import java.util.List;
@AllArgsConstructor
public class RequestBuilderImpl implements RequestBuilder {
private SipFactory sipFactory;
private String host;
private int port;
private String user;
private String method;
private boolean transportTcp;
private long cSeq = 1L;
private final List<ViaHeader> viaHeaders = new ArrayList<>();
private ContactHeader contactHeader;
private SubjectHeader subjectHeader;
private FromHeader fromHeader;
private ToHeader toHeader;
private AuthorizationHeader authorizationHeader;
private ContentTypeHeader contentTypeHeader;
private byte[] content;
private SipURI requestLine;
public RequestBuilderImpl(SipFactory sipFactory) {
this.sipFactory = sipFactory;
}
@Override
@SneakyThrows
public RequestBuilder requestLine(String sipId, String host, int port) {
requestLine = sipFactory.createAddressFactory().createSipURI(sipId, host + ":" + port);
return this;
}
@Override
public RequestBuilder user(String user) {
this.user = user;
return this;
}
@Override
public RequestBuilder method(String method) {
this.method = method;
return this;
}
@Override
@SneakyThrows
public RequestBuilder via(String host,
int port,
String transport,
String viaTag) {
this.port = port;
this.transportTcp = "TCP".equals(transport);
this.host = host;
ViaHeader viaHeader = sipFactory.createHeaderFactory()
.createViaHeader(host, port, transport, viaTag);
viaHeader.setRPort();
viaHeaders.add(viaHeader);
return this;
}
@Override
@SneakyThrows
public RequestBuilder from(String sipId,
String domain,
String fromTag) {
SipURI from = sipFactory.createAddressFactory().createSipURI(sipId, domain);
Address fromAddress = sipFactory.createAddressFactory().createAddress(from);
fromHeader = sipFactory.createHeaderFactory()
.createFromHeader(fromAddress, fromTag);
return this;
}
@Override
@SneakyThrows
public RequestBuilder to(String sipId,
String domain,
String toTag) {
SipURI from = sipFactory.createAddressFactory().createSipURI(sipId, domain);
Address fromAddress = sipFactory.createAddressFactory().createAddress(from);
toHeader = sipFactory.createHeaderFactory()
.createToHeader(fromAddress, toTag);
return this;
}
@Override
@SneakyThrows
public RequestBuilder contact(String user, int port) {
Address concatAddress = sipFactory.createAddressFactory()
.createAddress(sipFactory.createAddressFactory()
.createSipURI(user, user + ":" + port));
contactHeader = sipFactory.createHeaderFactory().createContactHeader(concatAddress);
return this;
}
@Override
public RequestBuilder cSeq(int cSeq) {
this.cSeq = cSeq;
return this;
}
@Override
@SneakyThrows
public RequestBuilder subject(String subject) {
subjectHeader = sipFactory.createHeaderFactory()
.createSubjectHeader(subject);
return this;
}
@Override
@SneakyThrows
public RequestBuilder contentxml(byte[] content) {
contentTypeHeader = sipFactory.createHeaderFactory()
.createContentTypeHeader("APPLICATION", "MANSCDP+xml");
this.content = content;
return this;
}
@Override
@SneakyThrows
public RequestBuilder contentsdp(byte[] content) {
contentTypeHeader = sipFactory.createHeaderFactory()
.createContentTypeHeader("APPLICATION", "SDP");
this.content = content;
return this;
}
@Override
public RequestBuilder authorization(AuthorizationHeader header) {
this.authorizationHeader = header;
return this;
}
@Override
@SneakyThrows
public Request build(CallIdHeader callId) {
//请求行
SipURI requestLine = this.requestLine == null ? sipFactory.createAddressFactory()
.createSipURI(user, host + ":" + port)
: this.requestLine;
//callid
CallIdHeader callIdHeader = callId;
//Forwards
MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
//ceq
CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(cSeq, method);
Request request = sipFactory.createMessageFactory()
.createRequest(requestLine, method, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards);
//Authorization
if (this.authorizationHeader != null) {
request.addHeader(this.authorizationHeader);
}
//Contact
if (contactHeader != null) {
request.addHeader(contactHeader);
}
// Subject
if (subjectHeader != null) {
request.addHeader(subjectHeader);
}
//Content
if (content != null) {
request.setContent(content, contentTypeHeader);
}
return request;
}
}

View File

@ -0,0 +1,122 @@
package com.fastbee.sip.server.impl;
import com.fastbee.sip.domain.SipConfig;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.server.IRtspCmd;
import com.fastbee.sip.server.ReqMsgHeaderBuilder;
import com.fastbee.sip.service.ISipConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.sip.ClientTransaction;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import javax.sip.SipProvider;
import javax.sip.message.Request;
import java.text.ParseException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class RtspCmdImpl implements IRtspCmd {
public static Map<String, Long> CSEQCACHE = new ConcurrentHashMap<>();
@Autowired
private ReqMsgHeaderBuilder headerBuilder;
@Autowired
@Qualifier(value = "udpSipServer")
private SipProvider sipserver;
@Autowired
private ISipConfigService sipConfigService;
public void playPause(SipDevice device, String channelId, String streamId) {
try {
SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
if (sipConfig == null) {
log.error("[playPause] sipConfig is null");
return ;
}
String content = "PAUSE RTSP/1.0\r\n" +
"CSeq: " + getInfoCseq() + "\r\n" +
"PauseTime: now\r\n";
Request request = headerBuilder.createRtspRequest(device, sipConfig, channelId, streamId, content);
ClientTransaction clientTransaction = sipserver.getNewClientTransaction(request);
clientTransaction.sendRequest();
} catch (SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
}
}
public void playReplay(SipDevice device, String channelId, String streamId) {
try {
SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
if (sipConfig == null) {
log.error("[playReplay] sipConfig is null");
return ;
}
String content = "PLAY RTSP/1.0\r\n" +
"CSeq: " + getInfoCseq() + "\r\n" +
"Range: npt=now-\r\n";
Request request = headerBuilder.createRtspRequest(device, sipConfig, channelId, streamId, content);
ClientTransaction clientTransaction = sipserver.getNewClientTransaction(request);
clientTransaction.sendRequest();
} catch (SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
}
}
public void playBackSeek(SipDevice device, String channelId, String streamId, long seektime) {
try {
SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
if (sipConfig == null) {
log.error("[playBackSeek] sipConfig is null");
return ;
}
String content = "PLAY RTSP/1.0\r\n" +
"CSeq: " + getInfoCseq() + "\r\n" +
"Range: npt=" + Math.abs(seektime) + "-\r\n";
Request request = headerBuilder.createRtspRequest(device, sipConfig, channelId, streamId, content);
ClientTransaction clientTransaction = sipserver.getNewClientTransaction(request);
clientTransaction.sendRequest();
} catch (SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
}
}
public void playBackSpeed(SipDevice device, String channelId, String streamId, Integer speed) {
try {
SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
if (sipConfig == null) {
log.error("[playBackSpeed] sipConfig is null");
return ;
}
String content = "PLAY RTSP/1.0\r\n" +
"CSeq: " + getInfoCseq() + "\r\n" +
"Scale: " + speed + ".000000\r\n";
Request request = headerBuilder.createRtspRequest(device, sipConfig, channelId, streamId, content);
ClientTransaction clientTransaction = sipserver.getNewClientTransaction(request);
clientTransaction.sendRequest();
} catch (SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
}
}
public void setCseq(String streamId) {
if (CSEQCACHE.containsKey(streamId)) {
CSEQCACHE.put(streamId, CSEQCACHE.get(streamId) + 1);
} else {
CSEQCACHE.put(streamId, 2l);
}
}
private int getInfoCseq() {
return (int) ((Math.random() * 9 + 1) * Math.pow(10, 8));
}
}

View File

@ -0,0 +1,381 @@
package com.fastbee.sip.server.impl;
import com.fastbee.sip.domain.MediaServer;
import com.fastbee.sip.domain.SipConfig;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.enums.SessionType;
import com.fastbee.sip.model.InviteInfo;
import com.fastbee.sip.model.VideoSessionInfo;
import com.fastbee.sip.server.ISipCmd;
import com.fastbee.sip.server.ReqMsgHeaderBuilder;
import com.fastbee.sip.server.VideoSessionManager;
import com.fastbee.sip.service.IInviteService;
import com.fastbee.sip.service.IMediaServerService;
import com.fastbee.sip.service.ISipConfigService;
import com.fastbee.sip.service.ISipDeviceService;
import com.fastbee.sip.util.SipUtil;
import com.fastbee.sip.util.ZlmApiUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.sip.*;
import javax.sip.message.Request;
import java.text.ParseException;
import java.util.List;
@Slf4j
@Component
public class SipCmdImpl implements ISipCmd {
@Autowired
private VideoSessionManager streamSession;
@Autowired
private ReqMsgHeaderBuilder headerBuilder;
@Autowired
private ISipConfigService sipConfigService;
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private ISipDeviceService sipDeviceService;
@Autowired
private ZlmApiUtils zlmApiUtils;
@Autowired
private VideoSessionManager videoSessionManager;
@Autowired
private IInviteService inviteService;
@Autowired
@Qualifier(value = "udpSipServer")
private SipProvider sipserver;
@Override
public VideoSessionInfo playStreamCmd(SipDevice device, String channelId, boolean record) {
try {
SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
if (sipConfig == null) {
log.error("playStreamCmd sipConfig is null");
return null;
}
MediaServer mediaInfo = mediaServerService.selectMediaServerBydeviceSipId(device.getDeviceSipId());
if (mediaInfo == null) {
log.error("playStreamCmd mediaInfo is null");
return null;
}
VideoSessionInfo info = VideoSessionInfo.builder()
.mediaServerId(mediaInfo.getServerId())
.deviceId(device.getDeviceSipId())
.channelId(channelId)
.streamMode(device.getStreammode().toUpperCase())
.build();
String fromTag;
if (record) {
info.setType(SessionType.playrecord);
fromTag = "playrecord";
} else {
info.setType(SessionType.play);
fromTag = "play";
}
//创建rtp服务器
info = mediaServerService.createRTPServer(sipConfig, mediaInfo, device, info);
//创建Invite会话
String content = buildRequestContent(sipConfig, mediaInfo, info);
Request request = headerBuilder.createInviteRequest(device, sipConfig, channelId, content, info.getSsrc(), fromTag);
//发送消息
ClientTransaction transaction = transmitRequest(request);
log.info("playStreamCmd streamSession: {}", info);
InviteInfo invite = InviteInfo.builder()
.ssrc(info.getSsrc())
.fromTag(fromTag)
.callId(transaction.getDialog().getCallId().getCallId())
.port(info.getPort()).build();
log.warn("playStreamCmd invite: {}", invite);
inviteService.updateInviteInfo(info, invite);
streamSession.put(info, transaction);
return info;
} catch (SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
}
return null;
}
@Override
public VideoSessionInfo playbackStreamCmd(SipDevice device, String channelId, String startTime, String endTime) {
try {
SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
if (sipConfig == null) {
log.error("playbackStreamCmd sipConfig is null");
return null;
}
MediaServer mediaInfo = mediaServerService.selectMediaServerBydeviceSipId(device.getDeviceSipId());
if (mediaInfo == null) {
log.error("playbackStreamCmd mediaInfo is null");
return null;
}
VideoSessionInfo info = VideoSessionInfo.builder()
.mediaServerId(mediaInfo.getServerId())
.deviceId(device.getDeviceSipId())
.channelId(channelId)
.streamMode(device.getStreammode().toUpperCase())
.type(SessionType.playback)
.startTime(startTime)
.endTime(endTime)
.build();
//创建rtp服务器
info = mediaServerService.createRTPServer(sipConfig, mediaInfo, device, info);
//创建Invite会话
String fromTag = "playback" + SipUtil.getNewFromTag();
String viaTag = SipUtil.getNewViaTag();
String content = buildRequestContent(sipConfig, mediaInfo, info);
Request request = headerBuilder.createPlaybackInviteRequest(device, sipConfig, channelId, content, viaTag, fromTag);
//发送消息
ClientTransaction transaction = transmitRequest(request);
log.info("playbackStreamCmd streamSession: {}", info);
InviteInfo invite = InviteInfo.builder()
.ssrc(info.getSsrc())
.fromTag(fromTag)
.viaTag(viaTag)
.callId(transaction.getDialog().getCallId().getCallId())
.port(info.getPort()).build();
log.warn("playbackStreamCmd invite: {}", invite);
inviteService.updateInviteInfo(info, invite);
streamSession.put(info, transaction);
return info;
} catch (SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
}
return null;
}
@Override
public VideoSessionInfo downloadStreamCmd(SipDevice device, String channelId,
String startTime, String endTime, int downloadSpeed) {
try {
SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
if (sipConfig == null) {
log.error("downloadStreamCmd sipConfig is null");
return null;
}
MediaServer mediaInfo = mediaServerService.selectMediaServerBydeviceSipId(device.getDeviceSipId());
if (mediaInfo == null) {
log.error("downloadStreamCmd mediaInfo is null");
return null;
}
VideoSessionInfo info = VideoSessionInfo.builder()
.mediaServerId(mediaInfo.getServerId())
.deviceId(device.getDeviceSipId())
.channelId(channelId)
.streamMode(device.getStreammode().toUpperCase())
.type(SessionType.download)
.startTime(startTime)
.endTime(endTime)
.downloadSpeed(downloadSpeed)
.build();
;
//创建rtp服务器
info = mediaServerService.createRTPServer(sipConfig, mediaInfo, device, info);
//创建Invite会话
String fromTag = "download" + SipUtil.getNewFromTag();;
String viaTag = SipUtil.getNewViaTag();
String content = buildRequestContent(sipConfig, mediaInfo, info);
Request request = headerBuilder.createPlaybackInviteRequest(device, sipConfig, channelId, content, viaTag, fromTag);
//发送消息
ClientTransaction transaction = transmitRequest(request);
log.info("downloadStreamCmd streamSession: {}", info);
InviteInfo invite = InviteInfo.builder()
.ssrc(info.getSsrc())
.fromTag(fromTag)
.viaTag(viaTag)
.callId(transaction.getDialog().getCallId().getCallId())
.port(info.getPort()).build();
log.warn("downloadStreamCmd invite: {}", invite);
inviteService.updateInviteInfo(info, invite);
streamSession.put(info, transaction);
return info;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void streamByeCmd(SipDevice device, String channelId, String stream, String ssrc) {
SipConfig sipConfig = sipConfigService.selectSipConfigBydeviceSipId(device.getDeviceSipId());
if (sipConfig == null) {
log.error("[发送BYE] sipConfig is null");
return;
}
MediaServer mediaInfo = mediaServerService.selectMediaServerBydeviceSipId(device.getDeviceSipId());
if (mediaInfo == null) {
log.error("[发送BYE] mediaInfo is null");
return;
}
List<VideoSessionInfo> SessionInfoList = streamSession.getSessionInfoForAll(device.getDeviceSipId(), channelId, stream, ssrc);
if (SessionInfoList == null || SessionInfoList.isEmpty()) {
log.warn("[发送BYE] 未找到事务信息,设备: device: {}, channel: {}", device.getDeviceSipId(), channelId);
return;
}
for (VideoSessionInfo info : SessionInfoList) {
try {
log.warn("[发送BYE] 设备: device: {}, channel: {}, stream: {}, ssrc: {}", device.getDeviceSipId(),
info.getChannelId(), info.getStream(), info.getSsrc());
List<InviteInfo> list = inviteService.getInviteInfoAll(info.getType(), info.getDeviceId(), info.getChannelId(), info.getStream());
if (list.isEmpty()) {
log.warn("[发送BYE] 未找到invite信息,设备: Stream: {}", info.getStream());
} else {
for (InviteInfo invite : list) {
// 发送bye消息
Request request = headerBuilder.createByeRequest(device, sipConfig, channelId, invite);
//获取缓存会话
ClientTransaction transaction = videoSessionManager.getclientTransaction(info);
if (transaction == null) {
log.warn("[发送BYE] transaction is null");
continue;
}
Dialog dialog = transaction.getDialog();
if (dialog == null) {
log.warn("[发送BYE] transaction is dialog");
continue;
}
//创建客户端,发送请求
ClientTransaction clientTransaction = sipserver.getNewClientTransaction(request);
dialog.sendRequest(clientTransaction);
// 释放ssrc
SipUtil.releaseSsrc(info.getSsrc());
// 关闭rtp服务器
zlmApiUtils.closeRTPServer(mediaInfo, stream);
log.warn("closeRTPServer Port:{}", info.getPort());
if (info.isPushing()) {
info.setPushing(false);
}
if (info.isRecording()) {
info.setPushing(false);
}
streamSession.put(info, null);
// 删除会话缓存
streamSession.remove(info.getDeviceId(), info.getChannelId(), stream, info.getSsrc());
// 删除invite缓存
inviteService.removeInviteInfo(info.getType(), info.getDeviceId(), info.getChannelId(), info.getStream());
}
}
} catch (ParseException | SipException | InvalidArgumentException e) {
e.printStackTrace();
}
}
}
@Override
public void streamByeCmd(String deviceId, String channelId, String stream, String ssrc) {
SipDevice dev = sipDeviceService.selectSipDeviceBySipId(deviceId);
if (dev == null) {
log.error("[发送BYE] device is null");
return;
}
streamByeCmd(dev, channelId, stream, ssrc);
}
private ClientTransaction transmitRequest(Request request) throws SipException {
log.info("transmitRequest:{}", request);
ClientTransaction clientTransaction = sipserver.getNewClientTransaction(request);
clientTransaction.sendRequest();
return clientTransaction;
}
private String buildRequestContent(SipConfig sipConfig, MediaServer mediaInfo, VideoSessionInfo info) {
String streamMode = info.getStreamMode();
StringBuilder content = new StringBuilder(200);
content.append("v=0\r\n");
switch (info.getType()) {
case play:
content.append("o=").append(info.getChannelId()).append(" 0 0 IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
content.append("s=Play\r\n");
content.append("c=IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
content.append("t=0 0\r\n");
break;
case playrecord:
content.append("o=").append(info.getChannelId()).append(" 0 0 IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
content.append("s=Play\r\n");
content.append("c=IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
content.append("t=0 0\r\n");
break;
case playback:
content.append("o=").append(info.getChannelId()).append(" 0 0 IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
content.append("s=Playback\r\n");
content.append("u=").append(info.getChannelId()).append(":0\r\n");
content.append("c=IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
content.append("t=").append(info.getStartTime()).append(" ").append(info.getEndTime()).append("\r\n");
break;
case download:
content.append("o=").append(info.getChannelId()).append(" 0 0 IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
content.append("s=Download\r\n");
content.append("u=").append(info.getChannelId()).append(":0\r\n");
content.append("c=IN IP4 ").append(mediaInfo.getIp()).append("\r\n");
content.append("t=").append(info.getStartTime()).append(" ").append(info.getEndTime()).append("\r\n");
break;
}
if (sipConfig.getSeniorsdp() != null && sipConfig.getSeniorsdp() == 1) {
if ("TCP-PASSIVE".equals(streamMode)) {
content.append("m=video ").append(info.getPort()).append(" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
} else if ("TCP-ACTIVE".equals(streamMode)) {
content.append("m=video ").append(info.getPort()).append(" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
} else if ("UDP".equals(streamMode)) {
content.append("m=video ").append(info.getPort()).append(" 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");
content.append("a=fmtp:126 profile-level-id=42e01e\r\n");
content.append("a=rtpmap:126 H264/90000\r\n");
content.append("a=rtpmap:125 H264S/90000\r\n");
content.append("a=fmtp:125 profile-level-id=42e01e\r\n");
content.append("a=rtpmap:99 MP4V-ES/90000\r\n");
content.append("a=fmtp:99 profile-level-id=3\r\n");
content.append("a=rtpmap:98 H264/90000\r\n");
content.append("a=rtpmap:97 MPEG4/90000\r\n");
if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
content.append("a=setup:passive\r\n");
content.append("a=connection:new\r\n");
} else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
content.append("a=setup:active\r\n");
content.append("a=connection:new\r\n");
}
} else {
switch (streamMode) {
case "TCP-PASSIVE":
content.append("m=video ").append(info.getPort()).append(" TCP/RTP/AVP 96 97 98 99\r\n");
break;
case "TCP-ACTIVE":
content.append("m=video ").append(info.getPort()).append(" TCP/RTP/AVP 96 97 98 99\r\n");
break;
case "UDP":
//content.append("m=video ").append(info.getPort()).append(" RTP/AVP 96 97 98 99\r\n");
content.append("m=video ").append(info.getPort()).append(" RTP/AVP 96 97 98\r\n");
break;
}
content.append("a=recvonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
content.append("a=rtpmap:97 MPEG4/90000\r\n");
content.append("a=rtpmap:98 H264/90000\r\n");
//content.append("a=rtpmap:99 H265/90000\r\n");
if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式
content.append("a=setup:passive\r\n");
content.append("a=connection:new\r\n");
} else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式
content.append("a=setup:active\r\n");
content.append("a=connection:new\r\n");
}
}
if (info.getType() == SessionType.download) {
content.append("a=downloadspeed:").append(info.getDownloadSpeed()).append("\r\n");
}
content.append("y=").append(info.getSsrc()).append("\r\n");// ssrc
return content.toString();
}
}

View File

@ -0,0 +1,76 @@
package com.fastbee.sip.server.msg;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fastbee.sip.enums.AlarmMethod;
import com.fastbee.sip.enums.AlarmType;
import com.fastbee.sip.server.SipMessage;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.Optional;
@Getter
@Setter
@ToString
public class Alarm implements SipMessage {
@JacksonXmlProperty(localName = "DeviceID")
private String deviceId;
@JacksonXmlProperty(localName = "AlarmPriority")
private String alarmPriority;
@JacksonXmlProperty(localName = "AlarmTime")
private String alarmTime;
@JacksonXmlProperty(localName = "AlarmMethod")
private AlarmMethod alarmMethod;
@JacksonXmlProperty(localName = "Longitude")
private Float longitude;
@JacksonXmlProperty(localName = "Latitude")
private Float latitude;
@JacksonXmlProperty(localName = "AlarmDescription")
private String description;
@JacksonXmlProperty(localName = "Info")
private Info info;
@JacksonXmlProperty(localName = "SN")
private String sn;
@Getter
@Setter
public static class Info {
@JacksonXmlProperty(localName = "AlarmType")
private String alarmType;
@JacksonXmlProperty(localName = "AlarmTypeParam")
private AlarmTypeParam alarmTypeParam;
public Optional<AlarmType> getAlarmTypeEnum(AlarmMethod method) {
return AlarmType.of(method, alarmType);
}
}
@Getter
@Setter
public static class AlarmTypeParam {
//1-进入区域;2-离开区域
@JacksonXmlProperty(localName = "EventType")
private String eventType;
}
@Override
public String getDeviceId() {
return this.deviceId;
}
@Override
public String getSn() {
return this.sn;
}
}

View File

@ -0,0 +1,104 @@
package com.fastbee.sip.server.msg;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fastbee.sip.model.GB28181DeviceChannel;
import com.fastbee.sip.server.SipMessage;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import static com.fastbee.sip.util.SipUtil.safeString;
@Getter
@Setter
public class CatalogInfo implements SipMessage {
@JacksonXmlProperty(localName = "DeviceID")
private String deviceId;
@JacksonXmlProperty(localName = "SN")
private String sn;
@JacksonXmlProperty(localName = "SumNum")
private String _sumNum;
@JacksonXmlProperty(localName = "DeviceList")
private List<GB28181DeviceChannel> channelList;
@Override
public String getDeviceId() {
return null;
}
@Override
public String getSn() {
return null;
}
@Override
public int totalPart() {
return getSumNum();
}
@Override
public int numberOfPart() {
return channelList == null ? 0 : channelList.size();
}
public int getSumNum() {
return _sumNum == null ? 0 : Integer.parseInt(_sumNum);
}
public void setSumNum(int sumNum) {
this._sumNum = String.valueOf(sumNum);
}
public String toXml(String charset) {
StringBuilder body = new StringBuilder("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\n" +
"<Response>\n" +
"<CmdType>Catalog</CmdType>\n" +
"<SN>" + getSn() + "</SN>\n" +
"<DeviceID>" + getDeviceId() + "</DeviceID>\n" +
"<Result>OK</Result>\n" +
"<SumNum>" + getSumNum() + "</SumNum>\n" +
"<DeviceList Num=\"" + getChannelList().size() + "\">");
for (GB28181DeviceChannel channel : getChannelList()) {
body
.append("<Item>\n")
.append("<DeviceID>").append(channel.getChannelId()).append("</DeviceID>\n")
.append("<Name>").append(safeString(channel.getName())).append("</Name>\n")
.append("<Manufacturer>").append(safeString(channel.getManufacturer())).append("</Manufacturer>\n")
.append("<Model>").append(safeString(channel.getModel())).append("</Model>\n")
.append("<Owner>").append(safeString(channel.getOwner())).append("</Owner>\n")
.append("<CivilCode>").append(safeString(channel.getCivilCode())).append("</CivilCode>\n")
.append("<Block>").append(safeString(channel.getBlock())).append("</Block>\n")
.append("<Address>").append(safeString(channel.getAddress())).append("</Address>\n")
.append("<Parental>").append(safeString(channel.getParental())).append("</Parental>\n")
.append("<ParentID>").append(safeString(channel.getParentId())).append("</ParentID>\n")
.append("<SafetyWay>").append(safeString(channel.getSafetyWay())).append("</SafetyWay>\n")
.append("<RegisterWay>").append(safeString(channel.getRegisterWay())).append("</RegisterWay>\n")
.append("<CertNum>").append(safeString(channel.getCertNum())).append("</CertNum>\n")
.append("<Certifiable>").append(safeString(channel.getCertifiable())).append("</Certifiable>\n")
.append("<ErrCode>").append(safeString(channel.getErrCode())).append("</ErrCode>\n")
.append("<EndTime>").append(safeString(channel.getEndTime())).append("</EndTime>\n")
.append("<Secrecy>").append(safeString(channel.getSecrecy())).append("</Secrecy>\n")
.append("<IPAddress>").append(safeString(channel.getIpAddress())).append("</IPAddress>\n")
.append("<Port>").append(safeString(channel.getPort())).append("</Port>\n")
.append("<Password>").append(safeString(channel.getPassword())).append("</Password>\n")
.append("<Status>").append(safeString(channel.getStatus().getCode())).append("</Status>\n")
.append("<Longitude>").append(safeString(channel.getLongitude())).append("</Longitude>\n")
.append("<Latitude>").append(safeString(channel.getLatitude())).append("</Latitude>\n");
if (channel.getInfo() != null) {
body.append("<Info>")
.append(channel.getInfo().toXML())
.append("</Info>\n");
}
body.append("</Item>\n");
}
body.append("</DeviceList>\n</Response>");
return body.toString();
}
}

View File

@ -0,0 +1,130 @@
package com.fastbee.sip.server.msg;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fastbee.sip.server.SipMessage;
import lombok.Getter;
import lombok.Setter;
import java.util.StringJoiner;
@Getter
@Setter
public class ConfigDownload implements SipMessage {
@JacksonXmlProperty(localName = "DeviceID")
private String deviceId;
@JacksonXmlProperty(localName = "SN")
private String sn;
@JacksonXmlProperty(localName = "ConfigType")
private String configType;
@JacksonXmlProperty(localName = "BasicParam")
private BasicParam basicParam;
@JacksonXmlProperty(localName = "VideoParamOp")
private VideoParamOp videoParamOp;
public enum ConfigType {
BasicParam,
VideoParamOpt,
SVACEncodeConfig,
SVACDecodeConfig
}
@Getter
@Setter
public static class BasicParam {
@JacksonXmlProperty(localName = "Name")
private String name;
//注册过期时间
@JacksonXmlProperty(localName = "Expiration")
private String expiration;
//心跳间隔时间
@JacksonXmlProperty(localName = "HeartBeatInterval")
private int heartBeatInterval;
//心跳超时次数
@JacksonXmlProperty(localName = "HeartBeatCount")
private int heartBeatCount = 5;
//定位功能支持情况,取值:0-不支持;1-支持 GPS定位;2-支持北斗定位(可选, 默认取值为0)
@JacksonXmlProperty(localName = "PositionCapability")
private int positionCapability;
//经度
@JacksonXmlProperty(localName = "Longitude")
private float longitude;
//纬度
@JacksonXmlProperty(localName = "Latitude")
private float latitude;
public String toXml() {
StringJoiner joiner = new StringJoiner("\n");
joiner.add("<Name>" + name + "</Name>");
joiner.add("<Expiration>" + expiration + "</Expiration>");
joiner.add("<HeartBeatInterval>" + heartBeatInterval + "</HeartBeatInterval>");
joiner.add("<HeartBeatCount>" + heartBeatCount + "</HeartBeatCount>");
joiner.add("<PositionCapability>" + positionCapability + "</PositionCapability>");
joiner.add("<Longitude>" + longitude + "</Longitude>");
joiner.add("<Latitude>" + latitude + "</Latitude>");
return joiner.toString();
}
}
@Getter
@Setter
public static class VideoParamOp {
@JacksonXmlProperty(localName = "DownloadSpeed")
private String downloadSpeed;
@JacksonXmlProperty(localName = "Resolution")
private String resolution;
}
public String toXml(int sn, String charset) {
StringJoiner joiner = new StringJoiner("\r\n");
joiner.add("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>");
joiner.add("<Query>");
joiner.add("<CmdType>ConfigDownload</CmdType>");
joiner.add("<SN>" + sn + "</SN>");
joiner.add("<DeviceID>" + deviceId + "</DeviceID>");
if (configTypeIs(ConfigType.BasicParam) && getBasicParam() != null) {
joiner.add("<BasicParam>" + getBasicParam().toXml() + "</BasicParam>");
}
joiner.add("<Info></Info>");
joiner.add("</Query>");
return joiner.toString();
}
@Override
public String toString() {
return JSON.toJSONString(this, SerializerFeature.PrettyFormat);
}
public boolean configTypeIs(ConfigType type) {
return type.name().equals(configType);
}
@Override
public String getDeviceId() {
return null;
}
@Override
public String getSn() {
return null;
}
}

View File

@ -0,0 +1,163 @@
package com.fastbee.sip.server.msg;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fastbee.sip.enums.Direct;
import com.fastbee.sip.server.SipMessage;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.util.StringUtils;
import java.util.Map;
import java.util.StringJoiner;
@Getter
@Setter
@ToString
public class DeviceControl implements SipMessage {
@JacksonXmlProperty(localName = "DeviceID")
private String deviceId;
@JacksonXmlProperty(localName = "SN")
private String sn;
@JacksonXmlProperty(localName = "PTZCmd")
private String ptzCmd;
@JacksonXmlProperty(localName = "RecordCmd")
private String recordCmd;
@JacksonXmlProperty(localName = "GuardCmd")
private String guardCmd;
@JacksonXmlProperty(localName = "AlarmCmd")
private String alarmCmd;
@JacksonXmlProperty(localName = "IFameCmd")
private String iFameCmd;
@JacksonXmlProperty(localName = "DragZoomIn")
private DragZoom dragZoomIn;
@JacksonXmlProperty(localName = "DragZoomOut")
private DragZoom dragZoomOut;
@Getter
@Setter
public static class DragZoom {
//播放窗口长度像素值
@JacksonXmlProperty(localName = "Length")
private int length;
//播放窗口宽度像素值
@JacksonXmlProperty(localName = "Width")
private int width;
//拉框中心的横轴坐标像素值
@JacksonXmlProperty(localName = "MidPointX")
private int midPointX;
//拉框中心的纵轴坐标像素值
@JacksonXmlProperty(localName = "MidPointY")
private int midPointY;
//拉框长度像素值
@JacksonXmlProperty(localName = "LengthX")
private int lengthX;
//拉框宽度像素值
@JacksonXmlProperty(localName = "LengthY")
private int lengthY;
}
//看守位控制命令
@Getter
@Setter
public static class HomePosition {
//看守位使能1:开启,0:关闭
@JacksonXmlProperty(localName = "Enabled")
private int enabled;
//自动归位时间间隔,开启看守位时使用,单位:秒(s)
@JacksonXmlProperty(localName = "ResetTime")
private Integer resetTime;
//调用预置位编号,开启看守位时使用,取值范围0~255
@JacksonXmlProperty(localName = "PresetIndex")
private Integer presetIndex;
}
@Getter
@Setter
public static class AlarmCmdInfo {
//复位报警的报警方式属性
@JacksonXmlProperty(localName = "AlarmMethod")
private String alarmMethod;
@JacksonXmlProperty(localName = "AlarmType")
private String alarmType;
}
public DeviceControl setPtzDirect(Map<Direct, Integer> directAndSpeed) {
int code = 0;
StringBuilder cmd = new StringBuilder("A50F4D");
for (Map.Entry<Direct, Integer> entry : directAndSpeed.entrySet()) {
code = entry.getKey().merge(code);
}
//控制码
cmd.append(String.format("%02X", code), 0, 2);
//水平控制速度
int lrSpeed = directAndSpeed.getOrDefault(Direct.LEFT, directAndSpeed.getOrDefault(Direct.RIGHT, 0));
cmd.append(String.format("%02X", lrSpeed), 0, 2);
//垂直控制速度
int udSpeed = directAndSpeed.getOrDefault(Direct.UP, directAndSpeed.getOrDefault(Direct.DOWN, 0));
cmd.append(String.format("%02X", udSpeed), 0, 2);
//缩放控制速度
int zoomSpeed = directAndSpeed.getOrDefault(Direct.ZOOM_IN, directAndSpeed.getOrDefault(Direct.ZOOM_OUT, 0)) & 0xF;
cmd.append(String.format("%X", zoomSpeed), 0, 1)
.append("0");
//校验码
int checkCode = (0XA5 + 0X0F + 0X4D + code + lrSpeed + udSpeed + (zoomSpeed << 4)) % 256;
cmd.append(String.format("%02X", checkCode), 0, 2);
setPtzCmd(cmd.toString());
return this;
}
public String toXml(int sn, String charset) {
StringJoiner joiner = new StringJoiner("\n");
joiner.add("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>");
joiner.add("<Control>");
joiner.add("<CmdType>DeviceControl</CmdType>");
joiner.add("<SN>" + sn + "</SN>");
joiner.add("<DeviceID>" + deviceId + "</DeviceID>");
if (isPtzControl()) {
joiner.add("<PTZCmd>" + getPtzCmd() + "</PTZCmd>");
}
joiner.add("<Info>");
joiner.add("<ControlPriority>10</ControlPriority>");
joiner.add("</Info>");
joiner.add("</Control>");
return joiner.toString();
}
@Override
public String getDeviceId() {
return null;
}
@Override
public String getSn() {
return null;
}
public boolean isPtzControl() {
return StringUtils.hasText(ptzCmd);
}
}

View File

@ -0,0 +1,104 @@
package com.fastbee.sip.server.msg;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fastbee.sip.server.SipMessage;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
@Getter
@Setter
@ToString
public class GB28181Device implements SipMessage {
@JsonProperty("DeviceID")
private String id;
/**
* 设备名
*/
@JsonProperty("DeviceName")
private String name;
/**
* 生产厂商
*/
@JsonProperty("Manufacturer")
private String manufacturer;
/**
* 型号
*/
@JsonProperty("Model")
private String model;
/**
* 固件版本
*/
@JsonProperty("Firmware")
private String firmware;
/**
* 传输协议
* UDP/TCP
*/
private String transport;
/**
* 数据流传输模式
*/
private StreamMode streamMode = StreamMode.UDP;
/**
* 访问地址
*/
private String host;
/**
* 访问端口
*/
private int port;
/**
* 是否在线
*/
private boolean online;
@JsonProperty("Channel")
private int channelNumber;
/**
* 通道列表
*/
private List<String> channelList;
@JsonProperty("SN")
private String sn;
/**
* 心跳间隔
*/
private long heartBeatInterval = 300;
@Override
public String getDeviceId() {
return null;
}
@Override
public String getSn() {
return null;
}
public enum StreamMode {
UDP,
TCP_ACTIVE,//主动模式
TCP_PASSIVE//被动模式
}
public String getHostAndPort() {
return getHost() + ":" + getPort();
}
}

View File

@ -0,0 +1,29 @@
package com.fastbee.sip.server.msg;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fastbee.sip.server.SipMessage;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class KeepaliveMessage implements SipMessage {
@JacksonXmlProperty(localName = "DeviceID")
private String deviceId;
@JacksonXmlProperty(localName = "Status")
private String status;
@JacksonXmlProperty(localName = "SN")
private String sn;
@Override
public String getDeviceId() {
return null;
}
@Override
public String getSn() {
return null;
}
}

View File

@ -0,0 +1,11 @@
package com.fastbee.sip.service;
import com.alibaba.fastjson2.JSONObject;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import java.util.List;
public interface IGatewayService {
void sendFunction(String deviceID,List<ThingsModelSimpleItem> functinos);
void sendFunction(String deviceID,String identifier,String value);
}

View File

@ -0,0 +1,26 @@
package com.fastbee.sip.service;
import com.fastbee.sip.enums.SessionType;
import com.fastbee.sip.model.InviteInfo;
import com.fastbee.sip.model.VideoSessionInfo;
import java.util.List;
public interface IInviteService {
void updateInviteInfo(VideoSessionInfo sinfo, InviteInfo inviteInfo);
InviteInfo getInviteInfo(SessionType type,
String deviceId,
String channelId,
String stream);
List<InviteInfo> getInviteInfoAll(SessionType type, String deviceId, String channelId, String stream);
InviteInfo getInviteInfoBySSRC(String ssrc);
void removeInviteInfo(SessionType type,
String deviceId,
String channelId,
String stream);
}

View File

@ -0,0 +1,80 @@
package com.fastbee.sip.service;
import com.alibaba.fastjson.JSONObject;
import com.fastbee.sip.domain.MediaServer;
import com.fastbee.sip.domain.SipConfig;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.model.VideoSessionInfo;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
* 流媒体服务器配置Service接口
*
* @author zhuangpeng.li
* @date 2022-11-30
*/
public interface IMediaServerService
{
/**
* 查询流媒体服务器配置
*
* @param id 流媒体服务器配置主键
* @return 流媒体服务器配置
*/
public MediaServer selectMediaServerById(Long id);
/**
* 查询流媒体服务器配置
*
* @return 流媒体服务器配置
*/
List<MediaServer> selectMediaServer();
MediaServer selectMediaServerBytenantId(Long tenantId);
MediaServer selectMediaServerBydeviceSipId(String deviceSipId);
/**
* 查询流媒体服务器配置列表
*
* @param mediaServer 流媒体服务器配置
* @return 流媒体服务器配置集合
*/
List<MediaServer> selectMediaServerList(MediaServer mediaServer);
/**
* 新增流媒体服务器配置
*
* @param mediaServer 流媒体服务器配置
* @return 结果
*/
int insertMediaServer(MediaServer mediaServer);
/**
* 修改流媒体服务器配置
*
* @param mediaServer 流媒体服务器配置
* @return 结果
*/
int updateMediaServer(MediaServer mediaServer);
boolean syncMediaServer(MediaServer mediaServer,String secret);
/**
* 批量删除流媒体服务器配置
*
* @param ids 需要删除的流媒体服务器配置主键集合
* @return 结果
*/
int deleteMediaServerByIds(Long[] ids);
/**
* 删除流媒体服务器配置信息
*
* @param id 流媒体服务器配置主键
* @return 结果
*/
int deleteMediaServerById(Long id);
JSONObject getMediaList(String schema, String stream);
JSONObject listRtpServer();
VideoSessionInfo createRTPServer(SipConfig sipConfig, MediaServer mediaInfo, SipDevice device, VideoSessionInfo videoSessionInfo);
MediaServer checkMediaServer(String ip, Long port, String secret);
}

View File

@ -0,0 +1,18 @@
package com.fastbee.sip.service;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.sip.domain.SipDevice;
import com.fastbee.sip.domain.SipDeviceChannel;
import com.fastbee.sip.model.RecordList;
import com.fastbee.sip.server.msg.Alarm;
import java.util.List;
public interface IMqttService {
void publishInfo(SipDevice device);
void publishStatus(SipDevice device, int deviceStatus);
void publishEvent(Alarm alarm);
void publishProperty(Long productId, String deviceNum, List<ThingsModelSimpleItem> thingsList, int delay);
void publishChannelsProperty(String DeviceSipId, List<SipDeviceChannel> channels);
void publishRecordsProperty(String DeviceSipId, RecordList recordList);
}

View File

@ -0,0 +1,20 @@
package com.fastbee.sip.service;
import com.fastbee.sip.model.Stream;
public interface IPlayService {
Stream play(String deviceId, String channelId, boolean record);
Stream playback(String deviceId, String channelId, String startTime, String endTime);
String closeStream(String deviceId, String channelId, String streamId);
String playbackPause(String deviceId, String channelId, String streamId);
String playbackReplay(String deviceId, String channelId, String streamId);
String playbackSeek(String deviceId, String channelId, String streamId, long seektime);
String playbackSpeed(String deviceId, String channelId, String streamId, Integer speed);
}

View File

@ -0,0 +1,7 @@
package com.fastbee.sip.service;
import com.fastbee.sip.enums.Direct;
public interface IPtzCmdService {
public boolean directPtzCmd(String deviceId, String channelId, Direct direct, Integer speed);
}

View File

@ -0,0 +1,30 @@
package com.fastbee.sip.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fastbee.sip.model.RecordItem;
import com.fastbee.sip.model.RecordList;
import com.fastbee.sip.model.Stream;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
public interface IRecordService {
RecordList listDevRecord(String deviceId, String channelId, String startTime, String endTime);
List<RecordItem> listRecord(String channelId, String sn);
JSONObject listServerRecord(String recordApi, Integer pageNum, Integer pageSize);
JSONArray listServerRecordByDate(String recordApi, Integer year, Integer month, String app, String stream);
JSONObject listServerRecordByStream(String recordApi, Integer pageNum, Integer pageSize, String app);
JSONObject listServerRecordByApp(String recordApi, Integer pageNum, Integer pageSize);
JSONObject listServerRecordByFile(String recordApi, Integer pageNum, Integer pageSize, String app, String stream, String startTime, String endTime);
JSONObject listServerRecordByDevice(Integer pageNum, Integer pageSize, String deviceId, String channelId, String startTime, String endTime);
boolean startRecord(String stream);
boolean stopRecord(String stream);
boolean isRecording(String stream);
JSONObject getMp4RecordFile(String stream,String period);
Stream download(String deviceId, String channelId,
String startTime, String endTime, int downloadSpeed);
JSONObject upload(String recordApi, String file);
Stream playRecord(String deviceId, String channelId);
}

View File

@ -0,0 +1,12 @@
package com.fastbee.sip.service;
import com.fastbee.sip.model.RecordList;
import com.fastbee.sip.model.ZlmMediaServer;
public interface ISipCacheService {
Long getCSEQ(String serverSipId);
void updateMediaInfo(ZlmMediaServer mediaServerConfig);
void setRecordList(String key, RecordList recordList);
}

Some files were not shown because too many files have changed in this diff Show More