JAIN SIP API详解与GB28181服务器实现【保姆级源码教程】

avatar
作者
猴君
阅读量:7

目录

一 JAIN SIP API

1 摘要

2 关于JAIN SIP API

3 API概述

3.1 maven坐标

3.2 类/接口

3.3 Message接口

3.4 Request接口

3.5 Response接口

4 即时通讯程序

4.1 TextClient代码概述

4.2 Message Processor

4.3 SIP协议栈

4.4 发送SIP请求

4.5 发送会话消息

4.6 接收SIP响应

4.7 接收SIP请求

4.8 处理错误

4.9 小节

二 GB28181SIP服务器——MSH

1 简介

2 GB28181

2.1 注册

2.2 保活

2.2.1 命令流程

2.2.2 协议接口

2.3 MSH代码概述

2.3.1 创建springboot项目

2.3.2 SIP协议栈

2.3.3 接收SIP请求响应

2.3.4 处理SIP请求

2.3.5 发送SIP请求

2.3.6 IPC接入

2.3.7 抓包与流程分析

2.3.7.1 注册

2.3.7.2 保活

3 小结

3.1 SIP服务器

3.2 WEB服务器


一 JAIN SIP API

1 摘要

这篇文章展示了基于Java SE如何创建客户端侧的SIP应用。JAIN SIP API是一个强大的“SIP协议栈”。本文将通过一个简单的即时通讯程序以及一个GB28181协议的简单应用程序,详细的分析该技术。

2 关于JAIN SIP API

Java api for Integrated Networks (JAIN)是一个JCP工作组所管理的电信标准,Session Initiation Protocol(SIP)是一种标准的通信协议,将Java和SIP结合在一起,就得到了JAIN SIP API,这是一个标准的、功能强大的电信API。这个API通常用于客户端应用程序开发。其他基于容器的技术,如SIP Servlet API(参见BEA WebLogic SIP Server的例子),更适合于服务器端开发,但是在GB28181协议应用程序中我们也采用该API用作SIP服务器的开发实现IPC与联网平台的信令交互。

3 API概述

3.1 maven坐标

	<dependency>     	<groupId>javax.sip</groupId>    	 	<artifactId>jain-sip-ri</artifactId>     	<version>1.3.0-91</version> 	</dependency>

3.2 类/接口

下面概述了JAIN SIP API实现中的主要类和接口。

