janus模块介绍-SIP Gateway

模块启动

默认的SIP GateWay也是https协议,端口为8088或者8089
如果需要在自己搭建的测试服务上测试SIP GateWay模块,则也需要修改为wss
具体改动如下: 找到/opt/janus/share/janus/demos/siptest.js

var server = "wss://" + window.location.hostname + ":8989";

在这里插入图片描述
然后重启janus服务,执行

/opt/janus/bin/janus --debug-level=5 --log-file=$HOME/janus-log

打开测试网页,在demos中找到SIP Gateway,点击start开始运行。
在这里插入图片描述
在这里插入图片描述
点击f12可以看到demo中Using WebSockets to contact Janus: wss://49.232.250.45:8989
在这里插入图片描述

模块基本说明

这是Janus的一个简单SIP插件,允许WebRTC对等方在SIP服务器(例如Asterisk)注册,并通过Janus实例调用SIP用户代理。具体地,当连接到插件时,请求对等方提供其SIP服务器证书,即SIP服务器的地址和其用户名/密码。这导致插件在SIP服务器处注册,并代表网络对等方充当SIP客户端。大多数SIP状态和生存期都被插件屏蔽,只有相关事件(例如INVITE和BYE)和功能(呼叫、挂断)可供网络对等方使用:对等方可以在SIP服务器上呼叫扩展或等待传入的INVITE,并且在呼叫期间可以发送DTMF音调。调用可以执行纯RTP或SDES-SRTP。
该插件背后的概念是允许与同一对等方相关联的不同网页,从而允许同一SIP用户同时连接到插件,但只进行一次SIP REGISTER。这同样适用于呼叫:虽然传入的呼叫将被通知给与对等方相关的所有web UI,但只有一个能够接听和接听,这与SIP分叉的工作方式非常相似,但不需要在同一个地方分叉。然而,这一特定功能尚未实现。

SIP Plugin API

我们可以在SIP插件API中发送的所有请求都是异步的,这意味着所有响应(成功和错误)都将作为同一事务的事件传递。
支持的请求包括注册、注销、调用、接受、拒绝、信息、消息、dtmf_info、订阅、取消订阅、传输、录制、保留、取消保留、更新和挂断。顾名思义,register可以用于在SIP注册器处注册用户名以调用和被调用,而unregister则注销该用户名;call用于通过插件向不同的SIP URI发送INVITE,而accept和dence用于在邀请而不是邀请的情况下接受或拒绝呼叫;接送负责有人接送和无人接送(更多详细信息,请参阅有人接送或无人接送);hold和unhold可以分别用于将呼叫挂起和恢复呼叫;info允许您发送一个通用的SIP info请求,而dtmf_info则专注于将info用于dtmf;message是用于向另一个对等方发送SIP消息的方法;subscribe和unsubscribe用于处理SIP事件,即发送subscribe请求,该请求将导致NOTIFY异步事件;相反,录音用于将对话记录到一个或多个.mjr文件中(取决于您想要录制的方向);更新允许您更新现有会话(例如,进行重新协商或强制ICE重新启动);最后,挂断可以用于在任何时候终止通信,或者挂断(BYE)正在进行的呼叫,或者取消/拒绝(cancel/BYE)尚未开始的呼叫。
无论请求如何,错误响应或事件的格式始终如下:

{"sip" : "event","error_code" : <numeric ID, check Macros below>,"error" : "<error description as a string>"
}

请注意,上面的错误语法指的是插件API消息传递,而不是响应于SIP请求而获得的SIP错误代码,这些错误代码是使用不同的语法通知的:

{"sip" : "event","result" : {"event" : "<name of the error event>","code" : <SIP error code>,"reason" : "<SIP error reason>","reason_header" : "<Reason header text; optional>","reason_header_protocol" : "<Reason header protocol; optional>","reason_header_cause" : "<Reason header cause code; optional>"}
}

对于可用的请求,您可以使用注册请求发送一个SIP REGISTER。更准确地说,注册请求可能会导致SIP register,因为这种方法实际上提供了无需注册即可开始使用SIP帐户的方法。例如,所谓的访客注册就是这样的情况:如果你注册为访客,这意味着你将使用From标头中提供的SIP URI进行呼叫,但实际上你不会发送SIP register;这对于向不需要注册的服务(例如,IVR系统或会议桥)发出的呼叫特别有用,但也意味着除非对等方知道您的私人SIP地址,否则您将无法接收呼叫。注册为帮助者时也不会发送SIP REGISTER:正如我们稍后将解释的,帮助者会话仅用于方便使用同一帐户设置同时SIP呼叫。
也就是说,注册请求的格式必须如下:

