增加sip会议视频接入能力

sip
648540858 2023-06-07 15:29:52 +08:00
parent db3240d918
commit 508ee15066
28 changed files with 1834 additions and 11 deletions

View File

@ -279,8 +279,50 @@ create table wvp_user_role (
update_time character varying(50) update_time character varying(50)
); );
create table wvp_sip_server (
id serial primary key ,
local_ip character varying(50) not null ,
local_port integer,
server_ip character varying(50) not null ,
server_port integer,
create_time character varying(50),
update_time character varying(50),
transport character varying(50),
status bool default false,
constraint uk_sip_server_server_ip_server_port unique (server_ip, server_port)
);
create table wvp_sip_server_account (
id serial primary key ,
sip_server_id integer,
username character varying(50) not null ,
password character varying(50) not null ,
device_channel_id integer ,
push_stream_id integer,
proxy_stream_id integer,
create_time character varying(50),
update_time character varying(50),
status bool default false,
constraint uk_sip_server_account_username unique (username)
);
create table wvp_sip_video (
id serial primary key ,
sip_server_id integer,
sip_account_id integer,
media_server_id character varying(50) not null ,
request_no character varying(50) not null ,
create_time character varying(50),
update_time character varying(50),
auto_reconnect_on_reboot bool default false,
status bool default false,
constraint uk_sip_server_account_username unique (request_no)
);
/*初始数据*/ /*初始数据*/
INSERT INTO wvp_user VALUES (1, 'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57','3e80d1762a324d5b0ff636e0bd16f1e3'); INSERT INTO wvp_user VALUES (1, 'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57','3e80d1762a324d5b0ff636e0bd16f1e3');
INSERT INTO wvp_user_role VALUES (1, 'admin','0','2021-04-13 14:14:57','2021-04-13 14:14:57'); INSERT INTO wvp_user_role VALUES (1, 'admin','0','2021-04-13 14:14:57','2021-04-13 14:14:57');

View File

@ -64,7 +64,7 @@ public class SipLayer implements CommandLineRunner {
try { try {
sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties(monitorIp, userSetting.getSipLog())); sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(DefaultProperties.getProperties(monitorIp, userSetting.getSipLog()));
} catch (PeerUnavailableException e) { } catch (PeerUnavailableException e) {
logger.error("[Sip Server] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp); logger.error("[国标28181服务] 启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp);
return; return;
} }
@ -76,12 +76,12 @@ public class SipLayer implements CommandLineRunner {
tcpSipProvider.addSipListener(sipProcessorObserver); tcpSipProvider.addSipListener(sipProcessorObserver);
tcpSipProviderMap.put(monitorIp, tcpSipProvider); tcpSipProviderMap.put(monitorIp, tcpSipProvider);
logger.info("[Sip Server] tcp://{}:{} 启动成功", monitorIp, port); logger.info("[国标28181服务] tcp://{}:{} 启动成功", monitorIp, port);
} catch (TransportNotSupportedException } catch (TransportNotSupportedException
| TooManyListenersException | TooManyListenersException
| ObjectInUseException | ObjectInUseException
| InvalidArgumentException e) { | InvalidArgumentException e) {
logger.error("[Sip Server] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" logger.error("[国标28181服务] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确"
, monitorIp, port); , monitorIp, port);
} }
@ -93,12 +93,12 @@ public class SipLayer implements CommandLineRunner {
udpSipProviderMap.put(monitorIp, udpSipProvider); udpSipProviderMap.put(monitorIp, udpSipProvider);
logger.info("[Sip Server] udp://{}:{} 启动成功", monitorIp, port); logger.info("[国标28181服务] udp://{}:{} 启动成功", monitorIp, port);
} catch (TransportNotSupportedException } catch (TransportNotSupportedException
| TooManyListenersException | TooManyListenersException
| ObjectInUseException | ObjectInUseException
| InvalidArgumentException e) { | InvalidArgumentException e) {
logger.error("[Sip Server] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" logger.error("[国标28181服务] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确"
, monitorIp, port); , monitorIp, port);
} }
} }

View File

