SIP与RTP综合应用(转)

2023-11-19

SIP是一个会话协议,很多大企业都在用,通信行业的一个标准,其业务逻辑比较,简单地来说如下:

User Agent Server

 ------------------REGISTER----------->

 <----------401(407) Unauthorized--

 ----------REG(带上用户口令)----------->

 ---------------200 OK 1 Bindings---

双方交互几次,注册成功。

因为Sip 通信一般采用UDP,所以有个保活的问题,一般每隔两三分钟再向server注册一下。server也可能每隔一两分钟向客户发Unauthorized,让客户再刷新一下登录。

登录成功后,某个客户端向另一个客户端发起呼叫,通过服务器中转命令。简单来讲,这个和IM的原理是一样的。对方同意接收呼叫后,把媒体端口通知给server 及对方。到了这里,有IM开发经验的人,自然就知道下一步怎么做了:如果想P2P直连的话,就先穿透NAT打洞,否则就通过Server中转。

很明显,SIP会话和现有的IM类似,但效率或效果上来讲差的很多,比如登录保活, 还是同名用户同时登录等等,都处理的不够好。不过SIP是电信协议,最初是用在VOIP和可视电话上,环境比IM简单地多,所以这个协议足够用了,估计名字中的S也是因为这个原因。

sip呼叫成功,建立连接之后,媒体传输(音视频)是通过RTP协议进行的。简单地说,采集到声音和视频,先按指定编码方面编码,比如音频编码成 g711,视频编码成h263,然后根据RFC相关协议加上包头用UDP向指定发送出去。对方收到后先解包,再解码,然后播放。

如果想了解SIP的详细工作流程,可以这样:

1 找一个外网的sip server (如果有经验,可以用yate2,或Trixbox等自己搭建)

2 安装x-lite ( 很不错的sip软电话客户端,如果安装eyeBeam更好,带视频)

3 安装ethereal和WinPcap (抓包工具)

然后,用x-lite拨打其他的客户端或SIP话机,用抓包工具抓出相关的数据包,先看流程,然后再看包结构。

后面附上一个介绍SIP的PPT,写的非常好,可能是台湾方面出品,以前收集的。是个.rar文件,因为这里只能上传图片,所以改名为.jpg再上传,下载后把.jpg去掉解压就可以了。

PPT写的非常好,用心看,很快就能了解SIP的工作流程。

下一步,就是自己动手实现SIP VOIP系统了。

如果商用的话,server 采用Trixbox,也可以仔细研究一下 Asterisk。客户端就用x-lite好了。

做为程序员,第一反应就是怎么样自己动手写一个客户端,甚至服务器。好在开源产品众多,写一个并不难。

经过几天的调试,发现几个协议栈做的不错:

1 SIP协议栈:

 a osip+exosip (建立客户端及通信非常简单,质量也好),

 b reSIProcate (全面,有server端例子,综合调试方便)。

 c 其他的还用过一个pjsip,不过它与音视频结合成一个库之后, 音频质量不好。但是比较小巧, 听说台湾很 多嵌入设备采用。

2 RTP协议栈:

 a Linphone采用的是oRTP,音视频部分采用的是 MediaStreamer2

 b JRtpLib,结合emiplib的音视频处理。

 c ffmpeg,ffmpeg本来是专门处理音视频编解码的,不过也提供了rtp,rtsp,最近好象也增加了rtmp协议的支持。顺便一提,MS2和emiplib底层也采用了ffmpeg。只要和音视频打交道,并且质量很不错的产品,都离不开它,比如mplayer,ffdshow。顺便BS一下kmplayer,上了ffmpeg黑名单。

 这里面着重提到的是jrtplib,之前误解为它只是按RTP传输数据包,以前写过的几个文章,都是在RTP包之后,自己再封装了一下,当然,做为自己用的音视频聊天程序,这样是没问题的。但用在SIP及其他VOIP产品上,要考虑互通,就要严格搂RTP协议来执行了。

了解了几个开源的东西,下面自己动手建一个简单的SIP环境:

1 对Linux比较熟的人, 在CentOS上安装Asterisk,客户端采用Linphone,自己研究吧。

