概念:
国标协议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>
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 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);
}