概念:

国标协议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);


}