2 象我这样只要在Linux下用点g++的,如果想针对VOIP快速学习的话,服务器安装yate2,客户端随便拿哪个都行。

3 如果自己想定制sip server,干脆一步到位,下载reSIProcate,用vc2005编译,一次通过。运行时提示缺少几个dll,google一下很快都找到了,然后运行repro,做为server先临时用着,反正是学习。

 客户端呢,网上流行一个很不错的,名字叫Youtoo,下载,简单编译后可以做为一个语音的客户端使用。

然后,PC上安装几个虚拟机,一个运行server,一个运行x-lite(做为一个参考的标准),主要上运行我们自己写的客户端进行测试。如果要调试server,就是主机上运行repro,虚拟上分别运行两个x-lite。

环境搭建立好了,下一步就开始调试。

根据这几天的实践,找出了一个最优的配置:

1 sip server采用Trixbox,如果对Linux很熟建议直接用Asterisk.

2 客户端如果直接使用,建议ekiga.

顺便说一下几个客户端使用的感受:

1 linphone:好象名气不小,不过,最新版3.1.2安装后启动就崩溃。我安装的是普通的XP-SP3,电脑公司特别版。一般软件运行没问题。如果在这个平台上都崩溃,真不知道说什么好。

 后来再试3.1.1,这个可以启动,运行能看到视频图像。不过奇怪的是,与视频电话连接上视频窗口反而隐藏起来。结束通话,又显示出来。搞不懂它的视频功能是做什么用的。另外显示本地视频只支持QCIF。

2 eyeBeam :名气更大,使用起来也不错,这个不错不包含视频功能。如果启动了视频的话,只显示第一帧图,摄像头怎么转它也不动。另外,主动连视频电话时,不能启动视频功能。视频电话呼叫它才能启动。启动后,"Start Video"点一下又灰住,完全不能用。

3 Wengo不错,现在改名叫Quate了,连接摄像头非常快,本地预览也正常。不过,解码有问题,看 到的是一堆绿色图块,不知道是它解不了码,还是弄几个颜色块在那边骗人玩。

4 yate好象不支持视频,不过声音倒是不错。

5 回头再说ekiga,边续试用了上面几个软件,以为我用的视频电话硬件有问题,但用ekiga连接后,双方的视频都正常。

上面是从视频效果角度出发来评测的,如果不使用视频的话都差不多。

搭建好环境,测试通过,熟悉协议之后, 就是自己做一个这样的平台了。

服务器想都不用想,直接用Tixbox,重头写不现实。

至于客户端,一般的程序架构应该如下:

一 协议部分:

主要处理sip的注册,呼叫,接收,挂机等功能,所有的协议都差不多,随便选一个就行。

二 媒体传输,这部分比较复杂 :

1 音视频采集

2 音视频编码

3 音视频编码后组RTP

4 RTP/RTCP发送

5 RTP/RTCP接收

6 从RTP解包还原成编码后的音视频

7 音视频解码

8 音视频播放

一般如果分配任务,快速做一个客户端,首先想到的就是找一个开源,编译出来再修改。

不过,试了几个,极度痛苦,分别说一下。

1 Linphone:

这个产品只能算一般,不过用到的lib非常不错,exosip+osip为sip命令服务,ortp+mediastreamer2为流媒体服务。不过,编译真是麻烦,别的不说,光mediastreamer2就用到了ffmpeg,gsm,ortp,srtp,openssl,speex, theora等,稀里湖涂足足花了大半天时间把所有这些都编译好,然后编译ms2.lib时提示几个链接出错。因为我看到网上几个文章说明是用vc2005轻松编译出来的,我也用的vc2005。估计用mingw会简单一些。不过,已经耗了近一天的时间,感觉不爽,放弃。估计是linphone估计搞的复杂,好让antisip卖钱。

2 ekiga

这个需要ptlib,第一感觉这东西很麻烦,不过编译时出奇的顺利(关键是官方提供的资料详细,网友写的文章也详细)。然后编译opal,也很顺利。(只是占用机器比较厉害,P4 2.6的占CPU极严重。不过,用的机器是联想的超薄机箱那种,不排除官方弄个很烂的CPU冒充。因为换到另一个P4 3G,速度快上两三倍)。

