java 对接设备的代码资料较少,这里介绍
GB2818
的基本对接流程,有用自取👇
- java负责SIP信令的注册交互,推流、拉流鉴权
- 摄像头负责推流、流媒体负责拉流、转码
wvp-GB28181-pro项目 ,如果java对接各种摄像头,这个项目很👍,比较完善,可参考。进去
star
支持一波
做到需要播放摄像头视频需要:
- 摄像头:视频摄像数据的输出端,协议输出端。
- SIP服务端:java开发sip信令注册交互,流媒体(推流、播放)鉴权
- 流媒体服务器:负责注册rtp流媒体,摄像头推流输出端
概念:
国标协议2818 组成:
- SIP:会话初始协议(Session Initiation Protocol),是一个应用层的 点对点协议,用于初始、管理和终止网络中的语音和视频会话,是 GB28181 的核心之一
- 流媒体:音视频流的传输与转换
sip服务端:
- java实现一般采用
JAIN-SIP
- 项目启动时,初始化tpc、udp端口监听,当有接收sip信令时,会触发相关请求事件
流媒体:
- 用作视频、音频流的接入服务的,拉流、推流、解编码服务端;
- 一般用
ZLMediaKit
用做流媒体服务器,C++11 性能高、部署较方便- https://gitee.com/xia-chu/ZLMediaKit?_from=gitee_search
配置摄像头接入:
sip注册交互流程:
拉流交互过程:
摄像头返回推流端口:
invite 抓包:
部分代码示例:
<!-- sip协议栈 -->
<dependency>
<groupId>javax.sip</groupId>
<artifactId>jain-sip-ri</artifactId>
<version>1.3.0-91</version>
</dependency>
初始监听端口:
- 参考 panll / wvp-GB28181-pro 项目,👆上面有地址
import com.config.SipConfig;
import com.sip.uitl.SipUtil;
import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.SipStackImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import javax.sip.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Sip信令服务器启动监听
* @author heyonghao
* @date 2023/5/11
*/
@Slf4j
@Component
public class SipInitListen implements CommandLineRunner {
@Resource
private SipConfig sipConfig;
/**
* sip通信,消息监听处理
*/
@Resource
private SipProcessListener sipProcessListener;
private SipFactory sipFactory;
/**
* tcp-sip提供
*/
private final Map<String, SipProviderImpl> tcpSipProviderMap = new ConcurrentHashMap<>();
/**
* udp-sip提供
*/
private final Map<String, SipProviderImpl> udpSipProviderMap = new ConcurrentHashMap<>();
@Override
public void run(String... args) {
List<String> monitorIps = new ArrayList<>();
// 使用逗号分割多个ip
String separator = ",";
if (sipConfig.getIp().indexOf(separator) > 0) {
String[] split = sipConfig.getIp().split(separator);
monitorIps.addAll(Arrays.asList(split));
}else {
monitorIps.add(sipConfig.getIp());
}
sipFactory = SipFactory.getInstance();
sipFactory.setPathName("gov.nist");
if (monitorIps.size() > 0) {
for (String monitorIp : monitorIps) {
addListeningPoint(monitorIp, sipConfig.getPort());
}
if (udpSipProviderMap.size() + tcpSipProviderMap.size() == 0) {
System.exit(1);
}
}
}
/**
* 添加 监听ip
* @param monitorIp 监听ip
* @param port 端口
*/
private void addListeningPoint(String monitorIp, int port){
//sip协议栈
SipStackImpl sipStack;
try {
sipStack = (SipStackImpl)sipFactory.createSipStack(SipUtil.defaultProperties(monitorIp, Boolean.FALSE));
} catch (PeerUnavailableException e) {
e.printStackTrace();
log.error("[Sip Server] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp);
return;
}
try {
//创建 TCP传输监听
ListeningPoint tcpListeningPoint = sipStack.createListeningPoint(monitorIp, port, "TCP");
//tcp 消息处理实现
SipProviderImpl tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
tcpSipProvider.setDialogErrorsAutomaticallyHandled();
tcpSipProvider.addSipListener(sipProcessListener);
tcpSipProviderMap.put(monitorIp, tcpSipProvider);
log.info("[Sip Server] tcp://{}:{} 启动成功", monitorIp, port);
} catch (TransportNotSupportedException
| TooManyListenersException
| ObjectInUseException
| InvalidArgumentException e) {
log.error("[Sip Server] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确"
, monitorIp, port);
}
try {
//创建 UDP传输监听
ListeningPoint udpListeningPoint = sipStack.createListeningPoint(monitorIp, port, "UDP");
//udp 消息处理实现
SipProviderImpl udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
udpSipProvider.addSipListener(sipProcessListener);
udpSipProviderMap.put(monitorIp, udpSipProvider);
log.info("[Sip Server] udp://{}:{} 启动成功", monitorIp, port);
} catch (TransportNotSupportedException
| TooManyListenersException
| ObjectInUseException
| InvalidArgumentException e) {
log.error("[Sip Server] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确"
, monitorIp, port);
}
}
public SipFactory getSipFactory() {
return sipFactory;
}
public SipProviderImpl getUdpSipProvider(String ip) {
if (ObjectUtils.isEmpty(ip)) {
return null;
}
return udpSipProviderMap.get(ip);
}
public SipProviderImpl getUdpSipProvider() {
if (udpSipProviderMap.size() != 1) {
return null;
}
return udpSipProviderMap.values().stream().findFirst().get();
}
public SipProviderImpl getTcpSipProvider() {
if (tcpSipProviderMap.size() != 1) {
return null;
}
return tcpSipProviderMap.values().stream().findFirst().get();
}
public SipProviderImpl getTcpSipProvider(String ip) {
if (ObjectUtils.isEmpty(ip)) {
return null;
}
return tcpSipProviderMap.get(ip);
}
public String getLocalIp(String deviceLocalIp) {
if (!ObjectUtils.isEmpty(deviceLocalIp)) {
return deviceLocalIp;
}
return getUdpSipProvider().getListeningPoint().getIPAddress();
}
}
摄像头消息监听
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.sip.*;
import javax.sip.header.CSeqHeader;
import javax.sip.message.Response;
/**
* 消息回调处理
*
* @author heyonghao
* @date 2023/5/11
*/
@Component
@Slf4j
public class SipProcessListener implements SipListener {
@Resource
private SipEventService sipEventService;
/**
* 服务收到请求出发
* @param requestEvent - 请求事件
*/
@Override
public void processRequest(RequestEvent requestEvent) {
log.info("收到摄像机服务请求");
String method = requestEvent.getRequest().getMethod();
log.info("method:"+method);
if (method.equals("REGISTER")){
sipEventService.requestRegister(requestEvent);
}
if (method.equals("MESSAGE")){
sipEventService.requestMessage(requestEvent);
}
if (method.equals("BYE")){
sipEventService.requestBye(requestEvent);
}
}
/**
* 服务收到响应出发
* @param responseEvent - 响应事件
*/
@Override
public void processResponse(ResponseEvent responseEvent) {
log.info("收到摄像机服务响应");
Response response = 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();
log.info("method:"+method);
sipEventService.responseInvite(responseEvent);
} else if ((status >= Response.TRYING) && (status < Response.OK)) {
// 增加其它无需回复的响应,如101、180等
} else {
log.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase());
if (responseEvent.getDialog() != null) {
responseEvent.getDialog().delete();
}
}
}
/**
* 超时回调
* @param timeoutEvent - 超时事件
*/
@Override
public void processTimeout(TimeoutEvent timeoutEvent) {
log.info("收到摄像机 超时回调");
}
/**
* IO异常的回调
* @param exceptionEvent - 异常事件
*/
@Override
public void processIOException(IOExceptionEvent exceptionEvent) {
log.info("收到摄像机 IO异常的回调");
}
/**
* 事务中断回调
* @param transactionTerminatedEvent - 事务事件
*/
@Override
public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
log.info("收到摄像机 事务中断回调");
}
/**
* 对话框关闭事件
* @param dialogTerminatedEvent - 对话框事件
*/
@Override
public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
log.info("收到摄像机 对话框关闭事件");
}
}
业务处理 service
import com.sip.bean.Device;
import com.sip.bean.SSRCInfo;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
/**
* sip 交互事件处理
*
* @author heyonghao
* @date 2023/5/12
*/
public interface SipEventService {
/**
* 接收请求,注册事件
* @param requestEvent requestEvent
*/
void requestRegister(RequestEvent requestEvent);
/**
* 接收请求,消息事件
* @param requestEvent requestEvent
*/
void requestMessage(RequestEvent requestEvent);
/**
* 响应invite请求
* @param responseEvent
*/
void responseInvite(ResponseEvent responseEvent);
/**
* 接收请求,bye事件
* @param requestEvent requestEvent
*/
void requestBye(RequestEvent requestEvent);
/**
* 发送 invite -> 摄像机
*/
void sendInvite(Device device, SSRCInfo ssrcInfo);
/**
* 获取设备详情
* @param device 设备-摄像头
*/
void getDeviceInfo(Device device);
}
@Slf4j
@Service
public class SipEventServiceImpl implements SipEventService {
@Resource
private RedisTemplate<String,Object> redisTemplate;
@Resource
private SipConfig sipConfig;
@Resource
private SIPSender sipSender;
@Resource
private SipInitListen sipInitListen;
@Resource
private SipProcessResponse sipProcessResponse;
@Resource
private SIPRequestHeaderProvider requestHeaderProvider;
@SneakyThrows
@Override
public void requestRegister(RequestEvent evt) {
RequestEventExt evtExt = (RequestEventExt) evt;
String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort();
log.info("[注册请求] 开始处理: {}", requestAddress);
SIPRequest sipRequest = (SIPRequest) evt.getRequest();
Response response = null;
//密码是否正确
boolean passwordCorrect = false;
// 注册标志
boolean registerFlag;
FromHeader fromHeader = (FromHeader) sipRequest.getHeader(FromHeader.NAME);
AddressImpl address = (AddressImpl) fromHeader.getAddress();
SipUri uri = (SipUri) address.getURI();
//设备ID(保留)
String deviceId = uri.getUser();
//是否携带认证信息
AuthorizationHeader authHead = (AuthorizationHeader) sipRequest.getHeader(AuthorizationHeader.NAME);
String password = sipConfig.getPassword();
if (authHead == null) {
log.info("[注册请求] 摄像头未携带认证信息");
log.info("[注册请求] 回复401: {}", requestAddress);
response = sipProcessResponse.getMessageFactory().createResponse(Response.UNAUTHORIZED, sipRequest);
new DigestServerAuthenticationHelper().generateChallenge(sipProcessResponse.getHeaderFactory(), response, sipConfig.getDomain());
sipSender.send(sipRequest.getLocalAddress().getHostAddress(), response);
return;
}
passwordCorrect = new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(sipRequest, password);
//密码验证失败
if (!passwordCorrect) {
// 注册失败
log.info("[注册请求] 携带认证信息,但是密码验证错误");
response = sipProcessResponse.getMessageFactory().createResponse(Response.FORBIDDEN, sipRequest);
response.setReasonPhrase("wrong password");
log.info("[注册请求] 密码/SIP服务器ID错误, 回复403: {}", requestAddress);
sipSender.send(sipRequest.getLocalAddress().getHostAddress(), response);
return;
}
// 携带授权头并且密码正确
response = sipProcessResponse.getMessageFactory().createResponse(Response.OK, sipRequest);
// 添加date头
SIPDateHeader dateHeader = new SIPDateHeader();
// 使用自己修改的
WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
dateHeader.setDate(wvpSipDate);
response.addHeader(dateHeader);
if (sipRequest.getExpires() == null) {
response = sipProcessResponse.getMessageFactory().createResponse(Response.BAD_REQUEST, sipRequest);
sipSender.send(sipRequest.getLocalAddress().getHostAddress(), response);
return;
}
// 添加Contact头
response.addHeader(sipRequest.getHeader(ContactHeader.NAME));
// 添加Expires头
response.addHeader(sipRequest.getExpires());
RemoteAddressInfo remoteAddressInfo = SipUtil.getRemoteAddressFromRequest(sipRequest,false);
String key = "camera-device:"+deviceId;
Device device = (Device)redisTemplate.opsForValue().get(key);
if (device == null) {
device = new Device();
device.setStreamMode("UDP");
device.setCharset("GB2312");
device.setGeoCoordSys("WGS84");
device.setTreeType("CivilCode");
device.setDeviceId(deviceId);
device.setOnline(0);
}
device.setIp(remoteAddressInfo.getIp());
device.setPort(remoteAddressInfo.getPort());
device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
device.setLocalIp(sipRequest.getLocalAddress().getHostAddress());
if (sipRequest.getExpires().getExpires() == 0) {
// 注销成功
registerFlag = false;
} else {
// 注册成功
device.setExpires(sipRequest.getExpires().getExpires());
registerFlag = true;
// 判断TCP还是UDP
ViaHeader reqViaHeader = (ViaHeader) sipRequest.getHeader(ViaHeader.NAME);
String transport = reqViaHeader.getTransport();
device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
}
sipSender.send(sipRequest.getLocalAddress().getHostAddress(), response);
// 注册成功
// 保存到redis
if (registerFlag) {
log.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress);
device.setRegisterTime(DateUtil.getNow());
device.setOnline(1);
} else {
log.info("[注销成功] deviceId: {}->{}", deviceId, requestAddress);
device.setOnline(0);
}
redisTemplate.opsForValue().set(key,device);
}
@Override
public void requestMessage(RequestEvent evt) {
SIPRequest sipRequest = (SIPRequest)evt.getRequest();
log.info("接收到消息:" + evt.getRequest());
String deviceId = SipUtil.getUserIdFromFromHeader(evt.getRequest());
CallIdHeader callIdHeader = sipRequest.getCallIdHeader();
SIPRequest request = (SIPRequest) evt.getRequest();
// 查询设备是否存在
String key = "camera-device:"+deviceId;
Device device = (Device)redisTemplate.opsForValue().get(key);
// 查询上级平台是否存在
try {
if (device == null) {
// 不存在则回复404
sipProcessResponse.responseAck(request, Response.NOT_FOUND, "device "+ deviceId +" not found");
log.warn("[设备未找到 ]deviceId: {}, callId: {}", deviceId, callIdHeader.getCallId());
}else {
Element rootElement = null;
try {
rootElement = sipProcessResponse.getRootElement(evt);
if (rootElement == null) {
log.error("处理MESSAGE请求 未获取到消息体{}", evt.getRequest());
sipProcessResponse.responseAck(request, Response.BAD_REQUEST, "content is null");
return;
}
//message 命令类型
String cmdType = XmlUtil.getText(rootElement, "CmdType");
switch (cmdType){
case "DeviceInfo":
//厂家
String manufacturer = XmlUtil.getText(rootElement, "Manufacturer");
String Channel = XmlUtil.getText(rootElement, "DeviceID");
sipProcessResponse.responseAck(sipRequest, Response.OK);
return;
case "Keepalive":
sipProcessResponse.responseAck(sipRequest, Response.OK,"注册成功");
return;
default:
sipProcessResponse.responseAck(sipRequest, Response.OK);
}
} catch (DocumentException e) {
log.warn("解析XML消息内容异常", e);
// 不存在则回复404
sipProcessResponse.responseAck(request, Response.BAD_REQUEST, e.getMessage());
}
}
} catch (SipException e) {
log.warn("SIP 回复错误", e);
} catch (InvalidArgumentException e) {
log.warn("参数无效", e);
} catch (ParseException e) {
log.warn("SIP回复时解析异常", e);
}
}
@Override
public void responseInvite(ResponseEvent evt) {
log.debug("响应invite:" + evt.getResponse());
try {
SIPResponse response = (SIPResponse)evt.getResponse();
int statusCode = response.getStatusCode();
// trying不会回复
if (statusCode == Response.TRYING) {
}
// 成功响应
// 下发ack
if (statusCode == Response.OK) {
log.info("回复ACK,准备推流");
ResponseEventExt event = (ResponseEventExt)evt;
String contentString = new String(response.getRawContent());
// jainSip不支持y=字段, 移除以解析。
int ssrcIndex = contentString.indexOf("y=");
// 检查是否有y字段
SessionDescription sdp;
if (ssrcIndex >= 0) {
//ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段
String substring = contentString.substring(0, contentString.indexOf("y="));
sdp = SdpFactory.getInstance().createSessionDescription(substring);
} else {
sdp = SdpFactory.getInstance().createSessionDescription(contentString);
}
SipURI requestUri = sipInitListen.getSipFactory().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort());
Request reqAck = requestHeaderProvider.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response);
log.info("[回复ack] {}-> {}:{} ", sdp.getOrigin().getUsername(), event.getRemoteIpAddress(), event.getRemotePort());
sipSender.send( response.getLocalAddress().getHostAddress(), reqAck);
}
} catch (InvalidArgumentException | ParseException | SipException | SdpParseException e) {
log.info("[点播回复ACK],异常:", e );
}
}
@Override
public void requestBye(RequestEvent evt) {
log.info("处理BYE请求");
try {
sipProcessResponse.responseAck((SIPRequest) evt.getRequest(), Response.OK);
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[回复BYE信息失败],{}", e.getMessage());
}
}
@Override
public void sendInvite(Device device,SSRCInfo ssrcInfo) {
try {
String channelId="34020000001320000001";
String sdpIp;
if (!ObjectUtils.isEmpty(device.getSdpIp())) {
sdpIp = device.getSdpIp();
}else {
sdpIp = "192.168.1.250";
}
//封装 sdp协议信息,告诉摄像头 推流配置
StringBuffer content = new StringBuffer(200);
content.append("v=0\r\n");
content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n");
content.append("s=Play\r\n");
content.append("c=IN IP4 " + sdpIp + "\r\n");
content.append("t=0 0\r\n");
//is -SDP
if (Boolean.FALSE) {
if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) {
content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
} else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) {
content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n");
} else if ("UDP".equalsIgnoreCase(device.getStreamMode())) {
content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n");
}
content.append("a=recvonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
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 H265/90000\r\n");
content.append("a=rtpmap:98 H264/90000\r\n");
content.append("a=rtpmap:97 MPEG4/90000\r\n");
if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式
content.append("a=setup:passive\r\n");
content.append("a=connection:new\r\n");
} else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式
content.append("a=setup:active\r\n");
content.append("a=connection:new\r\n");
}
} else {
if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) {
content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
} else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) {
content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n");
} else if ("UDP".equalsIgnoreCase(device.getStreamMode())) {
content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n");
}
content.append("a=recvonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
content.append("a=rtpmap:98 H264/90000\r\n");
content.append("a=rtpmap:97 MPEG4/90000\r\n");
content.append("a=rtpmap:99 H265/90000\r\n");
if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式
content.append("a=setup:passive\r\n");
content.append("a=connection:new\r\n");
} else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式
content.append("a=setup:active\r\n");
content.append("a=connection:new\r\n");
}
}
content.append("y=" + ssrcInfo + "\r\n");//ssrc
System.out.println(content);
CallIdHeader newCallIdHeader=sipSender.getNewCallIdHeader(sipInitListen.getLocalIp(device.getLocalIp()),device.getTransport());
Request request = requestHeaderProvider.createInviteRequest(device, channelId,
content.toString(), SipUtil.getNewViaTag(), SipUtil.getNewFromTag(), null, ssrcInfo.getSsrc(), newCallIdHeader);
sipSender.send(sipConfig.getIp(),request);
} catch (ParseException | InvalidArgumentException | PeerUnavailableException e) {
throw new RuntimeException(e);
}
}
@Override
public void getDeviceInfo(Device device) {
try {
sipSender.deviceInfoQuery(device);
} catch (InvalidArgumentException | SipException | ParseException e) {
throw new RuntimeException(e);
}
}
}
消息发送:
@Slf4j
@Component
public class SIPSender {
@Autowired
private SipInitListen sipInitListen;
@Autowired
private SIPRequestHeaderProvider headerProvider;
/**
* 发送消息
* @param ip 目标ip
* @param message 消息体
*/
@SneakyThrows
public void send(String ip, Message message) {
ViaHeader viaHeader = (ViaHeader)message.getHeader(ViaHeader.NAME);
String transport = "UDP";
if (viaHeader == null) {
log.warn("[消息头缺失]: ViaHeader, 使用默认的UDP方式处理数据");
}else {
transport = viaHeader.getTransport();
}
if (message.getHeader(UserAgentHeader.NAME) == null) {
message.addHeader(SipUtil.createUserAgentHeader(sipInitListen.getSipFactory()));
}
switch (transport){
case "TCP":
sendTCP(ip,message);
return;
case "UDP":
sendUDP(ip,message);
return;
default:
sendTCP(ip,message);
}
}
private boolean sendUDP(String ip, Message message) throws SipException {
SipProviderImpl sipProvider = sipInitListen.getUdpSipProvider(ip);
if (sipProvider == null) {
log.error("[发送信息失败] 未找到udp://{}的监听信息", ip);
return true;
}
if (message instanceof Request) {
sipProvider.sendRequest((Request) message);
}else if (message instanceof Response) {
sipProvider.sendResponse((Response) message);
}
return false;
}
private boolean sendTCP(String ip, Message message) throws SipException {
SipProviderImpl tcpSipProvider = sipInitListen.getTcpSipProvider(ip);
if (tcpSipProvider == null) {
log.error("[发送信息失败] 未找到tcp://{}的监听信息", ip);
return true;
}
if (message instanceof Request) {
tcpSipProvider.sendRequest((Request) message);
}else if (message instanceof Response) {
tcpSipProvider.sendResponse((Response) message);
}
return false;
}
public CallIdHeader getNewCallIdHeader(String ip, String transport){
if (ObjectUtils.isEmpty(transport)) {
return sipInitListen.getUdpSipProvider().getNewCallId();
}
SipProviderImpl sipProvider;
if (ObjectUtils.isEmpty(ip)) {
sipProvider = transport.equalsIgnoreCase("TCP") ? sipInitListen.getTcpSipProvider()
: sipInitListen.getUdpSipProvider();
}else {
sipProvider = transport.equalsIgnoreCase("TCP") ? sipInitListen.getTcpSipProvider(ip)
: sipInitListen.getUdpSipProvider(ip);
}
if (sipProvider == null) {
sipProvider = sipInitListen.getUdpSipProvider();
}
if (sipProvider != null) {
return sipProvider.getNewCallId();
}else {
log.warn("[新建CallIdHeader失败], ip={}, transport={}", ip, transport);
return null;
}
}
/**
* 查询设备信息
* @param device
* @throws InvalidArgumentException
* @throws SipException
* @throws ParseException
*/
public void deviceInfoQuery(Device device) throws InvalidArgumentException, SipException, ParseException {
StringBuffer catalogXml = new StringBuffer(200);
String charset = device.getCharset();
catalogXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
catalogXml.append("<Query>\r\n");
catalogXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
catalogXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
catalogXml.append("</Query>\r\n");
Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtil.getNewViaTag(), SipUtil.getNewFromTag(), null,getNewCallIdHeader(sipInitListen.getLocalIp(device.getLocalIp()),device.getTransport()));
send(sipInitListen.getLocalIp(device.getLocalIp()), request);
}
}