Class / Interface描述
SipFactory / AddressFactory / HeaderFactory / MessageFactory工厂类来创建系统的各种对象。它们返回声明了标准接口的对象。
SipStack您需要的第一个接口,用于创建ListeningPoints和SipProviders。
ListeningPoint这个接口封装了一个传输/端口对(例如UDP/5060)。
SipProvider这个接口用来发送SIP消息。您还可以使用此接口为传入的SIP消息注册一个监听器。参见下面的SipListener。
SipListener您必须实现此接口以允许接收传入的SIP消息。
RequestEvent / ResponseEvent表示传入的SIP请求、响应。传递给SipListener进行处理。分别包含一个Request或Response对象。
TimeoutEvent表示传出请求没有回复时的失败条件。传递给SipListener进行处理。
IOExceptionEvent表示在发送外发请求时出现输入/输出问题时的失败条件。传递给SipListener进行处理。
Request / Response表示SIP请求、响应。两者都是Message接口的子接口。它们提供对报头、内容和SIP消息的其他部分的访问。
Dialog此接口的对象封装了一个SIP对话框。(提醒:在对话框中,所有消息都与同一个调用相关;对话通常以INVITE开始,以BYE结束。
ClientTransaction / ServerTransaction封装SIP事务。(提醒:事务以请求开始,以最终响应结束。事务通常存在于对话框中。)

3.3 Message接口

Message接口是SIP消息的基本接口,下面是可用方法的概述。

Method描述
void addHeader(Header) void setHeader(Header)将报头字段设置为SIP消息。第一种方法可用于可重复或具有多个值的标头,如Contact标头。第二个方法删除该类型的现有头,然后添加单个头值。
void removeHeader(Header)删除此类型的现有标头。
ListIterator getHeaderNames()返回所有头文件名称。
ListIterator getUnrecognizedHeaders()返回非标准报头类型的报头名称。
Header getHeader(String) ListIterator getHeaders(String)ListIterator getHeaders(字符串) 特定头的getter。第二种形式返回可重复标头的所有值,或具有多个值的标头,如Contact标头。
void setContent(Object, ContentTypeHeader)设置消息的有效负载以及Content-Type报头。如果类型是字符串,Content-Length也被设置,否则使用void setContentLength(ContentLengthHeader)。
byte [] getRawContent() Object getContent()检索消息的有效负载。
void removeContent()清空有效负载。
void setContentLength(ContentLengthHeader) ContentLengthHeader getContentLength() void setContentLanguage(ContentLanguageHeader) ContentLanguageHeader getContentLanguage() void setContentEncoding(ContentEncodingHeader) ContentEncodingHeader getContentEncoding() void setContentDisposition(ContentDispositionHeader) ContentDispositionHeader getContentDisposition()与有效负载相关的特殊头访问器。很少使用。
void setExpires(ExpiresHeader) ExpiresHeader getExpires()管理Expires报头。
void setSipVersion(String) String getSipVersion()字符串getSipVersion () SIP版本元素的访问器。很少使用,默认为SIP/2.0。
Object clone()创建消息的副本。很少使用。

3.4 Request接口

Message接口的子接口

Method描述
String getMethod() void setMethod(String)方法元素的访问器。可以是任何SIP方法,包括请求接口常量中的方法:ACK、BYE、CANCEL、INVITE、OPTIONS、REGISTER、NOTIFY、SUBSCRIBE、MESSAGE、REFER、INFO、PRACK和UPDATE。
URI getRequestURI() void setRequestURI(URI)请求URI的访问器,这是SIP请求的第一行。通常,这是SipURI的一个实例。

3.5 Response接口

Message接口的子接口。

Method描述
void setStatusCode() int getStatusCode()状态代码的访问器。这可以是任何SIP状态码,包括Response接口的常量成员中的状态码。这里有一些:RINGING (180), OK (200), BAD_REQUEST(400),等等。
void setReasonPhrase(String) String getReasonPhrase()访问器,用于人类可读的状态代码解释。

4 即时通讯程序

TextClient是一个即时消息传递应用程序,可以通过SIP协议发送和接收文本消息。此应用程序的一个实例可以向另一个实例发送消息,但从理论上讲,此客户机可用于向其他类型的SIP即时消息传递客户机,甚至SIP服务器应用程序发送消息。如下图所示,SIP客户端yrz向另一个SIP客户端yz发送了一条”我是yrz2023年4月18日13:46:22“的消息,随后SIP客户端yz回复了一条”yz收到2023年4月18日13:46:22“的消息。

4.1 TextClient代码概述

两个类和一个接口组成了整个TextClient代码。下表介绍:

Class / Interface描述
TextClient主类,包含应用程序小部件的Swing窗口。
SipLayer它负责所有SIP通信。它由TextClient类实例化,并通过MessageProcessor接口回调它。
MessageProcessor回调接口(观察者模式),用于将SipLayer与其容器解耦。

4.2 Message Processor

创建MessageProcessor接口,将SIP层与GUI层分离。TextClient类实现该接口,其构造函数将SipLayer对象作为参数,您将能够使用SipLayer对象将信息发送回GUI。

public interface MessageProcessor {     // 请求回调方法     void processMessage(String sender, String message);     // 请求错误回调方法     void processError(String errorMessage);     // 响应回调方法     void processInfo(String infoMessage); }

4.3 SIP协议栈

让我们开始编写SipLayer类。TextClient必须能够接收来自其他SIP端点的异步消息。这个类实现了SipListener接口来处理传入的消息:

public class SipLayer implements SipListener { 	... }

SipListener接口方法如下:

public interface SipListener extends EventListener {     void processRequest(RequestEvent var1);      void processResponse(ResponseEvent var1);      void processTimeout(TimeoutEvent var1);      void processIOException(IOExceptionEvent var1);      void processTransactionTerminated(TransactionTerminatedEvent var1);      void processDialogTerminated(DialogTerminatedEvent var1); }

在本例中,用于处理传入消息的最重要的方法显然是processRequest()和processResponse()。接下来是存储稍后需要的对象的两个字段:username和messageProcessor,这些与SIP API没有直接关系,但是在本例中需要它们。第一个是前面讨论过的MessageProcessor对象,用于回调方法将消息发回给GUI,username用于随时保留用户名,这两个字段有getter和setter方法。

private MessageProcessor messageProcessor; private String username;

接下来是构造函数,一种启动JAIN SIP API的经典方法——建立一堆以后会有用的对象(工厂和SIP协议栈实例),TextClient就是采用的这种方法。

private SipStack sipStack;      private SipFactory sipFactory;      private AddressFactory addressFactory;      private HeaderFactory headerFactory;      private MessageFactory messageFactory;      private SipProvider sipProvider;  public SipLayer(String username, String ip, int port) throws	PeerUnavailableException,  TransportNotSupportedException,InvalidArgumentException, ObjectInUseException, TooManyListenersException {            setUsername(username);            sipFactory = SipFactory.getInstance();            sipFactory.setPathName("gov.nist");            Properties properties = new Properties();            properties.setProperty("javax.sip.STACK_NAME",                    "TextClient");            properties.setProperty("javax.sip.IP_ADDRESS",                    ip);            sipStack = sipFactory.createSipStack(properties);            headerFactory = sipFactory.createHeaderFactory();            addressFactory = sipFactory.createAddressFactory();            messageFactory = sipFactory.createMessageFactory();            ...

SipFactory用于实例化SipStack实现,但由于可能有多个实现,因此必须通过setPathName()方法命名您想要的那个实现。名称“gov.nist”表示您获得的SIP堆栈。

SipStack对象具有许多属性。至少,您必须设置堆栈名称。所有其他属性都是可选的。在这里,我设置了一个由堆栈使用的IP地址,用于一台计算机有多个IP地址的情况。注意,这里有标准属性(所有SIP API实现都必须支持)和非标准属性(依赖于实现)。

下一步是创建一对ListeningPoint和SipProvider对象。这些对象提供了发送和接收消息的通信功能。TCP有一组,UDP有一组。这也是你选择SipLayer作为传入SIP消息的监听器的地方:

...            ListeningPoint tcp = sipStack.createListeningPoint(port, "tcp");            ListeningPoint udp = sipStack.createListeningPoint(port, "udp");            sipProvider = sipStack.createSipProvider(tcp);            sipProvider.addSipListener(this);            sipProvider = sipStack.createSipProvider(udp);            sipProvider.addSipListener(this);          }

构造函数就是这样结束的。您已经使用JAIN SIP API创建了一个SipStack实例、一堆工厂、两个listeningpoint和一个SipProvider。这些对象将在接下来的方法中用于发送和接收消息。

4.4 发送SIP请求

现在让我们编写一个使用JAIN SIP API发送SIP消息的方法,在此之前你必须非常了解SIP协议。SIP API是相当低级的抽象,在大多数情况下,不使用默认值或隐藏头、请求uri或SIP消息的内容。这种设计的优点是您可以完全控制SIP消息所包含的内容。

发送一个SIP请求大致分为四个部分:

  • 创建主要元素

  • 创建消息

  • 完整的消息

  • 发送消息

使用JAIN SIP API构造消息最少需要以下主要SIP元素:

  • 请求URI

  • 方法

  • 通话身份头

  • CSeq头

  • 从标题

  • Via报头数组

  • Max-forwards头

下面的代码片段创建了所有这些元素:

public void sendMessage(String to, String message) throws                 ParseException, InvalidArgumentException, SipException {                  SipURI from = addressFactory.createSipURI(getUsername(),                     getHost() + ":" + getPort());         Address fromNameAddress = addressFactory.createAddress(from);             fromNameAddress.setDisplayName(getUsername());             FromHeader fromHeader =                     headerFactory.createFromHeader(fromNameAddress,                             "textclientv1.0");                  String username = to.substring(to.indexOf(":")+1, to.indexOf("@"));             String address = to.substring(to.indexOf("@")+1);                  SipURI toAddress =                     addressFactory.createSipURI(username, address);             Address toNameAddress = addressFactory.createAddress(toAddress);             toNameAddress.setDisplayName(username);             ToHeader toHeader =                     headerFactory.createToHeader(toNameAddress, null);                  SipURI requestURI =                     addressFactory.createSipURI(username, address);             requestURI.setTransportParam("udp");                  ArrayList viaHeaders = new ArrayList();             ViaHeader viaHeader =                     headerFactory.createViaHeader(                             getHost(),                             getPort(),                             "udp",                             null);             viaHeaders.add(viaHeader);                  CallIdHeader callIdHeader = sipProvider.getNewCallId();                  CSeqHeader cSeqHeader =                     headerFactory.createCSeqHeader(1, Request.MESSAGE);                  MaxForwardsHeader maxForwards =                     headerFactory.createMaxForwardsHeader(70);             ...

我使用在构造函数HeaderFactory和AddressFactory中创建的工厂来实例化这些元素。接下来让我们实例化实际的SIP消息本身,传入之前创建的所有元素:

Request request =  messageFactory.createRequest(             requestURI, Request.MESSAGE, callIdHeader, cSeqHeader,             fromHeader, toHeader, viaHeaders,       maxForwards);     ...

注意,这一步使用了MessageFactory。然后,让我们向消息添加其他元素:联系人标头和消息的内容(有效负载),也可以添加自定义标题。

SipURI contactURI = addressFactory.createSipURI(getUsername(),                     getHost());             contactURI.setPort(getPort());             Address contactAddress = addressFactory.createAddress(contactURI);             contactAddress.setDisplayName(getUsername());             ContactHeader contactHeader =                     headerFactory.createContactHeader(contactAddress);             request.addHeader(contactHeader);             ContentTypeHeader contentTypeHeader =                     headerFactory.createContentTypeHeader("text", "plain");             request.setContent(message, contentTypeHeader);             ...

最后,使用SipProvider实例发送消息:

sipProvider.sendRequest(request);    }

4.5 发送会话消息

你在会话外发送我们的信息,这意味着消息之间没有关联,这对于TextClient这样的简单即时消息传递应用程序来说效果很好。另一种方法是使用INVITE消息创建一个会话,然后在该会话内发送消息。TextClient不使用这种技术,但是是值得学习的东西,本小节描述了如何做到这一点。

在会话中发送消息需要创建Dialog和Transaction对象。在初始消息(即创建会话的消息)上,不使用提供程序发送消息,而是实例化一个Transaction,然后从中获取Dialog。您保留Dialog引用以供以后使用。然后使用事务发送消息:

ClientTransaction trans = sipProvider.getNewClientTransaction(invite);     dialog = trans.getDialog();     trans.sendRequest();

稍后,当您希望在同一个会话中发送新消息时,您可以使用前面的Dialog对象来创建一个新请求。然后,您可以对请求进行消息处理,最后,使用Transaction发送消息。

request = dialog.createRequest(Request.MESSAGE);     request.setHeader(contactHeader);     request.setContent(message, contentTypeHeader);        ClientTransaction trans = sipProvider.getNewClientTransaction(request);     trans.sendRequest();

从本质上讲,在现有会话中发送消息时,您跳过了“创建主要元素”步骤。当您使用INVITE创建对话框时,不要忘记在对话框结束时发送一个BYE消息来清理它。此技术还用于刷新注册和订阅。

在前面,您已经看到了SipListener接口,其中包含processDialogTerminated()和processTransactionTerminated()方法。它们分别在对话框和事务结束时自动调用。通常,实现这些方法是为了清理(例如,丢弃Dialog和Transaction实例)。您将把这两个方法留空,因为在TextClient中不需要它们。

4.6 接收SIP响应

前面,您注册了传入消息的监听器。监听器接口SipListener包含方法processResponse(),当SIP响应消息到达时,由SIP协议栈调用该方法。processResponse()接受一个ResponseEvent类型的参数,它封装了一个Response对象。

public void processResponse(ResponseEvent evt) {          	Response response = evt.getResponse();          	int status = response.getStatusCode();           	if( (status >= 200) && (status < 300) ) { //Success!                  	messageProcessor.processInfo("--Sent");                  	return;          	}     messageProcessor.processError("Previous message not sent: " + status);  }

在此方法中,您将检查先前MESSAGE消息的响应是否表示成功(2xx范围的状态码)或错误(否则)。然后通过回调接口将此信息转发给用户。

通常,您只读取processResponse()方法中的Response对象。唯一的例外是对INVITE消息的成功响应;在这种情况下,你必须发送一个ACK请求,就像这样:

Dialog dialog = evt.getClientTransaction().getDialog(); Request ack =  dialog.createAck(); dialog.sendAck(ack);

4.7 接收SIP请求

接收SIP请求消息与接收响应一样简单。您只需实现SipListener接口的另一个方法processRequest(), SIP堆栈将自动调用它。该方法的唯一参数是RequestEvent对象,其中包含Request对象。这是你之前见过的相同类型,它有相同的方法。但是,您不应该在传入请求上设置任何字段,因为这没有多大意义。

processRequest()的典型实现就是分析请求,然后创建并发回适当的响应:

public void processRequest(RequestEvent evt) {          	Request req = evt.getRequest();           	String method = req.getMethod();          	if( ! method.equals("MESSAGE")) { //bad request type.                  		messageProcessor.processError("Bad request type: " + method);                  		return;          	}           	FromHeader from = (FromHeader)req.getHeader("From");          	messageProcessor.processMessage(from.getAddress().toString(), new String(req.getRawContent()));         	Response response=null;          	try { //Reply with OK 		response = messageFactory.createResponse(200, req);                  		ToHeader toHeader = (ToHeader)response.getHeader(ToHeader.NAME);                  		toHeader.setTag("888"); //Identifier, specific to your application                  		ServerTransaction st = sipProvider.getNewServerTransaction(req);           				 		st.sendResponse(response);	 	} catch (Throwable e) {                  		e.printStackTrace();                  		messageProcessor.processError("Can't send OK reply.");          	}  }

在这种情况下,您总是用一个成功响应(200)来回复,但是您也可以发回任何错误响应(通常是4xx范围)。

4.8 处理错误

SipListener接口中还有其他尚未实现的方法。当由于特定原因无法发送请求时,由SIP协议调用它们。例如,当接收消息的端点没有及时应答时,将调用processTimeout()。这是一种没有响应的特殊情况,因此没有可用的response对象。TimeoutEvent参数包含超时请求的ClientTransaction,如果需要,可以使用该参数链接回原始请求。在这个实现中,你只需使用回调接口通知用户:

public void processTimeout(TimeoutEvent evt) {          	messageProcessor.processError("Previous message not sent: " + "timeout");  }

类似地,Input/Output (IO)错误的处理方法如下:

public void processIOException(IOExceptionEvent evt) {          	messageProcessor.processError("Previous message not sent: " + "I/O Exception");  }

4.9 小节

本文概述了JAIN SIP API,并展示了如何编写一个简单的应用程序来使用这项技术。现在,您应该对可用的api有了很好的了解,并且知道如何使用SIP编写自己的IM客户机。

以上内容主要来自ORACLE官网《An Introduction to the JAIN SIP API》文章,TextClient源码下载地址也在文章提供,感兴趣的同学可以阅读原文,文章地址:An Introduction to the JAIN SIP API

下面将该API应用到安防领域实现一个能够满足GB28181协议的SIP服务器。

二 GB28181SIP服务器——MSH

1 简介

SIP(Session Initiation Protocol,会话发起协议)是一种基于文本的网络通信协议,主要用于实现语音、视频和数据等多种媒体资源的实时传输。SIP信令在以下应用领域和行业中发挥着重要作用:
1.语音通信:SIP协议可用于固定电话、移动电话和网络电话之间的通话,实现电话拨号、通话建立、通话保持和通话结束等功能。
2.视频通信:通过SIP协议,用户可以实现音视频通话、视频会议和协同工作等应用,满足企业和个人之间的沟通需求。
3.即时通讯:SIP协议可应用于即时通讯领域,提供文本、图片、语音和视频等丰富的通信方式,如微信、WhatsApp等。
4.网络电视和多媒体广播:SIP协议可用于实现点播、直播和时移电视等多媒体服务,满足用户对多媒体内容的需求。
5.智能家居:通过SIP协议,可以实现家庭设备之间的互联互通,如智能音响、智能摄像头、智能照明等,提升家居生活的便捷性和舒适度。
6.企业通信:SIP协议可应用于企业内部通信系统,实现电话交换、电话会议、呼叫中心等功能,提高企业通信效率。
7.物联网:SIP协议可用于物联网设备之间的通信,实现智能控制、远程监控和数据分析等应用。
公共服务:SIP协议在公共安全、紧急救援、交通管理等领域具有广泛应用,提高公共服务的质量和效率。
8.教育:SIP协议可以实现远程教学、在线课堂和视频讲座等功能,拓展教育领域的发展空间。
9.医疗:通过SIP协议,可以实现远程诊断、视频咨询和在线挂号等医疗服务,提高医疗资源的利用效率。

本demo(项目)是JAIN SIP API的应用,适用于安防领域,当然如果你是一位其他领域的从业者,该项目代码也会起到举一反三、抛砖引玉的作用。

本项目不仅仅包含了SIP服务器,还有流媒体服务器和WEB服务器的实现,是一个完整的程序,方便实现二次开发与功能拓展。下文仅仅对SIP服务器的实现做简单的介绍。

2 GB28181

在GB28181-2022协议规范中“9控制、传输流程和协议接口”中规定了IPC注册、注销、点播、状态信息报送等控制的命令流程与协议接口,下面我们将按照GB28181流程,采用JAIN SIP API实现IPC的向SIP服务器的注册与状态信息报送(保活)。

2.1 注册

2.2 保活

2.2.1 命令流程

2.2.2 协议接口

2.3 MSH代码概述

2.3.1 创建springboot项目

创建一个springboot项目并引入JAIN SIP API依赖。

2.3.2 SIP协议栈

创建SipLayer声明CommandLineRunner接口,项目启动时会建立一堆以后会有用的对象:SipFactory、SipStack、ListeningPoint,同时创建TCP与UDP监听器用来兼容IPC的TCP/UDP接入。

SipLayer类注入SipConfig对象,该对象配置了SIP服务器的ip、端口、域名、id和密码。

SipLayer类注入SipServerListener,SipServerListener接口继承于SipListener,SipServerListener的子类为SipServerListenerImpl,SipServerListenerImpl为实现SIP请求响应的处理。

2.3.3 接收SIP请求响应

SipServerListenerImpl类实现了SipListener接口,重写processRequest()与processResponse()方法,来接收SIP请求与响应。该类采用了类似观察者模式的设计思路,声明了两个线程安全的容器reqHandlerMap与respHandlerMap用来存放不同的SIP请求响应的真实处理对象,例如processRequest()接收到一个REGISTER请求,利用java继承与多态的特性,processRequest()方法根据SIP方法类型为key获取到真实处理对象,最后由真实处理对象处理REGISTER请求。

在真实的平台与IPC进行信令交互时,会面临并发处理多种SIP请求响应的场景,所以在processRequest()与processResponse()方法上使用@Aync()注解,实现异步处理SIP信令。

2.3.4 处理SIP请求

SipReqHandler接口的实现类有两个RegisterReqHandler和KeepaliveReqHandler,分别实现IPC的注册与保活,代码实现流程请参照该小节的命令流程部分,最后我们将进行抓包分析整个信令的交互流程。

我们发现GB28181中,有很多控制传输流程都是通过MESSAGE方法+MANSCDP命令集实现的,所以我们要在接收到IPC的MESSAGE方法时,解析MANSCDP命令集,解析到cmdType = "Keepalive"的请求,才是保活请求,然后回复给IPC200,其他的MESSAGE请求这里暂时不处理。

2.3.5 发送SIP请求

SipSender类实现了SIP消息报文的封装,通过sendResponse()方法回复IPC消息。

2.3.6 IPC接入

1.首先启动SIP服务器,查看SIP服务器的配置信息:

#SIP # SIP服务器IP sip.ip=10.192.33.34 # SIP服务监听的端口 sip.port=5060 # SIP域 sip.domain=34020000 # SIP服务器国标ID sip.id=34020000001320000010 # SIP服务器密码 sip.password=admin123

2.IPC平台接入配置需要配置SIP服务器信息:

 IPC平台接入的密码为SIP服务器密码,用于服务器校验,校验正确才能实现IPC的注册。IPC每间隔60秒发送一次心跳信息,观察SIP服务器日志,满足GB28181规定命令流程,下面抓包分析信令交互流程。

3.SIP服务器日志:

 

 

2.3.7 抓包与流程分析
2.3.7.1 注册

REGISTER sip:34020000001320000010@34020000 SIP/2.0 Via: SIP/2.0/UDP 10.192.33.95:5060;rport;branch=z9hG4bK863117711 From: <sip:34020000001320000002@34020000>;tag=294565749 To: <sip:34020000001320000002@34020000> Call-ID: 1973051184 CSeq: 1 REGISTER Contact: <sip:34020000001320000002@10.192.33.95:5060> Max-Forwards: 70 User-Agent: IP Camera Expires: 3600 Content-Length: 0  SIP/2.0 401 Unauthorized CSeq: 1 REGISTER Call-ID: 1973051184 From: <sip:34020000001320000002@34020000>;tag=294565749 To: <sip:34020000001320000002@34020000> Via: SIP/2.0/UDP 10.192.33.95:5060;rport=5060;branch=z9hG4bK863117711;received=10.192.33.95 WWW-Authenticate: Digest realm="34020000",qop="auth",nonce="1ba00522b15b098aa2c05150cdb0df31",algorithm=MD5 User-Agent: sip-server-yrz Content-Length: 0  REGISTER sip:34020000001320000010@34020000 SIP/2.0 Via: SIP/2.0/UDP 10.192.33.95:5060;rport;branch=z9hG4bK713030866 From: <sip:34020000001320000002@34020000>;tag=294565749 To: <sip:34020000001320000002@34020000> Call-ID: 1973051184 CSeq: 2 REGISTER Contact: <sip:34020000001320000002@10.192.33.95:5060> Authorization: Digest username="34020000001320000002", realm="34020000", nonce="1ba00522b15b098aa2c05150cdb0df31", uri="sip:34020000001320000010@34020000", response="ff34c4434d132ad9b956c729aa229194", algorithm=MD5, cnonce="0a4f113b", qop=auth, nc=00000001 Max-Forwards: 70 User-Agent: IP Camera Expires: 3600 Content-Length: 0  SIP/2.0 200 OK CSeq: 2 REGISTER Call-ID: 1973051184 From: <sip:34020000001320000002@34020000>;tag=294565749 To: <sip:34020000001320000002@34020000> Via: SIP/2.0/UDP 10.192.33.95:5060;rport=5060;branch=z9hG4bK713030866;received=10.192.33.95 Date: 2023-04-19T11:29:33.703 Contact: <sip:34020000001320000002@10.192.33.95:5060> Expires: 3600 User-Agent: sip-server-yrz Content-Length: 0
  1. IPC发起REGISTER请求,未携带Authorization认证信息。

  2. SIP服务器回复401与认证加密算法。

  3. IPC重新发起REGISTER并携带Authorization认证信息。

  4. SIP服务器认证成功后回复200,IPC注册成功。

2.3.7.2 保活

MESSAGE sip:34020000001320000010@34020000 SIP/2.0 Via: SIP/2.0/UDP 10.192.33.95:5060;rport;branch=z9hG4bK1171736073 From: <sip:34020000001320000002@34020000>;tag=699092543 To: <sip:34020000001320000010@34020000> Call-ID: 776784695 CSeq: 20 MESSAGE Content-Type: Application/MANSCDP+xml Max-Forwards: 70 User-Agent: IP Camera Content-Length:   182  <?xml version="1.0" encoding="GB2312"?> <Notify> <CmdType>Keepalive</CmdType> <SN>3634867</SN> <DeviceID>34020000001320000002</DeviceID> <Status>OK</Status> <Info> </Info> </Notify> SIP/2.0 200 OK CSeq: 20 MESSAGE Call-ID: 776784695 From: <sip:34020000001320000002@34020000>;tag=699092543 To: <sip:34020000001320000010@34020000>;tag=1681876509157 Via: SIP/2.0/UDP 10.192.33.95:5060;rport=5060;branch=z9hG4bK1171736073;received=10.192.33.95 User-Agent: sip-server-yrz Content-Length: 0
  1. IPC发起MESSAGE请求并携带设备ID。

  2. SIP服务器回复200。

3 小结

3.1 SIP服务器

目前实现的为注册、保活、设备信息查询、目录查询、点播。下图为VLC播放取流播放截图。

3.2 WEB服务器

目前实现点播、通道同步、flv/hls/webrtc(zlm和webrtc-streame两种流媒体)格式流web预览。

支持人脸识别,人脸识别部分请查看文章http://t.csdnimg.cn/APKdS

代码持续更新中...

需要SIP服务器源码请私信我^-^

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!