其实,编译好opal,基本就可以了,它带了很不错的例子,拨打电话接听都不错。

最后编译ekiga时,需要交叉编译,直接放弃掉,有那时间不如好好研究opal了。

3 其他

编译了一下emiplib,这个库写的真不错。虽然封装的比较深,不过调用时,可以选择比较靠上的类来调用,有点类似ACE。只是视频格式少了一点。

回头再看上面的一般结构,SIP部分不用操心,随便找个库就能达到目的,关键是媒体传输这部分。仔细看1-8这些部分,很多我们自己动手就可以做,其实我们并不需要一下完整的全功能的库。

比如,音频采集播放用DirectSound,视频采用播放用DirectShow.编解码用ffmpeg编译出来的libavcodec,传输用jrtplib.这么一看,只有3 音视频编码后组RTP和6 从RTP解包还原成编码后的音视频 这两部分相对陌生,其他的都能找到成熟的代码。基于这个想法,就不用上述开源产品,直接自己写一个好了。

先做准备工作:

1 编译好ffmpeg及所带的libavcodec等几个lib和dll,音视频编解码时需要。

2 利用DirectShow做视频采集。播放就直接用GDI画图好了,简洁。

3 声音部分,参考Youtoo这个程序,连SIP都有了,用现成的exosip,osip,ms2.lib(这个库不支持视频,否则就不用做上面那些苦力了)。音质相当不错。

4 传输就用jrtplib,不过开始为了调试方便,自己写的UDP socket。

这些准备工作做好,下一步就开始参考RFC进行RTP的组包和解包了。

RTP接收部分比较简单(不用考虑jitterbuffer等),先从这里入手。

其实主要就3步:

1 创建一个udp,监听一个端口,比如5200。

2 收到RTP包,送到解包程序,继续收第 二个。

3 收齐一帧后,或保存文件,或解码去播放。

下面详细说一下具体过程:

1 创建UDP,非常非常地简单(这里只是简单地模拟RTP接收,虽然能正常工作,但是没有处理RTCP部分,会影响发送端):

lass CUDPSocket : public CAsyncSocket

{

public:

 CUDPSocket();

 virtual ~CUDPSocket();

 virtual void OnReceive(int nErrorCode);

};

调用者:CUDPSocket m_udp; m_udp.Create(...);这样就可以了。注意端口,如果指定端口创建不成功,就端口+1或+2重试一下。

重写OnReceive:

void CUDPSocket::OnReceive(int nErrorCode)

{

 char szBuffer[1500];

 SOCKADDR_IN sockAddr;

 memset(&sockAddr, 0, sizeof(sockAddr));

 int nSockAddrLen = sizeof(sockAddr);

 int nResult = ReceiveFrom(szBuffer, 1500, (SOCKADDR*)&sockAddr, &nSockAddrLen, 0);

 if(nResult == SOCKET_ERROR)

 {

 return;

 }

//如果必要可以处理对方IP端口

 USHORT unPort = ntohs(sockAddr.sin_port);

 ULONG ulIP = sockAddr.sin_addr.s_addr;

//收到的数据送去解码

 Decode((BYTE*)szBuffer, nResult);

}

2 收到了数据,开始Decode,一般通过RTP传输的视频主要有h263 (old,1998,2000),h264,mpeg4-es。mpeg4-es格式最简单,就从它入手。

如果了解RFC3160,直接分析格式写就是了。如果想偷懒,用现成的, 也找的到:在opal项目下,有个plugins目录,视频中包含了h261,h263,h264,mpeg4等多种解包,解码的源码,稍加改动就可以拿来用。

首先看:video\common下的rtpframe.h这个文件,这是对RTP包头的数据和操作的封装:

#ifndef __RTPFRAME_H__

#define __RTPFRAME_H__ 1

#ifdef _MSC_VER

#pragma warning(disable:4800) // disable performance warning

#endif

class RTPFrame {

public:

 RTPFrame(const unsigned char * frame, int frameLen) {

 _frame = (unsigned char*) frame;

 _frameLen = frameLen;

 };

 RTPFrame(unsigned char * frame, int frameLen, unsigned char payloadType) {

 _frame = frame;

 _frameLen = frameLen;

 if (_frameLen > 0)

 _frame [0] = 0x80;

 SetPayloadType(payloadType);

 }

 unsigned GetPayloadSize() const {

 return (_frameLen - GetHeaderSize());

 }

 void SetPayloadSize(int size) {

 _frameLen = size + GetHeaderSize();

 }

 int GetFrameLen () const {

 return (_frameLen);

 }

 unsigned char * GetPayloadPtr() const {

 return (_frame + GetHeaderSize());

 }

 int GetHeaderSize() const {

 int size;

 size = 12;

 if (_frameLen < 12)

 return 0;

 size += (_frame[0] & 0x0f) * 4;

 if (!(_frame[0] & 0x10))

 return size;

 if ((size + 4) < _frameLen)

 return (size + 4 + (_frame[size + 2] << 8) + _frame[size + 3]);

 return 0;

 }

 bool GetMarker() const {

 if (_frameLen < 2)

 return false;

 return (_frame[1] & 0x80);

 }

 unsigned GetSequenceNumber() const {

 if (_frameLen < 4)

 return 0;

 return (_frame[2] << 8) + _frame[3];

 }

 void SetMarker(bool set) {

 if (_frameLen < 2)

 return;

 _frame[1] = _frame[1] & 0x7f;

 if (set) _frame[1] = _frame[1] | 0x80;

 }

 void SetPayloadType(unsigned char type) {

 if (_frameLen < 2)

 return;

 _frame[1] = _frame [1] & 0x80;

 _frame[1] = _frame [1] | (type & 0x7f);

 }

 unsigned char GetPayloadType() const

 {

 if (_frameLen < 1)

 return 0xff;

 return _frame[1] & 0x7f;

 }

 unsigned long GetTimestamp() const {

 if (_frameLen < 8)

 return 0;

 return ((_frame[4] << 24) + (_frame[5] << 16) + (_frame[6] << 8) + _frame[7]);

 }

 void SetTimestamp(unsigned long timestamp) {

 if (_frameLen < 8)

 return;

 _frame[4] = (unsigned char) ((timestamp >> 24) & 0xff);

 _frame[5] = (unsigned char) ((timestamp >> 16) & 0xff);

 _frame[6] = (unsigned char) ((timestamp >> 8) & 0xff);

 _frame[7] = (unsigned char) (timestamp & 0xff);

 };

protected:

 unsigned char* _frame;

 int _frameLen;

};

struct frameHeader {

 unsigned int x;

 unsigned int y;

 unsigned int width;

 unsigned int height;

};

#endif

原封不动,可以直接拿来使用。当然,自己写一个也不麻烦。很多人写不好估计是卡在位运算上了。

然后,进入video\MPEG4-ffmpeg目录下看mpeg4.cxx,这里包含了完整的RFC解包重组及MPEG4解码的源码。直接编译可能通不过,好在代码写的非常整齐,提取出来就是了。解包解码只要看这一个函数:

bool MPEG4DecoderContext::DecodeFrames(const BYTE * src, unsigned & srcLen,

 BYTE * dst, unsigned & dstLen,

 unsigned int & flags)