@ -1,6 +1,5 @@
package com.genersoft.iot.vmp.gb28181.bean; package com.genersoft.iot.vmp.gb28181.bean;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse; import gov.nist.javax.sip.message.SIPResponse;
public class SipTransactionInfo { public class SipTransactionInfo {

View File

@ -50,10 +50,6 @@ public class SipUtils {
return uri.getUser(); return uri.getUser();
} }
public static String getNewViaTag() {
return "z9hG4bK" + System.currentTimeMillis();
}
public static UserAgentHeader createUserAgentHeader(GitUtil gitUtil) throws PeerUnavailableException, ParseException { public static UserAgentHeader createUserAgentHeader(GitUtil gitUtil) throws PeerUnavailableException, ParseException {
List<String> agentParam = new ArrayList<>(); List<String> agentParam = new ArrayList<>();
agentParam.add("WVP-Pro "); agentParam.add("WVP-Pro ");
@ -69,6 +65,10 @@ public class SipUtils {
return SipFactory.getInstance().createHeaderFactory().createUserAgentHeader(agentParam); return SipFactory.getInstance().createHeaderFactory().createUserAgentHeader(agentParam);
} }
public static String getNewViaTag() {
return "z9hG4bK" + System.currentTimeMillis();
}
public static String getNewFromTag(){ public static String getNewFromTag(){
return UUID.randomUUID().toString().replace("-", ""); return UUID.randomUUID().toString().replace("-", "");

View File

@ -0,0 +1,42 @@
package com.genersoft.iot.vmp.sip;
import com.genersoft.iot.vmp.sip.bean.SipEvent;
import com.genersoft.iot.vmp.sip.bean.SipServer;
import com.genersoft.iot.vmp.sip.bean.SipServerAccount;
import com.genersoft.iot.vmp.sip.utils.SIPRequestFactory;
import com.genersoft.iot.vmp.sip.utils.SipUtils;
import gov.nist.javax.sip.SipProviderImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import javax.sip.header.WWWAuthenticateHeader;
import javax.sip.message.Request;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
@Component
public class SipCommander {
@Autowired
private SipSender sipSender;
public void register(SipServer server, SipServerAccount account, SipProviderImpl sipProvider, boolean isRegister,
SipEvent okEvent, SipEvent errorEvent)
throws InvalidArgumentException, SipException, ParseException, NoSuchAlgorithmException {
register(server, account, sipProvider, null, isRegister, okEvent, errorEvent);
}
public void register(SipServer server, SipServerAccount account, SipProviderImpl sipProvider,
WWWAuthenticateHeader wwwAuthenticateHeader, boolean isRegister,
SipEvent okEvent, SipEvent errorEvent)
throws InvalidArgumentException, SipException, ParseException, NoSuchAlgorithmException {
if (server == null || account == null || sipProvider == null) {
return;
}
String callId = SipUtils.getNewCallId(sipProvider);
Request request = SIPRequestFactory.createRegisterRequest(server, account, callId, isRegister, wwwAuthenticateHeader);
sipSender.transmitRequest(sipProvider, request, okEvent, errorEvent);
}
}

View File

@ -0,0 +1,73 @@
package com.genersoft.iot.vmp.sip;
import com.genersoft.iot.vmp.sip.bean.SipEvent;
import com.genersoft.iot.vmp.sip.service.SipSubscribe;
import com.genersoft.iot.vmp.sip.utils.SipUtils;
import com.genersoft.iot.vmp.utils.GitUtil;
import gov.nist.javax.sip.SipProviderImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sip.SipException;
import javax.sip.header.CallIdHeader;
import javax.sip.header.UserAgentHeader;
import javax.sip.message.Message;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.text.ParseException;
/**
*
* @author lin
*/
@Component
public class SipSender {
private Logger logger = LoggerFactory.getLogger(SipSender.class);
@Autowired
private GitUtil gitUtil;
public void transmitRequest(SipProviderImpl sipProvider, Message message) throws SipException, ParseException {
transmitRequest(sipProvider, message, null, null);
}
public void transmitRequest(SipProviderImpl sipProvider, String ip, Message message, SipEvent errorEvent) throws SipException, ParseException {
transmitRequest(sipProvider, message, null, errorEvent );
}
public void transmitRequest(SipProviderImpl sipProvider, Message message, SipEvent okEvent, SipEvent errorEvent) throws SipException, ParseException {
if (message.getHeader(UserAgentHeader.NAME) == null) {
try {
message.addHeader(SipUtils.createUserAgentHeader(gitUtil));
} catch (ParseException e) {
logger.error("添加UserAgentHeader失败", e);
}
}
CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME);
// 添加错误订阅
if (errorEvent != null) {
SipSubscribe.getInstance().addErrorSubscribe(callIdHeader.getCallId(), (code, msg, data) -> {
errorEvent.response(code, msg, data);
SipSubscribe.getInstance().removeErrorSubscribe(callIdHeader.getCallId());
SipSubscribe.getInstance().removeOkSubscribe(callIdHeader.getCallId());
});
}
// 添加订阅
if (okEvent != null) {
SipSubscribe.getInstance().addOkSubscribe(callIdHeader.getCallId(), (code, msg, data) -> {
okEvent.response(code, msg, data);
SipSubscribe.getInstance().removeOkSubscribe(callIdHeader.getCallId());
SipSubscribe.getInstance().removeErrorSubscribe(callIdHeader.getCallId());
});
}
if (message instanceof Request) {
sipProvider.sendRequest((Request)message);
}else if (message instanceof Response) {
sipProvider.sendResponse((Response)message);
}
}
}

View File

@ -0,0 +1,9 @@
package com.genersoft.iot.vmp.sip;
import org.springframework.stereotype.Component;
@Component
public class SipSessionManager {
}

View File

@ -0,0 +1,6 @@
package com.genersoft.iot.vmp.sip.bean;
public interface ResultCallback<T> {
void run(int code, String msg, T data);
}

View File

@ -0,0 +1,6 @@
package com.genersoft.iot.vmp.sip.bean;
public interface SipEvent {
void response(int code, String msg, Object data);
}

View File

@ -0,0 +1,4 @@
package com.genersoft.iot.vmp.sip.bean;
public class SipResult {
}

View File

@ -0,0 +1,132 @@
package com.genersoft.iot.vmp.sip.bean;
/**
* sip
*/
public class SipServer {
private int id;
/**
* IP
*/
private String localIp;
/**
*
*/
private Integer localPort;
/**
* IP
*/
private String serverIp;
/**
*
*/
private Integer serverPort;
/**
* UDP UDP/TCP
*/
private String transport;
/**
*
*/
private boolean status;
/**
*
*/
private String createTime;
/**
*
*/
private String updateTime;
public static SipServer getInstance(String serverIp, Integer serverPort, String localIp, Integer localPort, String transport) {
SipServer sipServer = new SipServer();
sipServer.setTransport(transport);
sipServer.setServerIp(serverIp);
sipServer.setServerPort(serverPort);
sipServer.setLocalIp(localIp);
sipServer.setLocalPort(localPort);
return sipServer;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLocalIp() {
return localIp;
}
public void setLocalIp(String localIp) {
this.localIp = localIp;
}
public Integer getLocalPort() {
return localPort;
}
public void setLocalPort(Integer localPort) {
this.localPort = localPort;
}
public String getServerIp() {
return serverIp;
}
public void setServerIp(String serverIp) {
this.serverIp = serverIp;
}
public Integer getServerPort() {
return serverPort;
}
public void setServerPort(Integer serverPort) {
this.serverPort = serverPort;
}
public String getTransport() {
return transport;
}
public void setTransport(String transport) {
this.transport = transport;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
}

View File

@ -0,0 +1,163 @@
package com.genersoft.iot.vmp.sip.bean;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* sipsipsip
*/
public class SipServerAccount {
/**
* ID
*/
private Integer id;
/**
* SIPID
*/
private Integer sipServerId;
/**
*
*/
private String username;
/**
*
*/
private String password;
/**
* sip
* deviceChannelId/pushStreamId/proxyStreamId
*/
private Integer deviceChannelId;
/**
* sip
* deviceChannelId/pushStreamId/proxyStreamId
*/
private Integer pushStreamId;
/**
* sip
* deviceChannelId/pushStreamId/proxyStreamId
*/
private Integer proxyStreamId;
/**
*
*/
private boolean status;
/**
*
*/
private String createTime;
/**
*
*/
private String updateTime;
private final List<SipVideo> sipVideoList = new CopyOnWriteArrayList<>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getSipServerId() {
return sipServerId;
}
public void setSipServerId(Integer sipServerId) {
this.sipServerId = sipServerId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getDeviceChannelId() {
return deviceChannelId;
}
public void setDeviceChannelId(Integer deviceChannelId) {
this.deviceChannelId = deviceChannelId;
}
public Integer getPushStreamId() {
return pushStreamId;
}
public void setPushStreamId(Integer pushStreamId) {
this.pushStreamId = pushStreamId;
}
public Integer getProxyStreamId() {
return proxyStreamId;
}
public void setProxyStreamId(Integer proxyStreamId) {
this.proxyStreamId = proxyStreamId;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
public void addVideo(SipVideo sipVideo) {
sipVideoList.add(sipVideo);
}
public void removeVideoById(int id) {
if (sipVideoList.size() == 0) {
return;
}
for (int i = 0; i < sipVideoList.size(); i++) {
if (sipVideoList.get(i).getId() == id) {
sipVideoList.remove(i);
return;
}
}
}
}

View File

@ -0,0 +1,101 @@
package com.genersoft.iot.vmp.sip.bean;
/**
* sip
*/
public class SipVideo {
/**
* ID
*/
private int id;
/**
* sip ID
*/
private int sipAccountId;
/**
* 使
*/
private String mediaServerId;
/**
* sip
*/
private String requestNo;
/**
*
*/
private boolean autoReconnectOnReboot;
/**
*
*/
private String createTime;
/**
*
*/
private String updateTime;
public int getSipAccountId() {
return sipAccountId;
}
public void setSipAccountId(int sipAccountId) {
this.sipAccountId = sipAccountId;
}
public String getMediaServerId() {
return mediaServerId;
}
public void setMediaServerId(String mediaServerId) {
this.mediaServerId = mediaServerId;
}
public String getRequestNo() {
return requestNo;
}
public void setRequestNo(String requestNo) {
this.requestNo = requestNo;
}
public boolean isAutoReconnectOnReboot() {
return autoReconnectOnReboot;
}
public void setAutoReconnectOnReboot(boolean autoReconnectOnReboot) {
this.autoReconnectOnReboot = autoReconnectOnReboot;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
}

View File

@ -0,0 +1,41 @@
package com.genersoft.iot.vmp.sip.dao;
import com.genersoft.iot.vmp.sip.bean.SipServerAccount;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface SipServerAccountMapper {
@Insert("INSERT INTO wvp_sip_server_account (sip_server_id, username, password, device_channel_id, " +
"create_time, update_time, push_stream_id, proxy_stream_id, status ) " +
"VALUES (#{sipServerId}, #{username}, #{password}, #{deviceChannelId}, " +
"#{createTime}, #{updateTime}, #{pushStreamId}, #{proxyStreamId}, #{status})")
int add(SipServerAccount sipServerAccount);
@Delete("DELETE FROM wvp_sip_server_account WHERE id = #{id}")
int remove(int id);
@Update(value = {" <script>" +
"UPDATE wvp_sip_server_account " +
"SET update_time=#{updateTime}" +
"<if test='sip_server_id != null'>, sip_server_id=#{sipServerId}</if>" +
"<if test='username != null'>, username=#{username}</if>" +
"<if test='password != null'>, password=#{password}</if>" +
"<if test='device_channel_id != null'>, device_channel_id=#{deviceChannelId}</if>" +
"<if test='push_stream_id != null'>, push_stream_id=#{pushStreamId}</if>" +
"<if test='proxy_stream_id != null'>, proxy_stream_id=#{proxyStreamId}</if>" +
"<if test='status != null'>, status=#{status}</if>" +
"WHERE id = #{id}"+
" </script>"})
int update(SipServerAccount sipServerAccount);
@Select("SELECT * FROM wvp_sip_server_account WHERE id = #{id}")
SipServerAccount query(int id);
@Select("SELECT * FROM wvp_sip_server_account")
List<SipServerAccount> all(int id);
}

View File

@ -0,0 +1,41 @@
package com.genersoft.iot.vmp.sip.dao;
import com.genersoft.iot.vmp.sip.bean.SipServer;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface SipServerMapper {
@Insert("INSERT INTO wvp_sip_server (local_ip, local_port, server_ip, server_port, create_time, update_time, transport, status ) " +
"VALUES (#{localIp}, #{localPort}, #{serverIp}, #{serverPort}, #{createTime}, #{updateTime}, #{transport}, #{status})")
int add(SipServer sipServer);
@Delete("DELETE FROM wvp_sip_server WHERE id = #{sipServerId}")
int remove(int sipServerId);
@Update(value = {" <script>" +
"UPDATE wvp_sip_server " +
"SET update_time=#{updateTime}" +
"<if test='local_ip != null'>, local_ip=#{localIp}</if>" +
"<if test='local_port != null'>, local_port=#{localPort}</if>" +
"<if test='server_ip != null'>, server_ip=#{serverIp}</if>" +
"<if test='server_port != null'>, server_port=#{server_port}</if>" +
"<if test='transport != null'>, transport=#{transport}</if>" +
"<if test='status != null'>, status=#{status}</if>" +
"WHERE id = #{id}"+
" </script>"})
int update(SipServer sipServer);
@Select("SELECT * FROM wvp_sip_server WHERE id = #{sipServerId}")
SipServer query(int sipServerId);
@Select("SELECT * FROM wvp_sip_server WHERE id = #{sipServerId}")
List<SipServer> all();
}

View File

@ -0,0 +1,33 @@
package com.genersoft.iot.vmp.sip.dao;
import com.genersoft.iot.vmp.sip.bean.SipVideo;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface SipVideoMapper {
@Select("SELECT * FROM wvp_sip_video WHERE sip_server_id = #{serverId} AND sip_account_id = #{accountId}")
List<SipVideo> all(int serverId, int accountId);
@Insert("INSERT INTO wvp_sip_video (sip_server_id, sip_account_id, media_server_id, request_no, create_time, update_time, auto_reconnect_on_reboot, status ) " +
"VALUES (#{sipServerId}, #{sipAccountId}, #{mediaServerId}, #{requestNo}, #{createTime}, #{updateTime}, #{autoReconnectOnReboot}, #{status})")
int add(SipVideo sipVideo);
@Update(value = {" <script>" +
"UPDATE wvp_sip_video " +
"SET update_time=#{updateTime}" +
"<if test='media_server_id != null'>, media_server_id=#{mediaServerId}</if>" +
"<if test='request_no != null'>, request_no=#{requestNo}</if>" +
"<if test='auto_reconnect_on_reboot != null'>, auto_reconnect_on_reboot=#{autoReconnectOnReboot}</if>" +
"<if test='status != null'>, status=#{status}</if>" +
"WHERE id = #{id}"+
" </script>"})
int update(SipVideo video);
@Delete("DELETE FROM wvp_sip_video WHERE id = #{videoId}")
void remove(Integer videoId);
}

View File

@ -0,0 +1,11 @@
package com.genersoft.iot.vmp.sip.service;
import javax.sip.ResponseEvent;
public interface ISIPResponseProcessor {
void process(ResponseEvent evt);
}

View File

@ -0,0 +1,99 @@
package com.genersoft.iot.vmp.sip.service;
import com.genersoft.iot.vmp.sip.bean.SipServer;
import com.genersoft.iot.vmp.sip.bean.SipServerAccount;
import com.genersoft.iot.vmp.sip.bean.SipVideo;
import com.github.pagehelper.PageInfo;
/**
* SIP
*/
public interface ISipService {
/**
*
*/
SipServer getSipServer(int sipServerId);
/**
*
*/
void addSipServer(SipServer sipServer);
/**
*
*/
void removeSipServer(int sipServerId);
/**
*
*/
void updateSipServer(SipServer sipServer);
/**
*
*/
void startSipServer(int sipServerId, SipServerAccount account, SipVideo video);
/**
*
*/
void stopSipServer(int sipServerId);
/**
* 线
*/
void sipServerOnline(int sipServerId);
/**
* 线
*/
void sipServerOffline(int sipServerId);
/**
*
*/
PageInfo<SipServer> getServerList(int page, int count);
/**
* sip
*/
void addSipVideo(SipVideo sipVideo);
/**
*
*/
PageInfo<SipServerAccount> getAccountList(int serverId, Integer page, Integer count);
/**
*
*/
PageInfo<SipVideo> getVideoList(int serverId, int accountId, Integer page, Integer count);
/**
*
*/
void addSipServerAccount(SipServerAccount account);
/**
*
*/
void updateSipServerAccount(SipServerAccount account);
/**
*
*/
void removeSipServerAccount(Integer accountId);
/**
* SIP
*/
void updateSipVideo(SipVideo video);
/**
*
*/
void removeSipVideo(Integer videoId);
}

View File

@ -0,0 +1,107 @@
package com.genersoft.iot.vmp.sip.service.Impl;
import com.genersoft.iot.vmp.sip.bean.SipEvent;
import com.genersoft.iot.vmp.sip.service.ISIPResponseProcessor;
import com.genersoft.iot.vmp.sip.service.SipSubscribe;
import gov.nist.javax.sip.message.SIPResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.sip.*;
import javax.sip.header.CSeqHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.message.Response;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* sip
* @author lin
*/
@Service
public class SipListenerImpl implements SipListener {
private final static Logger log = LoggerFactory.getLogger(SipListenerImpl.class);
private Map<String, ISIPResponseProcessor> responseProcessorMap = new ConcurrentHashMap<>();
@Override
@Async("taskExecutor")
public void processRequest(RequestEvent requestEvent) {
String method = requestEvent.getRequest().getMethod();
// TODO 暂不处理
// if (method.equalsIgnoreCase("")) {
//
// }
}
@Override
@Async("taskExecutor")
public void processResponse(ResponseEvent responseEvent) {
SIPResponse response = (SIPResponse) responseEvent.getResponse();
int status = response.getStatusCode();
// Success
if (((status >= Response.OK) && (status < Response.MULTIPLE_CHOICES)) || status == Response.UNAUTHORIZED) {
CSeqHeader cseqHeader = (CSeqHeader) responseEvent.getResponse().getHeader(CSeqHeader.NAME);
String method = cseqHeader.getMethod();
ISIPResponseProcessor sipRequestProcessor = responseProcessorMap.get(method);
if (sipRequestProcessor != null) {
sipRequestProcessor.process(responseEvent);
}
if (status != Response.UNAUTHORIZED && responseEvent.getResponse() != null && SipSubscribe.getInstance().getOkSubscribesSize() > 0 ) {
CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME);
if (callIdHeader != null) {
SipEvent subscribe = SipSubscribe.getInstance().getOkSubscribe(callIdHeader.getCallId());
if (subscribe != null) {
SipSubscribe.getInstance().removeOkSubscribe(callIdHeader.getCallId());
subscribe.response(response.getStatusCode(), response.getReasonPhrase(), responseEvent);
}
}
}
} else if ((status >= Response.TRYING) && (status < Response.OK)) {
// 增加其它无需回复的响应如101、180等
} else {
log.warn("[SIP] 接收到失败的response响应status" + status + ",message:" + response.getReasonPhrase());
if (responseEvent.getResponse() != null && SipSubscribe.getInstance().getErrorSubscribesSize() > 0 ) {
CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME);
if (callIdHeader != null) {
SipEvent subscribe = SipSubscribe.getInstance().getErrorSubscribe(callIdHeader.getCallId());
if (subscribe != null) {
SipSubscribe.getInstance().removeErrorSubscribe(callIdHeader.getCallId());
subscribe.response(response.getStatusCode(), response.getReasonPhrase(), null);
}
}
}
if (responseEvent.getDialog() != null) {
responseEvent.getDialog().delete();
}
}
}
@Override
public void processTimeout(TimeoutEvent timeoutEvent) {
}
@Override
public void processIOException(IOExceptionEvent exceptionEvent) {
}
@Override
public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
}
@Override
public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
}
}

View File

@ -0,0 +1,135 @@
package com.genersoft.iot.vmp.sip.service.Impl;
import com.genersoft.iot.vmp.gb28181.conf.DefaultProperties;
import com.genersoft.iot.vmp.sip.SipCommander;
import com.genersoft.iot.vmp.sip.bean.ResultCallback;
import com.genersoft.iot.vmp.sip.bean.SipEvent;
import com.genersoft.iot.vmp.sip.bean.SipServer;
import com.genersoft.iot.vmp.sip.bean.SipServerAccount;
import com.genersoft.iot.vmp.sip.service.SipSdk;
import com.genersoft.iot.vmp.sip.utils.SipUtils;
import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.SipStackImpl;
import gov.nist.javax.sip.message.SIPResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.sip.*;
import javax.sip.header.WWWAuthenticateHeader;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.util.Map;
import java.util.TooManyListenersException;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class SipSdkImpl implements SipSdk {
private Logger logger = LoggerFactory.getLogger(SipSdkImpl.class);
private Map<String, SipProviderImpl> sipProviderMap = new ConcurrentHashMap<>();
@Autowired
private SipListener sipListenerImpl;
@Autowired
private SipCommander sipCommander;
@Override
public void register(SipServer sipServer, SipServerAccount account, ResultCallback<Object> callback) throws PeerUnavailableException, TransportNotSupportedException, InvalidArgumentException, ObjectInUseException, TooManyListenersException {
if (sipServer == null || account == null) {
return;
}
if (sipServer.getLocalIp() == null) {
sipServer.setLocalIp("0.0.0.0");
}
if (sipServer.getLocalPort() == null) {
if (sipServer.getTransport().equalsIgnoreCase("UDP")) {
sipServer.setLocalPort(SipUtils.getRandomUdpPort());
}else {
sipServer.setLocalPort(SipUtils.getRandomTcpPort());
}
}
SipProviderImpl sipProvider = initSipLister(sipServer);
try {
SipEvent successEvent = (code, msg, data)->{
// 注册成功后开启定时任务
};
sipCommander.register(sipServer, account, sipProvider, true, (code, msg, data)->{
if (code == 200) {
// 注册成功
if (callback != null) {
callback.run(code, msg, data);
}
successEvent.response(code, msg, data);
}else if (code == 401) {
ResponseEvent responseEvent = (ResponseEvent) data;
SIPResponse response = (SIPResponse) responseEvent.getResponse();
WWWAuthenticateHeader authenticateHeader = (WWWAuthenticateHeader) response.getHeader(WWWAuthenticateHeader.NAME);
try {
sipCommander.register(sipServer, account, sipProvider, authenticateHeader, true, (code1, msg1, data1)->{
if (code1 == 200) {
// 注册成功
if (callback != null) {
callback.run(code1, msg1, data1);
}
successEvent.response(code1, msg1, data1);
}else {
// 注册失败
if (callback != null) {
callback.run(code1, msg1, data1);
}
}
}, (code2, msg2, data2)->{
// 注册失败
if (callback != null) {
callback.run(code2, msg2, data2);
}
});
} catch (SipException | ParseException | NoSuchAlgorithmException| InvalidArgumentException e) {
logger.warn("[SIP] 发送注册消息失败", e);
if (callback != null) {
callback.run(-1, e.getMessage(), null);
}
}
}else {
// 注册失败
if (callback != null) {
callback.run(code, msg, data);
}
}
}, (code, msg, data)->{
// 注册失败
if (callback != null) {
callback.run(code, msg, data);
}
});
} catch (SipException | ParseException | NoSuchAlgorithmException e) {
logger.warn("[SIP] 发送注册消息失败", e);
if (callback != null) {
callback.run(-1, e.getMessage(), null);
}
}
}
private SipProviderImpl initSipLister(SipServer sipServer) throws PeerUnavailableException, TransportNotSupportedException, InvalidArgumentException, ObjectInUseException, TooManyListenersException {
SipFactory.getInstance().setPathName("gov.nist");
SipStackImpl sipStack = (SipStackImpl)SipFactory.getInstance().createSipStack(
DefaultProperties.getProperties(sipServer.getLocalIp(), false));
ListeningPoint listeningPoint = sipStack.createListeningPoint(sipServer.getLocalIp(), sipServer.getLocalPort(), sipServer.getTransport());
SipProviderImpl sipProvider = (SipProviderImpl)sipStack.createSipProvider(listeningPoint);
sipProvider.setDialogErrorsAutomaticallyHandled();
sipProvider.addSipListener(sipListenerImpl);
sipProviderMap.put(sipServer.getLocalIp() + ":" + sipServer.getLocalPort(), sipProvider);
return sipProvider;
}
}

View File

@ -0,0 +1,162 @@
package com.genersoft.iot.vmp.sip.service.Impl;
import com.genersoft.iot.vmp.sip.bean.SipServer;
import com.genersoft.iot.vmp.sip.bean.SipServerAccount;
import com.genersoft.iot.vmp.sip.bean.SipVideo;
import com.genersoft.iot.vmp.sip.dao.SipServerAccountMapper;
import com.genersoft.iot.vmp.sip.dao.SipServerMapper;
import com.genersoft.iot.vmp.sip.dao.SipVideoMapper;
import com.genersoft.iot.vmp.sip.service.ISipService;
import com.genersoft.iot.vmp.sip.service.SipSdk;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.sip.InvalidArgumentException;
import javax.sip.ObjectInUseException;
import javax.sip.PeerUnavailableException;
import javax.sip.TransportNotSupportedException;
import java.util.List;
import java.util.TooManyListenersException;
@Service
public class SipServiceImpl implements ISipService {
private final static Logger logger = LoggerFactory.getLogger(SipServiceImpl.class);
@Autowired
private SipServerMapper serverMapper;
@Autowired
private SipServerAccountMapper accountMapper;
@Autowired
private SipVideoMapper videoMapper;
@Autowired
private SipSdk sipSdk;
@Override
public SipServer getSipServer(int sipServerId) {
return serverMapper.query(sipServerId);
}
@Override
public void addSipServer(SipServer sipServer) {
serverMapper.add(sipServer);
}
@Override
public void removeSipServer(int sipServerId) {
serverMapper.remove(sipServerId);
}
@Override
public void updateSipServer(SipServer sipServer) {
serverMapper.update(sipServer);
}
@Override
public void startSipServer(int sipServerId, SipServerAccount account, SipVideo video) {
SipServer sipServer = getSipServer(sipServerId);
if (sipServer == null){
return;
}
if (account.getId() == null) {
}
}
@Override
public void stopSipServer(int sipAccountId) {
}
@Override
public void sipServerOnline(int sipAccountId) {
}
@Override
public void sipServerOffline(int sipAccountId) {
}
@Override
public PageInfo<SipServer> getServerList(int page, int count) {
PageHelper.startPage(page, count);
List<SipServer> all = serverMapper.all();
return new PageInfo<>(all);
}
@Override
public void addSipVideo(SipVideo sipVideo) {
videoMapper.add(sipVideo);
}
@Override
public PageInfo<SipServerAccount> getAccountList(int serverId, Integer page, Integer count) {
PageHelper.startPage(page, count);
List<SipServerAccount> all = accountMapper.all(serverId);
return new PageInfo<>(all);
}
@Override
public PageInfo<SipVideo> getVideoList(int serverId, int accountId, Integer page, Integer count) {
PageHelper.startPage(page, count);
List<SipVideo> all = videoMapper.all(serverId, accountId);
return new PageInfo<>(all);
}
@Override
public void addSipServerAccount(SipServerAccount account) {
if (account.getSipServerId() == null) {
return;
}
SipServer server = serverMapper.query(account.getSipServerId());
if (server == null) {
return;
}
accountMapper.add(account);
try {
sipSdk.register(server, account, (code, msg, data) -> {
if (code == 200) {
}else {
}
});
} catch (PeerUnavailableException | TransportNotSupportedException | InvalidArgumentException |
ObjectInUseException | TooManyListenersException e) {
}
}
@Override
public void updateSipServerAccount(SipServerAccount account) {
accountMapper.update(account);
}
@Override
public void removeSipServerAccount(Integer accountId) {
accountMapper.remove(accountId);
}
@Override
public void updateSipVideo(SipVideo video) {
videoMapper.update(video);
}
@Override
public void removeSipVideo(Integer videoId) {
videoMapper.remove(videoId);
}
}

View File

@ -0,0 +1,19 @@
package com.genersoft.iot.vmp.sip.service;
import com.genersoft.iot.vmp.sip.bean.ResultCallback;
import com.genersoft.iot.vmp.sip.bean.SipServer;
import com.genersoft.iot.vmp.sip.bean.SipServerAccount;
import javax.sip.InvalidArgumentException;
import javax.sip.ObjectInUseException;
import javax.sip.PeerUnavailableException;
import javax.sip.TransportNotSupportedException;
import java.util.TooManyListenersException;
/**
* javaSIP SDK
*/
public interface SipSdk {
void register(SipServer sipServer, SipServerAccount account, ResultCallback<Object> callback) throws PeerUnavailableException, TransportNotSupportedException, InvalidArgumentException, ObjectInUseException, TooManyListenersException;
}

View File

@ -0,0 +1,80 @@
package com.genersoft.iot.vmp.sip.service;
import com.genersoft.iot.vmp.sip.bean.SipEvent;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author lin
*/
public class SipSubscribe {
private static SipSubscribe instance;
private SipSubscribe() {}
public static SipSubscribe getInstance() {
if (instance == null) {
synchronized (SipSubscribe.class) {
if (instance == null) {
instance = new SipSubscribe();
}
}
}
return instance;
}
private Map<String, SipEvent> errorSubscribes = new ConcurrentHashMap<>();
private Map<String, SipEvent> okSubscribes = new ConcurrentHashMap<>();
private Map<String, Instant> okTimeSubscribes = new ConcurrentHashMap<>();
private Map<String, Instant> errorTimeSubscribes = new ConcurrentHashMap<>();
public void addErrorSubscribe(String key, SipEvent event) {
errorSubscribes.put(key, event);
errorTimeSubscribes.put(key, Instant.now());
}
public void addOkSubscribe(String key, SipEvent event) {
okSubscribes.put(key, event);
okTimeSubscribes.put(key, Instant.now());
}
public SipEvent getErrorSubscribe(String key) {
return errorSubscribes.get(key);
}
public void removeErrorSubscribe(String key) {
if(key == null){
return;
}
errorSubscribes.remove(key);
errorTimeSubscribes.remove(key);
}
public SipEvent getOkSubscribe(String key) {
return okSubscribes.get(key);
}
public void removeOkSubscribe(String key) {
if(key == null){
return;
}
okSubscribes.remove(key);
okTimeSubscribes.remove(key);
}
public int getErrorSubscribesSize(){
return errorSubscribes.size();
}
public int getOkSubscribesSize(){
return okSubscribes.size();
}
}

View File

@ -0,0 +1,214 @@
package com.genersoft.iot.vmp.sip.utils;
import com.genersoft.iot.vmp.sip.bean.SipServer;
import com.genersoft.iot.vmp.sip.bean.SipServerAccount;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sip.PeerUnavailableException;
import javax.sip.SipFactory;
import javax.sip.address.SipURI;
import javax.sip.address.URI;
import javax.sip.header.AuthorizationHeader;
import javax.sip.message.Request;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.time.Instant;
import java.util.Random;
import java.util.UUID;
public class DigestClientAuthenticationHelper {
private Logger logger = LoggerFactory.getLogger(DigestClientAuthenticationHelper.class);
public static final String DEFAULT_ALGORITHM = "MD5";
public static final String DEFAULT_SCHEME = "Digest";
private static final char[] toHex = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
public static String toHexString(byte b[]) {
int pos = 0;
char[] c = new char[b.length * 2];
for (int i = 0; i < b.length; i++) {
c[pos++] = toHex[(b[i] >> 4) & 0x0F];
c[pos++] = toHex[b[i] & 0x0f];
}
return new String(c);
}
/**
* Generate the challenge string.
*
* @return a generated nonce.
*/
private static String generateNonce() throws NoSuchAlgorithmException {
long time = Instant.now().toEpochMilli();
Random rand = new Random();
long pad = rand.nextLong();
String nonceString = Long.valueOf(time).toString()
+ Long.valueOf(pad).toString();
byte mdbytes[] = MessageDigest.getInstance(DEFAULT_ALGORITHM).digest(nonceString.getBytes());
return toHexString(mdbytes);
}
public static AuthorizationHeader getAuthorizationHeader(SipServer server, SipServerAccount account)
throws PeerUnavailableException, ParseException, NoSuchAlgorithmException {
return getAuthorizationHeader(server, account, null, null, null, null);
}
public static AuthorizationHeader getAuthorizationHeader(SipServer server, SipServerAccount account,
String realm, String nonce, String algorithm, String qop)
throws PeerUnavailableException, ParseException, NoSuchAlgorithmException {
SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(account.getUsername(),
server.getServerIp() + ":" + server.getServerPort());
AuthorizationHeader authorizationHeader = SipFactory.getInstance().createHeaderFactory().createAuthorizationHeader(DEFAULT_SCHEME);
authorizationHeader.setUsername(account.getUsername());
if (realm == null) {
authorizationHeader.setRealm(server.getServerIp());
}else {
authorizationHeader.setRealm(realm);
}
if (nonce == null) {
authorizationHeader.setRealm(generateNonce());
}else {
authorizationHeader.setRealm(realm);
}
if (algorithm == null) {
authorizationHeader.setAlgorithm("MD5");
}else {
authorizationHeader.setAlgorithm(algorithm);
}
if (qop == null) {
authorizationHeader.setQop("auth");
}else {
authorizationHeader.setQop(qop);
}
authorizationHeader.setNonce(generateNonce());
authorizationHeader.setNonceCount(1);
authorizationHeader.setURI(requestURI);
authorizationHeader.setCNonce(UUID.randomUUID().toString());
String responseForAuth = getResponseForAuth(authorizationHeader, account.getPassword());
authorizationHeader.setResponse(responseForAuth);
return authorizationHeader;
}
private static String getResponseForAuth(AuthorizationHeader authHeader, String password) throws NoSuchAlgorithmException {
String realm = authHeader.getRealm();
String username = authHeader.getUsername();
if ( username == null || realm == null ) {
return null;
}
String nonce = authHeader.getNonce();
URI uri = authHeader.getURI();
if (uri == null) {
return null;
}
String A2 = "REGISTER:" + uri.toString();
String HA1 = password;
MessageDigest messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
byte[] mdbytes = messageDigest.digest(A2.getBytes());
String HA2 = toHexString(mdbytes);
String cnonce = authHeader.getCNonce();
String KD = HA1 + ":" + nonce;
if (cnonce != null) {
KD += ":" + cnonce;
}
KD += ":" + HA2;
mdbytes = messageDigest.digest(KD.getBytes());
return toHexString(mdbytes);
}
/**
* Authenticate the inbound request given plain text password.
*
* @param request - the request to authenticate.
* @param pass -- the plain text password.
*
* @return true if authentication succeded and false otherwise.
*/
public boolean doAuthenticatePlainTextPassword(Request request, String pass) throws NoSuchAlgorithmException {
AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
if ( authHeader == null || authHeader.getRealm() == null) {
return false;
}
String realm = authHeader.getRealm().trim();
String username = authHeader.getUsername().trim();
if ( username == null || realm == null ) {
return false;
}
String nonce = authHeader.getNonce();
URI uri = authHeader.getURI();
if (uri == null) {
return false;
}
// qop 保护质量 包含auth默认的和auth-int增加了报文完整性检测两种策略
String qop = authHeader.getQop();
// 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。
// 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护
String cnonce = authHeader.getCNonce();
// nonce计数器是一个16进制的数值表示同一nonce下客户端发送出请求的数量
int nc = authHeader.getNonceCount();
String ncStr = String.format("%08x", nc).toUpperCase();
// String ncStr = new DecimalFormat("00000000").format(nc);
// String ncStr = new DecimalFormat("00000000").format(Integer.parseInt(nc + "", 16));
String A1 = username + ":" + realm + ":" + pass;
String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
MessageDigest messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
byte mdbytes[] = messageDigest.digest(A1.getBytes());
String HA1 = toHexString(mdbytes);
logger.debug("A1: " + A1);
logger.debug("A2: " + A2);
mdbytes = messageDigest.digest(A2.getBytes());
String HA2 = toHexString(mdbytes);
logger.debug("HA1: " + HA1);
logger.debug("HA2: " + HA2);
// String cnonce = authHeader.getCNonce();
logger.debug("nonce: " + nonce);
logger.debug("nc: " + ncStr);
logger.debug("cnonce: " + cnonce);
logger.debug("qop: " + qop);
String KD = HA1 + ":" + nonce;
if (qop != null && qop.equalsIgnoreCase("auth") ) {
if (nc != -1) {
KD += ":" + ncStr;
}
if (cnonce != null) {
KD += ":" + cnonce;
}
KD += ":" + qop;
}
KD += ":" + HA2;
logger.debug("KD: " + KD);
mdbytes = messageDigest.digest(KD.getBytes());
String mdString = toHexString(mdbytes);
logger.debug("mdString: " + mdString);
String response = authHeader.getResponse();
logger.debug("response: " + response);
return mdString.equals(response);
}
}

View File

@ -0,0 +1,84 @@
package com.genersoft.iot.vmp.sip.utils;
import com.genersoft.iot.vmp.sip.bean.SipServer;
import com.genersoft.iot.vmp.sip.bean.SipServerAccount;
import javax.sip.InvalidArgumentException;
import javax.sip.PeerUnavailableException;
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.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.util.ArrayList;
public class SIPRequestFactory {
public static Request createRegisterRequest(SipServer server, SipServerAccount account, String callId, boolean isRegister, WWWAuthenticateHeader wwwAuthenticateHeader) throws ParseException, PeerUnavailableException, InvalidArgumentException, NoSuchAlgorithmException {
Request request = null;
String serverAddress = server.getServerIp()+ ":" + server.getServerPort();
String clientAddress = server.getLocalIp()+ ":" + server.getLocalPort();
//请求行
SipURI requestLine = SipFactory.getInstance().createAddressFactory().createSipURI(account.getUsername(),
serverAddress);
//via
ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
ViaHeader viaHeader = SipFactory.getInstance().createHeaderFactory().createViaHeader(server.getLocalIp(),
server.getLocalPort(), server.getTransport(), SipUtils.getNewViaTag());
viaHeader.setRPort();
viaHeaders.add(viaHeader);
//from
SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(account.getUsername(), serverAddress);
Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, SipUtils.getNewFromTag());
//to
SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(account.getUsername(), serverAddress);
Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress,null);
//Forwards
MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(callId);
//ceq
CSeqHeader cSeqHeader = SipFactory.getInstance().createHeaderFactory().createCSeqHeader(SipUtils.getCSEQ(), Request.REGISTER);
request = SipFactory.getInstance().createMessageFactory().createRequest(requestLine, Request.REGISTER, callIdHeader,
cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory()
.createSipURI(account.getUsername(), clientAddress));
ContactHeader contactHeader = SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress);
contactHeader.setParameter("methods", "INVITE,ACK,BYE,CANCEL,OPTIONS,PRACK,MESSAGE,SUBSCRIBE,NOTIFY,REFER,UPDATE");
// contactHeader.setQValue();
request.addHeader(contactHeader);
ExpiresHeader expires = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(isRegister ? 180 : 0);
request.addHeader(expires);
AllowHeader allowHeader = SipFactory.getInstance().createHeaderFactory().createAllowHeader("INVITE, ACK, BYE, CANCEL, OPTIONS, PRACK, MESSAGE, SUBSCRIBE, NOTIFY, REFER, UPDATE");
request.addHeader(allowHeader);
SupportedHeader supportedHeader = SipFactory.getInstance().createHeaderFactory().createSupportedHeader("timer, 100rel, replaces, gruu, outbound");
request.addHeader(supportedHeader);
AuthorizationHeader authorizationHeader;
if (wwwAuthenticateHeader != null) {
String qop = wwwAuthenticateHeader.getQop();
String algorithm = wwwAuthenticateHeader.getAlgorithm();
String nonce = wwwAuthenticateHeader.getNonce();
String realm = wwwAuthenticateHeader.getRealm();
authorizationHeader = DigestClientAuthenticationHelper.getAuthorizationHeader(server, account, realm, nonce, algorithm, qop);
}else {
authorizationHeader = DigestClientAuthenticationHelper.getAuthorizationHeader(server, account);
}
request.addHeader(authorizationHeader);
return request;
}
}

View File

@ -0,0 +1,83 @@
package com.genersoft.iot.vmp.sip.utils;
import com.genersoft.iot.vmp.utils.GitUtil;
import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.Utils;
import org.springframework.util.ObjectUtils;
import javax.sip.PeerUnavailableException;
import javax.sip.SipFactory;
import javax.sip.header.UserAgentHeader;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class SipUtils {
public static int getRandomTcpPort(){
try {
ServerSocket serverSocket = new ServerSocket(0);
int localPort = serverSocket.getLocalPort();
serverSocket.close();
return localPort;
}catch (IOException e) {
throw new RuntimeException(e);
}
}
public static int getRandomUdpPort(){
try {
DatagramSocket datagramSocket = new DatagramSocket(0);
int localPort = datagramSocket.getLocalPort();
datagramSocket.close();
return localPort;
}catch (IOException e) {
throw new RuntimeException(e);
}
}
public static String getNewCallId(SipProviderImpl sipProvider) {
return Utils.getInstance().generateCallIdentifier(sipProvider.getListeningPoint()
.getIPAddress());
}
public static UserAgentHeader createUserAgentHeader(GitUtil gitUtil) throws PeerUnavailableException, ParseException {
List<String> agentParam = new ArrayList<>();
agentParam.add("WVP-Pro ");
if (gitUtil != null ) {
if (!ObjectUtils.isEmpty(gitUtil.getBuildVersion())) {
agentParam.add("v");
agentParam.add(gitUtil.getBuildVersion() + ".");
}
if (!ObjectUtils.isEmpty(gitUtil.getCommitTime())) {
agentParam.add(gitUtil.getCommitTime());
}
}
return SipFactory.getInstance().createHeaderFactory().createUserAgentHeader(agentParam);
}
private static long cseq = 0L;
public static long getCSEQ() {
return cseq++;
}
public static String getNewViaTag() {
return "z9hG4bK" + System.currentTimeMillis();
}
public static String getNewFromTag(){
return UUID.randomUUID().toString().replace("-", "");
// return getNewTag();
}
public static String getNewTag(){
return String.valueOf(System.currentTimeMillis());
}
}

View File

@ -0,0 +1,137 @@
package com.genersoft.iot.vmp.vmanager.sip;
import com.genersoft.iot.vmp.service.IMediaServerService;
import com.genersoft.iot.vmp.sip.bean.SipServer;
import com.genersoft.iot.vmp.sip.bean.SipServerAccount;
import com.genersoft.iot.vmp.sip.bean.SipVideo;
import com.genersoft.iot.vmp.sip.service.ISipService;
import com.github.pagehelper.PageInfo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* SIP
*/
@Tag(name = "SIP接口", description = "")
@Controller
@RequestMapping(value = "/api/sip")
public class SipController {
private final static Logger logger = LoggerFactory.getLogger(SipController.class);
@Autowired
private IMediaServerService mediaServerService;
@Autowired
private ISipService sipService;
@Operation(summary = "分页获取SIP服务")
@Parameter(name = "page", description = "当前页")
@Parameter(name = "count", description = "每页查询数量")
@GetMapping(value = "/server/list")
@ResponseBody
public PageInfo<SipServer> getServerList(@RequestParam(required = false)Integer page,
@RequestParam(required = false)Integer count ){
return sipService.getServerList(page, count);
}
@Operation(summary = "添加SIP服务")
@PostMapping(value = "/server/add")
@ResponseBody
public void addServer(@RequestBody SipServer server){
sipService.addSipServer(server);
}
@Operation(summary = "更新SIP服务")
@PostMapping(value = "/server/update")
@ResponseBody
public void updateServer(@RequestBody SipServer server){
sipService.updateSipServer(server);
}
@Operation(summary = "删除SIP服务")
@DeleteMapping(value = "/server/remove")
@ResponseBody
public void deleteServer(Integer serverId){
sipService.removeSipServer(serverId);
}
@Operation(summary = "分页获取SIP帐号")
@Parameter(name = "page", description = "当前页")
@Parameter(name = "count", description = "每页查询数量")
@GetMapping(value = "/account/list")
@ResponseBody
public PageInfo<SipServerAccount> getAccountList(@RequestParam(required = false)Integer page,
@RequestParam(required = false)Integer count,
Integer serverId){
return sipService.getAccountList(serverId, page, count);
}
@Operation(summary = "添加SIP帐号")
@PostMapping(value = "/account/add")
@ResponseBody
public void addServerAccount(@RequestBody SipServerAccount account){
sipService.addSipServerAccount(account);
}
@Operation(summary = "更新SIP帐号")
@PostMapping(value = "/account/update")
@ResponseBody
public void updateServerAccount(@RequestBody SipServerAccount account){
sipService.updateSipServerAccount(account);
}
@Operation(summary = "删除SIP帐号")
@DeleteMapping(value = "/account/remove")
@ResponseBody
public void deleteServerAccount(Integer accountId){
sipService.removeSipServerAccount(accountId);
}
@Operation(summary = "分页获取SIP推送视频")
@Parameter(name = "page", description = "当前页")
@Parameter(name = "count", description = "每页查询数量")
@GetMapping(value = "/video/list")
@ResponseBody
public PageInfo<SipVideo> getVideoList(@RequestParam(required = false)Integer page,
@RequestParam(required = false)Integer count,
Integer serverId,
Integer accountId){
return sipService.getVideoList(serverId, accountId, page, count);
}
@Operation(summary = "添加SIP推送视频")
@PostMapping(value = "/video/add")
@ResponseBody
public void addVideo(@RequestBody SipVideo video){
sipService.addSipVideo(video);
}
@Operation(summary = "更新SIP推送视频")
@PostMapping(value = "/video/update")
@ResponseBody
public void updateVideo(@RequestBody SipVideo video){
sipService.updateSipVideo(video);
}
@Operation(summary = "删除SIP推送视频")
@DeleteMapping(value = "/video/remove")
@ResponseBody
public void deleteVideo(Integer videoId){
sipService.removeSipVideo(videoId);
}
}