{"request" : "register","type" : "<if guest or helper, no SIP REGISTER is actually sent; optional>","send_register" : <true|false; if false, no SIP REGISTER is actually sent; optional>,"force_udp" : <true|false; if true, forces UDP for the SIP messaging; optional>,"force_tcp" : <true|false; if true, forces TCP for the SIP messaging; optional>,"sips" : <true|false; if true, configures a SIPS URI too when registering; optional>,"rfc2543_cancel" : <true|false; if true, configures sip client to CANCEL pending INVITEs without having received a provisional response first; optional>,"username" : "<SIP URI to register; mandatory>","secret" : "<password to use to register; optional>","ha1_secret" : "<prehashed password to use to register; optional>","authuser" : "<username to use to authenticate (overrides the one in the SIP URI); optional>","display_name" : "<display name to use when sending SIP REGISTER; optional>","user_agent" : "<user agent to use when sending SIP REGISTER; optional>","proxy" : "<server to register at; optional, as won't be needed in case the REGISTER is not goint to be sent (e.g., guests)>","outbound_proxy" : "<outbound proxy to use, if any; optional>","headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP REGISTER; optional>","contact_params" : "<array of key/value objects, to specify custom Contact URI params to add to the SIP REGISTER; optional>","incoming_header_prefixes" : "<array of strings, to specify custom (non-standard) headers to read on incoming SIP events; optional>","refresh" : "<true|false; if true, only uses the SIP REGISTER as an update and not a new registration; optional>","master_id" : "<ID of an already registered account, if this is an helper for multiple calls (more on that later); optional>","register_ttl" : "<integer; number of seconds after which the registration should expire; optional>"
}

注册事件将被发回,因为这是一个异步请求。
如果需要,此请求将向具有正确凭据的指定服务器发起SIP REGISTER。401和407响应将被自动处理,因此错误将不会被通知回呼叫者,除非它们是决定性的(例如,错误的凭证)。注册失败将返回名称为registration_failed的错误。相反,成功注册会在注册事件中得到通知,格式如下:

{"sip" : "event","result" : {"event" : "registered","username" : <SIP URI username>,"register_sent" : <true|false, depending on whether a REGISTER was sent or not>,"master_id" : <unique ID of this registered session in the plugin, if a potential master>}
}

要注销,只需发送一个不带其他参数的注销请求:

{"request" : "unregister"
}

与以前一样,将发回一个注销事件。和以前一样,这也会发送一个SIP REGISTER,以防它最初被发送。在未注册的事件中通知成功注销:

{"sip" : "event","result" : {"event" : "unregistered","username" : <SIP URI username>,"register_sent" : <true|false, depending on whether a REGISTER was sent or not>}
}

注册后,您可以拨打电话或等待来电:请注意,如果您选择从不发送REGISTER,您将无法接听来电。
要发送SIP INVITE,您可以使用呼叫请求,其格式如下:

{"request" : "call","call_id" : "<user-defined value of Call-ID SIP header used in all SIP requests throughout the call; optional>","uri" : "<SIP URI to call; mandatory>","refer_id" : <in case this is the result of a REFER, the unique identifier that addresses it; optional>,"headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP INVITE; optional>","srtp" : "<whether to mandate (sdes_mandatory) or offer (sdes_optional) SRTP support; optional>","srtp_profile" : "<SRTP profile to negotiate, in case SRTP is offered; optional>","secret" : "<password to use to call, only needed in case authentication is needed and no REGISTER was sent; optional>","ha1_secret" : "<prehashed password to use to call, only needed in case authentication is needed and no REGISTER was sent; optional>","authuser" : "<username to use to authenticate as to call, only needed in case authentication is needed and no REGISTER was sent; optional>","autoaccept_reinvites" : <true|false, whether we should blindly accept re-INVITEs with a 200 OK instead of relaying the SDP to the application; optional, TRUE by default>
}

调用事件将被发回,因为这是一个异步请求。
请注意,此请求必须与JSEP优惠相关联:无法通过SIP插件发送无优惠INVITE。这将生成一个SIP INVITE,并根据指示进行发送。虽然100秒Trying不会通知回用户,但在振铃事件中,180秒将会响铃:

{"sip" : "event","call_id" : "<value of SIP Call-ID header for related call>","result" : {"event" : "ringing","headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"}
}

如果呼叫被拒绝或发生任何其他错误,则会发回挂断错误事件。如果呼叫被接受,则接受的事件将与被叫方发起的JSEP应答一起发送回用户:

{"sip" : "event","call_id" : "<value of SIP Call-ID header for related call>","result" : {"event" : "accepted","username" : "<SIP URI of the callee>","headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"}
}

在这一点上,除了PeerConnection相关的考虑因素外,可以认为该调用已建立。SIP插件会自动发送SIP ACK,因此不需要应用程序手动执行任何操作。
请注意,SIP插件通过183个响应响应支持早期媒体。在接收到183响应的情况下,在进度事件中,它将与被叫方发起的JSEP应答一起发送回用户:

{"sip" : "event","call_id" : "<value of SIP Call-ID header for related call>","result" : {"event" : "progress","username" : "<SIP URI of the callee>","headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"}
}

如果呼叫者收到进度事件,以下接受的事件将不包含JSEP应答,因为在“会话进度”事件中收到的应答将作为会话的SDP应答。
请注意,您只使用call来开始对话,即第一次INVITE。要通过重新邀请更新会话,例如,重新协商会话以添加/删除流或强制ICE重新启动,您不使用调用,而是使用另一个名为update的请求。这个请求不需要任何参数,因为整个上下文都是从会话的当前状态派生的。不过,作为重新谈判的一部分,它确实需要新的JSEP报价。

{"request" : "update"
}

更新事件将被发回,因为这是一个异步请求。
虽然呼叫请求允许您发送SIP INVITE(更新请求允许您更新现有会话),但也有一种方法可以对SIP INVITEs做出反应,即处理传入呼叫。传入呼叫通过传入呼叫事件通知应用程序:

{"sip" : "event","call_id" : "<value of SIP Call-ID header for related call>","result" : {"event" : "incomingcall","username" : "<SIP URI of the caller>","displayname" : "<display name of the caller, if available; optional>","callee" : "<SIP URI that was called (in case the user is associated with multiple public URIs)>","referred_by" : "<SIP URI header conveying the identity of the transferor, if this is a transfer; optional>","replaces" : "<call-ID of the call that this is supposed to replace, if this is an attended transfer; optional>","srtp" : "<whether the caller mandates (sdes_mandatory) or offers (sdes_optional) SRTP support; optional>","headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"}
}

来电可能会也可能不会附带JSEP报价,这取决于来电者发送的是无报价邀请还是常规邀请。无论哪种方式,您都可以通过接受请求来接受来电:

{"request" : "accept","srtp" : "<whether to mandate (sdes_mandatory) or offer (sdes_optional) SRTP support; optional>","headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP OK; optional>""autoaccept_reinvites" : <true|false, whether we should blindly accept re-INVITEs with a 200 OK instead of relaying the SDP to the browser; optional, TRUE by default>
}

由于这是一个异步请求,将发回一个接受事件。
这将导致200 OK被发送回呼叫者。接受请求必须始终附有JSEP回复(如果incomingcall事件包含要约)或要约(如果是无要约邀请)。在前一种情况下,接受的事件将被发回,只是为了确认呼叫可以被视为已建立;相反,在后一种情况下,接受事件将被发送回,而接受的事件只会在稍后,只要在呼叫者发送回的SIP ACK中有JSEP应答可用,就紧随其后。
请注意,如果您在另一个呼叫中接到来电,您将不会收到incomingcall事件,而是收到missed_call事件,这只是一个通知,因为在SIP插件中,无法在同一句柄上同时进行两个呼叫:

{"sip" : "event","call_id" : "<value of SIP Call-ID header for related call>","result" : {"event" : "missed_call","caller" : "<SIP URI of the caller>","displayname" : "<display name of the caller, if available; optional>","callee" : "<SIP URI that was called (in case the user is associated with multiple public URIs)>"}
}