{

 if (!FFMPEGLibraryInstance.IsLoaded())

 return 0;

 // Creates our frames

 RTPFrame srcRTP(src, srcLen);

 RTPFrame dstRTP(dst, dstLen, RTP_DYNAMIC_PAYLOAD);

 dstLen = 0;

 flags = 0;

 int srcPayloadSize = srcRTP.GetPayloadSize();

 SetDynamicDecodingParams(true); // Adjust dynamic settings, restart allowed

 // Don't exceed buffer limits. _encFrameLen set by ResizeDecodingFrame

 if(_lastPktOffset + srcPayloadSize < _encFrameLen)

 {

 // Copy the payload data into the buffer and update the offset

 memcpy(_encFrameBuffer + _lastPktOffset, srcRTP.GetPayloadPtr(),

 srcPayloadSize);

 _lastPktOffset += srcPayloadSize;

 }

 else {

 // Likely we dropped the marker packet, so at this point we have a

 // full buffer with some of the frame we wanted and some of the next

 // frame.

 //I'm on the fence about whether to send the data to the

 // decoder and hope for the best, or to throw it all away and start

 // again.

 // throw the data away and ask for an IFrame

 TRACE(1, "MPEG4\tDecoder\tWaiting for an I-Frame");

 _lastPktOffset = 0;

 flags = (_gotAGoodFrame ? PluginCodec_ReturnCoderRequestIFrame : 0);

 _gotAGoodFrame = false;

 return 1;

 }

 // decode the frame if we got the marker packet

 int got_picture = 0;

 if (srcRTP.GetMarker()) {

 _frameNum++;

 int len = FFMPEGLibraryInstance.AvcodecDecodeVideo

 (_avcontext, _avpicture, &got_picture,

 _encFrameBuffer, _lastPktOffset);

 if (len >= 0 && got_picture) {

#ifdef LIBAVCODEC_HAVE_SOURCE_DIR

 if (DecoderError(_keyRefreshThresh)) {

 // ask for an IFrame update, but still show what we've got

 flags = (_gotAGoodFrame ? PluginCodec_ReturnCoderRequestIFrame : 0);

 _gotAGoodFrame = false;

 }

#endif

 TRACE_UP(4, "MPEG4\tDecoder\tDecoded " << len << " bytes" << ", Resolution: " << _avcontext->width << "x" << _avcontext->height);

 // If the decoding size changes on us, we can catch it and resize

 if (!_disableResize

 && (_frameWidth != (unsigned)_avcontext->width

 || _frameHeight != (unsigned)_avcontext->height))

 {

 // Set the decoding width to what avcodec says it is

 _frameWidth = _avcontext->width;

 _frameHeight = _avcontext->height;

 // Set dynamic settings (framesize), restart as needed

 SetDynamicDecodingParams(true);

 return true;

 }

 // it's stride time

 int frameBytes = (_frameWidth * _frameHeight * 3) / 2;

 PluginCodec_Video_FrameHeader * header

 = (PluginCodec_Video_FrameHeader *)dstRTP.GetPayloadPtr();

 header->x = header->y = 0;

 header->width = _frameWidth;

 header->height = _frameHeight;

 unsigned char *dstData = OPAL_VIDEO_FRAME_DATA_PTR(header);

 for (int i=0; i<3; i ++) {

 unsigned char *srcData = _avpicture->data[i];

 int dst_stride = i ? _frameWidth >> 1 : _frameWidth;

 int src_stride = _avpicture->linesize[i];

 int h = i ? _frameHeight >> 1 : _frameHeight;

 if (src_stride==dst_stride) {

 memcpy(dstData, srcData, dst_stride*h);

 dstData += dst_stride*h;

 }

 else

 {

 while (h--) {

 memcpy(dstData, srcData, dst_stride);

 dstData += dst_stride;

 srcData += src_stride;

 }

 }

 }

 // Treating the screen as an RTP is weird

 dstRTP.SetPayloadSize(sizeof(PluginCodec_Video_FrameHeader)

 + frameBytes);

 dstRTP.SetPayloadType(RTP_DYNAMIC_PAYLOAD);

 dstRTP.SetTimestamp(srcRTP.GetTimestamp());

 dstRTP.SetMarker(true);

 dstLen = dstRTP.GetFrameLen();

 flags = PluginCodec_ReturnCoderLastFrame;

 _gotAGoodFrame = true;

 }

 else {

 TRACE(1, "MPEG4\tDecoder\tDecoded "<< len << " bytes without getting a Picture...");

 // decoding error, ask for an IFrame update

 flags = (_gotAGoodFrame ? PluginCodec_ReturnCoderRequestIFrame : 0);

 _gotAGoodFrame = false;

 }

 _lastPktOffset = 0;

 }

 return true;

}

写的非常非常的明白:if (srcRTP.GetMarker()),到了这里表示收满了一包,开始去解码。