此外,您只能使用accept来回答第一个INVITE。要接受重新邀请(将通过updatingcall事件通知),您不使用accept,而是使用之前引入的更新。这个请求不需要任何参数,因为整个上下文都是从会话的当前状态派生的。不过,作为重新谈判的一部分,它确实需要新的JSEP答案来提供。和以前一样,由于这是一个异步请求,因此会发回一个更新的事件。
关闭会话取决于调用状态。如果您有不想接听的来电,请使用拒绝请求;在所有其他情况下,请改用挂断请求。这两个请求都不需要额外的参数,因为整个上下文可以从插件中会话的当前状态中提取:

{"request" : "decline","code" : <SIP code to be sent, if not set, 486 is used; optional>","headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP request; optional>"
}
{"request" : "hangup","headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP BYE; optional>"
}

由于这些都是异步请求,您将得到一个响应事件:如果使用了谢绝,则拒绝;如果使用了挂起,则挂起。
如前所述,当呼叫被拒绝或挂断时,会发送一个挂断事件,这基本上是一个SIP错误事件通知,因为它包括代码和原因。例如,常规BYE将被通知200和SIP BYE,尽管也可以提供更详细的描述。
建立会话后,可以使用不同的请求与会话进行交互。
首先,您可以使用挂起请求将呼叫挂起。默认情况下,此请求将向对等方发送一个新的INVITE,其中包含媒体的sendonly方向,但如果您想设置不同的方向(recvonly或inactive),也可以通过传递direction属性来实现:

{"request" : "hold","direction" : "<sendonly, recvonly or inactive>"
}

持有者方不涉及WebRTC重新协商,因为这只会触发SIP方的重新INVITE。要从挂起状态移除调用,只需向插件发送一个取消挂起的请求,该请求不需要其他属性:

{"request" : "unhold"
}

并且将恢复在保持呼叫之前在SDP中设置的媒体方向。
消息请求允许您向对等方发送SIP message。默认情况下,在活动呼叫期间,它在对话框中发送。但是,如果用户已经注册,它也可能被发送到对话框之外。在这种情况下,uri参数是必需的。

{"request" : "message","call_id" : "<user-defined value of Call-ID SIP header used to send the message; optional>","content_type" : "<content type; optional>""content" : "<text to send>","uri" : "<SIP URI of the peer; optional; if set, the message will be sent out of dialog>","headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP MESSAGE; optional>"
}

messagesend事件将被发回。相反,传入的SIP消息会在消息事件中得到通知:

{"sip" : "event","result" : {"event" : "message","sender" : "<SIP URI of the message sender>","displayname" : "<display name of the sender, if available; optional>","content_type" : "<content type of the message>","content" : "<content of the message>","headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"}
}

传递后,消息传递事件将与SIP服务器响应一起发送回。用于跟踪邮件的传递状态。

{"sip" : "event","call_id" : "<value of SIP Call-ID header for related message>","result" : {"event" : "messagedelivery","code" : "<SIP error code>","reason" : "<SIP error reason>",}
}

SIP INFO的工作方式几乎相同,只是您使用一个对一个对等体的信息请求:

{"request" : "info","type" : "<content type>""content" : "<message to send>","headers" : "<object with key/value mappings (header name/value), to specify custom headers to add to the SIP INFO; optional>"
}

将发回一个infosend事件。相反,传入的SIP info会在信息事件中得到通知:

{"sip" : "event","result" : {"event" : "info","sender" : "<SIP URI of the message sender>","displayname" : "<display name of the sender, if available; optional>","type" : "<content type of the message>","content" : "<content of the message>","headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"}
}

正如预期的那样,使用SUBSCRIBE和NOTIFY机制也支持SIP事件。要做到这一点,您需要使用订阅请求,该请求的格式如下:

{"request" : "subscribe","call_id" : "<user-defined value of Call-ID SIP header used in all SIP requests throughout the subscription; optional>","event" : "<the event to subscribe to, e.g., 'message-summary'; mandatory>","accept" : "<what should be put in the Accept header; optional>","to" : "<who should be the SUBSCRIBE addressed to; optional, will use the user's identity if missing>","subscribe_ttl" : "<integer; number of seconds after which the subscription should expire; optional>","headers" : "<array of key/value objects, to specify custom headers to add to the SIP SUBSCRIBE; optional>"
}

如果subscribe请求被接受,则会发回订阅事件,然后是subscribe_succeed,如果事务失败,则会返回subscribe_failed。相反,传入的SIP NOTIFY事件在通知事件中得到通知:

{"sip" : "event","call_id" : "<value of SIP Call-ID header for related subscription>","result" : {"event" : "notify","notify" : "<name of the event that the user is subscribed to, e.g., 'message-summary'>","substate" : "<substate of the subscription, e.g., 'active'>","content-type" : "<content-type of the message>""content" : "<content of the message>","headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"}
}

您还可以录制SIP调用,它的工作原理与VideoCall插件的工作原理几乎相同。具体来说,您可以使用录制请求来启动或停止录制,使用以下语法:

{"request" : "recording","action" : "<start|stop, depending on whether you want to start or stop recording something>""audio" : <true|false; whether or not our audio should be recorded>,"video" : <true|false; whether or not our video should be recorded>,"peer_audio" : <true|false; whether or not our peer's audio should be recorded>,"peer_video" : <true|false; whether or not our peer's video should be recorded>,"send_peer_pli" : <true|false; whether or not send PLI to request keyframe from peer>,"filename" : "<base path/filename to use for all the recordings>"
}

正如您所看到的,这意味着对话的双方是分开录制的,音频和视频流也是如此(如果可用)。您可以选择要记录的内容,以防您只对子集感兴趣。文件名部分只是一个前缀,指示将用于最多四个可能需要启用的录制的实际文件名。
如果请求成功,则会发回已记录的更新事件。

Simultaneous SIP calls using the same account

如前几节所述,使用Janus句柄附加到SIP插件意味着代表用户或应用程序创建SIP堆栈:这通常意味着注册帐户,并能够启动或接收调用、处理订阅等。这也意味着,由于在Janus中,每个核心句柄只能与单个PeerConnection相关联,因此每个SIP帐户每次仅限于一个调用:如果用户已经在SIP会话中,并且另一个调用传入,则会自动拒绝,并显示486 Busy。
虽然通常不是什么大问题,但在某些用例中,支持多个并发调用是有意义的,并且可以从一个无缝地切换到另一个。这在使用所谓的助手会话的SIP插件中是可能的。具体来说,助手会话在假设存在正常注册的主会话(即“常规”SIP插件句柄)的情况下工作,并且这些助手会话可以简单地与之关联:任何时候需要另一个并发调用,如果主会话繁忙,则可以使用其中一个助手;可用的助手会话越多,可以建立的同时调用就越多。
其工作方式很简单:
您以通常的方式创建SIP会话,并在那里发送常规注册表;这将是主会话,并在成功注册时返回master_id;
对于要添加的每个助手,您将新的Janus句柄附加到SIP插件,并发送一个类型为“helper”的寄存器,并提供与主会话相同的用户名,以及引用主会话的master_id属性;
此时,新的助手与主机相关联,这意味着它可以用于启动新的调用或接收与主会话完全相同的调用,并使用相同的帐户信息、凭据等。
请注意,一旦主服务器注销,或者它所在的Janus句柄被分离,与它相关的所有助手会话也会自动删除。具体来说,插件将强制分离相关句柄。如果您需要再次注册,并希望在那里也有一些助手,您将不得不再次添加他们。
如果您想在实践中看到这一点,SIP插件演示有一个“隐藏”函数,您可以从JavaScript控制台调用该函数来玩助手:调用addHelper()函数将添加一个新的助手,并显示其他控件。您可以根据需要添加任意数量的辅助对象。

Attended and blind transfers

Janus SIP插件支持有人参与和盲传输,并且这样做主要依赖于多个调用功能:因此,请确保您已经阅读并熟悉关于使用相同帐户进行同步SIP调用的部分。
大多数与传输相关的功能都基于上一节中已经记录的现有消息和事件,但有几个方面需要注意。首先,如果您是传输者,则需要使用名为transfer的新请求,该请求允许您向受让者发送SIPREFER,以便到达不同的目标。传输请求的格式必须如下所示:

{"request" : "transfer","uri" : "<SIP URI to send the transferee too>","replace" : "<call-ID of the call this attended transfer is supposed to replace; default is none, which means blind/unattended transfer>"
}

无论是盲传输(无替换调用)还是有人参与传输,都将发回传输事件,因为这是一个异步请求。进一步的更新将以NOTIFY相关事件的形式出现,因为REFER隐式创建订阅。
相反,REFER的接收者将接收一个名为transfer的异步事件,其中包含它需要知道的信息。事实上,SIP插件不会自动执行任何操作:传入的REFER被通知给应用程序,以便它可以决定是否跟踪传输。事件的语法如下:

{"sip" : "event","result" : {"event" : "transfer","refer_id" : <unique ID, internal to Janus, of this referral>,"refer_to" : "<SIP URI to call>","referred_by" : "<SIP URI SIP URI header conveying the identity of the transferor; optional>","replaces" : "<call-ID of the call this transfer is supposed to replace; optional, and only present for attended transfers>","headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"}
}

该列表中最重要的属性是refer_id,因为如果传输被接受,则该值必须包含在调用请求中以调用目标:事实上,这是SIP插件必须将新的传出调用与前一个传输请求相关联的唯一方法,从而能够通过notify事件通知传输者调用的进展情况。请注意,如果受让人决定跟踪转移请求,并且他们已经在通话中(例如,与转发器),则他们必须为此目的使用不同的句柄,例如,通过使用相同帐户部分的同步SIP呼叫中描述的助手。
传输目标将完全像前面讨论的那样接收调用,不同的是,出于信息目的,它可能包含或不包含referred_by属性。就像受让人一样,如果他们已经在调用中,则应由应用程序创建一个助手来设置一个新的Janus句柄以接受传输。
请注意,插件不会将涉及的调用置于等待状态,或自动关闭打算由传输替换的调用。所有这些都是应用程序的责任,因此开发人员应该相应地对事件做出反应。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/828938.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

比较好的平民衣服品牌有哪些?平价质量好短袖品牌推荐

随着气候变暖&#xff0c;夏天的持续时间似乎越来越长&#xff0c;短袖作为夏季的必备服装&#xff0c;受到了广大男士的青睐。然而&#xff0c;面对市场上众多的短袖品牌和不同的质量&#xff0c;大家都觉得选短袖的时候实在难以找到质量好且合适自己的。 选择合适的短袖确实…

第59篇:创建Nios II工程之控制LED<一>

Q&#xff1a;还记得第1篇吗&#xff1f;设计简单的逻辑电路&#xff0c;控制DE2-115开发板上LED的亮与熄灭&#xff0c;一行Verilog HDL的assign赋值语句即可实现。本期开始创建Nios II工程&#xff0c;用C语言代码控制DE2-115开发板上的LED实现流水灯效果。 A&#xff1a;在…

VPP 中注册的node是如何被调用起来的

当我们在VPP/plugins目录下注册了自己的node后&#xff0c; 肯定有一个node.func(), 那这个函数是如何执行到的呢&#xff1a; 1. 首先我们要看一下这个插件注册的时候做了什么&#xff0c; 假设node 如下&#xff1a; 编译成功后&#xff0c; 我们可以从函数vlib_plugin_earl…

回归与聚类——K-Means(六)

什么是无监督学习 一家广告平台需要根据相似的人口学特征和购买习惯将美国人口分成不同的小 组&#xff0c;以便广告客户可以通过有关联的广告接触到他们的目标客户。Airbnb 需要将自己的房屋清单分组成不同的社区&#xff0c;以便用户能更轻松地查阅这些清单。一个数据科学团队…

工作记录:vue-grid-layout 修改 margin 导致 item 高度剧烈变化

问题 用 vue-gird-layout 时发现&#xff0c;当改变 margin 值时&#xff0c;item 的尺寸也会跟着变化。 如下图&#xff1a;row height 和每个 item 的 h 都保持不变。修改 margin-y&#xff0c;item 的实际高度也跟着变了&#xff1a; 原因 研究了一番&#xff0c;发现原…

python flask 假死情况处理+https证书添加

前言 当使用flask编写了后台程序跑在服务器端的时候&#xff0c;有时候虽然后台中显示在运行&#xff0c;但是页面无法访问&#xff0c;出现这个情况可以使用如下方法修改代码&#xff0c;进而防止假死&#xff0c;另外记录下flask下证书的添加。 假死处理 出现进程存在&…

SSM项目前后端分离详细说明

1.后端 1.1打包 说明&#xff1a;使用idea打开项目&#xff0c;然后进行打包。 1.2tomcat 说明&#xff1a;把后端打成war包后放入tomcat启动。 1.3启动tomcat 说明&#xff1a; 找到tomcat中bin目录中的startup.bat文件&#xff0c;进行启动。如果启动失败&#xff0c;可以…

【热门话题】PostCSS:现代前端开发中的CSS增强工具

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 PostCSS&#xff1a;现代前端开发中的CSS增强工具一、引言二、PostCSS简介2.1 核…

大数据计算引擎中的Calcite

1.Calcite介绍 Calcite是一个动态数据库管理框架&#xff0c;具备数据库管理系统的功能 Calcite具备SQL解析、校验、优化、生成、连接查询等功能 Calcite能够为不同平台和数据源提供统一的查询引擎 2.Calcite能力 比如&#xff0c;对于HBase而言&#xff0c;没有SQL查询的能力…

C++ 并发编程 - 入门

目录 写在前面 并发编程&#xff0c;启动&#xff01; 写在前面 计算机的并发指在单个系统里同时执行多个独立的任务。 在过去计算机内只有一个处理器时并发是通过快速的切换进程上下文所实现的&#xff0c;而现在计算机已经步入了多核并发时代&#xff0c;所以多个进程的并…

号卡流量卡分销推广系统源码

这是一个多功能的流量卡推广分销系统PHP源码&#xff0c;它是一套完善的、功能丰富的号卡分销系统&#xff0c;拥有多个接口&#xff0c;包括运营商接口&#xff0c;以及无限三级代理。这是目前市面上最优雅的号卡系统&#xff0c;没有之一。 软件架构说明&#xff1a; 环境要求…

239. 滑动窗口最大值/76. 最小覆盖子串

239. 滑动窗口最大值 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1a; 输入&#xff1a;nums [1,3,-…

python获取文件路径

文件&#xff1a;allpath_parameter.py # 获取当前目录路径 # current_dir os.getcwd() # 获取当前目录路径 realpath00 os.path.abspath(os.path.join(os.path.dirname(os.path.split(os.path.realpath(__file__))[0]), .)) print(realpath00)# 获取当前目录的上级目录路…

深圳智能抄表:现代城市管理的新篇章

1.行业背景和创新 深圳&#xff0c;做为中国最有创新力的城市之一。智能抄表是这处城市在公共服务领域的一次重大自主创新&#xff0c;是利用物联网技术、大数据和云计算等先进技术&#xff0c;改变了传统的手工制作抄水表方法&#xff0c;提高了效率&#xff0c;减少了偏差&a…

第一篇:Python简介:开启你的编程之旅

Python简介&#xff1a;开启你的编程之旅 在这个系列文章中&#xff0c;我将带领大家深入了解Python——一个极具魅力的编程语言。如果你对编程感兴趣&#xff0c;想要掌握一门既实用又强大的语言&#xff0c;那么Python无疑是一个绝佳的选择。本篇文章是这个系列的序章&#…

vue3 h5模板

vue3的h5模板 基于vue3tsvantrem的h5模板 觉得帮到你了就给个start

C++之通俗易懂学模版

目录 一、了解什么是泛性编程 二、模版 1.函数模版 1.1 函数模板概念 1.2 函数模板格式 1.3 函数模板的原理 1.4 函数模板的实例化 1.5 模板参数的匹配原则 2.类模板 2.1 类模板的定义格式 2.2 类模板的实例化 3. 非类型模板参数 4. 模板的特化 4.1 概念 4.2 …

java:Http协议和Tomcat

HTTP协议 Hyper Text Transfer Protocol 超文本传输协议,规定了浏览器和服务器之间数据传输的规则 特点: 基于TCP协议,面向连接,安全 基于请求响应模型:一次请求对应一次响应 HTTP协议是无状态协议,对事务的处理没有记忆能力,每次请求-响应都是独立的. 优点 速度较快 …

OceanBase开发者大会实录 - 阳振坤:云时代的数据库

本文来自2024 OceanBase开发者大会&#xff0c;OceanBase 首席科学家阳振坤的演讲实录——《云时代的数据库》。完整视频回看&#xff0c;请点击这里 >> 在去年的开发者大会中&#xff0c;我跟大家分享了我对数据库产品和技术一些看法&#xff0c;包括单机分布式一体化&…

书生·浦语 大模型(学习笔记-9)OpenCompass 大模型评测实战

目录 一、评测实现双赢 二、评测遇到的问题 三、如何评测大模型&#xff08;大概总结4大类方法&#xff09; 四、评测工具链及流水线 五、实战评测 GPU的环境安装 查看支持的数据集和模型 启动评测(会缺少protibuf库&#xff0c;提前安装&#xff09; 测评结果 一、评…