mpeg4-es的RFC还原重组就这么简单,下一步的解码,就涉及到用libavcodec.dll了

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SIP与RTP综合应用(转) 的相关文章

  • sip篇——服务器

    上篇提到sip定义了如何管理整个会话过程 xff0c 其实就是负责建立连接 监控和断开一个或多个参与者参与的会话 xff0c 利用IP地址和协议建立网络会话 xff0c 使得世界各地的人都可以通过互联网及时建立连接 1 sip中的逻辑组件
  • RTSP协议,与sip区别

    RTSP Real Time Streaming Protocol 是应用层中的实时流协议 xff0c 处于RTP RTCP SRTP等之上 xff0c 使用RTP或TCP来进行数据的传输 xff0c 其本身并不传输媒体流数据 xff0c
  • sip 认证分析

    SIP类似Http协议 其认证模式也一样 Http协议 xff08 RFC 2616 xff09 规定可以采用Basic模式和摘要模式 xff08 Digest schema xff09 RFC 2617 专门对两种认证模式做了规定 RFC
  • SIP鉴权—摘要式认证

    SIP 认证过程源自 HTTP 摘要式认证 HTTP Digest Authentication xff0c 它是一种基于质询的安全机制 当服务器收到一个请求 xff0c 将质询请求的发起者 xff0c 要求提供相应的身份信息 服务器发出的
  • SIP 鉴权 & HTTP 认证

    sip 鉴权是基于摘要签名认证的 具体来说 每一个用户都有一个用户名和密码 用户名和密码在客户端和SIP 服务器的数据库中都有保存 在认证的过程中 客户端将自己的信息 用户名 密码 url 等信息 做一些复杂的MD5 或者SHA256 SH
  • pjsip的一个qt写的demo

    msvc版本编译的pjsip的demo 有源码 也有可直接运行的包 本程序解决了pjsip双方互相同时呼叫时会出现的问题 目前只是用来呼叫接听的demo 没有做流媒体传输 https download csdn net download q
  • 5G Capital一年,“首都标准”初现

    在北京生活许多年 如果问我什么时候京味浓度最高 答案可能是下了飞机 走上出租车的那一刻 北京司机连闲聊都是一副见过世面的样子 你研究人工智能 我觉得吧交通管理就该这样那样 高铁咱都造出来了 什么高科技我看中国人很快就能搞出来 冬奥会场馆建得
  • 安卓 SIP SSL

    我在 Android 2 3 4 中使用 SIP 堆栈 一切运行良好 但是 我需要系统使用 SSL 并创建了以下代码 SipProfile Builder builder new SipProfile Builder user domain
  • C# SIP 堆栈/库 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 目前不接受答案 我正在寻找一个好的 SIP 库 要么用 C 编写 要么提供 C 包装器 不一定需要免费 有人用过什么好东西吗 为了澄清起见 我说的是 VoIP 协议
  • TAPI 的替代技术?

    是否有支持第三方呼叫控制 3pcc 的 TAPI 替代技术 我想在应用程序中提供以下 3pcc 功能 拨出电话 用户单击应用程序中的按钮 用户的电话摘机 被叫电话振铃 被叫者的电话显示被叫者的电话号码 而不是用于应用程序的电话号码 当被叫者
  • SIP 408:在 zoiper 中创建帐户时请求超时

    我已经下载了zoiper220 linux tar gz并解压缩它 但是当我尝试在其中创建帐户时 它显示 SIP 408 请求超时错误 我已经尝试这个好几天了 提前致谢 您必须首先使用 sip conf 中设置的用户名和密码在软电话中注册
  • 使用 Android SIP 堆栈进行点对点 SIP 呼叫?

    我一直在寻找一种方法来设置 Android SIP 堆栈 以便能够以临时方式在同一网络上的两个设备之间建立 SIP 呼叫 即无需注册到 SIP 服务器 我无法让它工作 因为 SIP 演示包括服务器注册 如果没有此步骤 我无法让它拨打或接听电
  • SIP 软电话客户端的 java SDK

    我想用java构建一个SIP客户端 将向 SIP 注册器注册并能够调用另一个 sip 客户端的 java 类 有什么建议么 如果你愿意的话 你可以尝试我的软件电话 它提供了一个非常简单的API 它被称为同行 http peers sourc
  • 是否可以将 VoiP 呼叫转接至 GSM [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 是否可以使用 Android 手机作为简单的 GSM 网关 电话将使用 最好 Android 内置 SIP 堆栈接收 VoiP 呼叫
  • SIP 中会话、对话和事务之间的区别?

    会话 对话和事务之间到底有什么区别 所有人都必须一起出现吗 交易 事务由请求 收到的任何非最终 1xx 响应和最终响应 2xx 3xx 4xx 5xx 或 6xx 以及响应的确认 ACK 或 PRACK 组成 但以下内容除外 对 2xx 响
  • 如何在iPhone中实现VoIP+SIP? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我想为 iPhone 开发 VoIP 应用程序 但我不知道 VoIP 概念的基础知识 也不知道是否有任
  • Android SIP 堆栈 - 使用什么? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我需要在 Android 上创建使用 SIP 堆栈 它将与 asterix 一起使用 并使用户能够更改
  • 来电时无振铃事件

    我不知道哪里出了问题 听者听不懂onRinging事件 这样我就可以决定接受还是拒绝来电 清单中是这样的
  • 如何在 Windows Phone 7 中创建自定义文本框?

    是否可以通过创建自定义文本框来处理 sip 我想创建一个自定义文本框 gt 创建获得焦点事件 gt 在我的自定义文本框的焦点上而不是 SIP 上 我的自定义键盘应该打开 要求 如何创建自定义文本框 打开自定义键盘而不是 SIP 获取文本字段
  • 适用于 iPhone 和 Android 的 sip 堆栈

    我正在寻找适用于 Android 和 iPhone 的 SIP 堆栈 我发现了很多类似的问题 有时已经很老了 我不太关心解决方案是商业的 但这是首选 还是开源的 到目前为止我发现 RADVISION 的 Android 商业解决方案 iPh

随机推荐

  • 剑指offer40

    class Solution public void FindNumsAppearOnce vector
  • 单片机编程:软件定时器

    单片机软件在没有RTOS的情况下 可使用 软件定时器 Timer 它的作用类似OS的线程 从而大大简化程序设计 提高代码质量 软件定时器 设置函数的基本参数 定时时长 回调函数 指针 序号 内容 1 定时时长 interval ms 2 回
  • MCMC抽样算法要点总结

    MCMC抽样算法 目的 给定一个已知的概率分布函数 p x 对随机变量 x 进行采样 使其满足 p x 概率分布 原理 一个马尔科夫链 对应的概率转移矩阵为 P 如果其具有 非周期性 且任意两个状态之间都是 连通 的 则不论初始的状态概率分
  • 1000以内的所有回文数

    02 程序的版权和版本声明部分 03 Copyright c 2013 烟台大学计算机学院 04 All rights reserved 05 文件名称 test cpp 06 作 者 马德鹏 07 完成日期 2013 年11月18日 08
  • JavaWebSSM-购物商城系统(idea可用)

    软件工程课程设计 毕业设计之购物商城系统代码 基于javaSSM的购物商城系统 基于Thymeleaf的商品销售平台代码 前言 本次文章主要是介绍蛋糕商城系统的功能 系统分为两个角色 管理员 用户 一 系统功能 1 1 开发环境 开发语言
  • STM32 用cubemx移植IAP功能,实现串口升级

    感谢网上的大神 通过你们的文章我终于测试iap升级通过了 具体iap功能我白嫖一段 程序上电先进入 bootloader代码功能 后面通过bootloader跳转到用户的功能代码中 使用的开发板芯片 STM32F103VET6 串口1升级
  • 手动更新(rpi-update)树莓派固件

    手动更新 rpi update 树莓派固件 1 下载固件 本地更新 先在 PC 上下载固件 也可以用如下方法下载 curl L https github com Hexxeh rpi firmware archive master tar
  • Pipes【Codeforces 1234 C】【思维】

    Codeforces Round 590 Div 3 C 此题无坑 自己挖坑 本来比赛中应该A的代码 就因为我在N 1的时候加了一组特判 然后一直就WA2 后来发现Test 2是强数据 而我一直在怀疑我的思维错了 就一直没交了 最后这道14
  • 批处理学习教程(4)------for的用法

    循环 for 1 如果批处理不具备批量处理的功能 那么它就徒有虚名了 而命令 for 在某种意义上彻底体现出了批处理的强大快捷省事批量的作用 在看过 for 后 可以归纳出 for 大致可以分三种常用的类型 或者叫使用方法 从针对的循环目标
  • 检查 JavaScript 对象中是否存在键?

    问 如何检查 JavaScript 对象或数组中是否存在特定键 如果密钥不存在 而我尝试访问它 它会返回 false 吗 还是抛出错误 答1 保持自己快人一步 享受全网独家提供的一站式外包任务 远程工作 创意产品订阅服务 huntsbot
  • Bootstrap-Table简单使用

    Bootstrap Table Bootstrap table 是一款基于 Bootstrap 的 jQuery 表格插件 功能比较完备 能够实现数据异步获取 编辑 排序等一系列功能 最可贵的是 只需要一些简单的配置就可以实现一个功能完备的
  • IDE + ChatGPT,这款编辑器真的做到可以自动写代码了!

    前言 Cursor 是集成了 GPT 4 的 IDE 工具 目前免费并且无需 API Key 支持 Win Mac Linux 平台 可以按要求生成代码 或者让 AI 帮助优化代码 分析代码 Cursor目前已经集成了openai的GPT
  • 月入过万——网店推广实战方法(第2版)

    月入过万 网店推广实战方法 第2版 本书以淘宝网为例 结合各种案例全面介绍了网店推广宣传的各种方法 手把手教读者如何运用这 些方法来增加网店的客流量和成交量 这些方法也同样适合于拍拍等网店 对其内容详细阅读
  • C++ 每个学生的数据包括:学号、姓名、3门课的成绩...

    1 每个学生的数据包括 学号 姓名 3门课的成绩 3门课的平均分 3门课的总分是 学生类的数据成员 实现以下功能 1 从键盘输入10个学生数据 2 打印出10个学生各课程的平均分 3 输出总分最高分同学的所有信息 Student h inc
  • 电商APP开源

    电商APP开源 https github com myxh CoolShopping 效果demo CoolShopping 一个仿拉手团购的购物App 采用Bmob后台实现短信验证码注册 登录 收藏 订单管理 自动更新等功能 数据抓取自拉
  • 数据结构--排序之快速排序

    个人主页 你帅你先说 欢迎点赞 关注 收藏 既选择了远方 便只顾风雨兼程 欢迎大家有问题随时私信我 版权 本文由 你帅你先说 原创 CSDN首发 侵权必究 快速排序基本思想及其代码实现 快速排序是Hoare于1962年提出的一种二叉树结构的
  • C++连接Mysql查询结果中文乱码问题

    在写项目时需要用到mysql数据库 使用select查询时查询结果中文全部变为问号 但是在Sql中查询结果正常 解决方案 MYSQL my fd mysql init NULL msyql query my fd set names utf
  • 电子元器件学习笔记2:电容器

    电容器 1 概述 电容器是一个电子元件 用于存储电荷和能量 它由两个导体板和介质组成 介质层位于两个导体板之间 当电容接入电路时 负电荷被存储在导体板上 并在两板之间产生电场 电容器的单位是法拉 F 一个法拉等于存储一库伦电荷所需的电势差为
  • JAVA中 成员变量和和实例变量一样吗

    Java语言支持的变量类型有 局部变量 成员变量 类变量 不一样的 例如 public class A String id 实例变量 private String Tel 实例变量 private int size 实例变量 private
  • SIP与RTP综合应用(转)

    SIP是一个会话协议 很多大企业都在用 通信行业的一个标准 其业务逻辑比较 简单地来说如下 User Agent Server REGISTER gt lt 401 407 Unauthorized REG 带上用户口令 gt 200 OK