SIPDroid

SIPDroid
SIPDroid

TOP secret

Android SIPdroid Project Sharing May 23 2011

Warning: If you have any questions, advice or something else, please let

team know.

Fight Team

Contents

怎么学习android Sipdroid开源代码 (3)

Sipdroid流程详细分析 (4)

Sipdroid会话邀请流程 (一) (5)

Sipdroid会话邀请流程(二) (6)

Sipdroid中像服务器注册是如何实现的(一) (8)

Sipdroid 注册(二)-Sipdroid注册SIP消息分析 (10)

Sipdroid注册(三)-注册返回结果的监听 (13)

Sipdroid数据发送流程 (15)

Sipdroid中的视频通话是如何实现的 (18)

Sipdroid中涉及到的音频编解码库 (19)

Sipdroid涉及到的协议之RFC3261 (20)

怎么学习android Sipdroid开源代码?

Sipdroid目前有24个包,140多个java文件.首先学习Sipdroid,至少得明白Sipdroid是怎么玩的,实现的主要功能是什么,具体的流程操作是怎么执行的。

先整理下下面的问题:

一.Sipdroid的数据流程怎么发送的?

参考文档:https://www.360docs.net/doc/f45039883.html,/thread-5-1-1.html

二.Sipdroid中采用的是什么协议?

这点非常的重要,因为Sipdroid采用的是RFC3261协议,大家看了RFC3261协议后,你就会明白,Sipdroid中对于Message的封装是如何完成,那么Message的封装和生成这块所涉及到得设计模式和代码,你基本就可以弄明白

参考文档:

https://www.360docs.net/doc/f45039883.html,/thread-3-1-1.html

三.分块来弄Sipdroid

首先Sipdroid的架构真是设计的是非常的人性化以及牛X,为什么呢,功能块是完全独立和分开的,高内聚低耦合,Sipdroid本身来说就像一座非常难以攻克的堡垒,但是咱们只要一块一块的攻,不久就发现整做城堡都被搞定了。

一般来说,应该这么做:

1.研究注册流程

参考文档:https://www.360docs.net/doc/f45039883.html,/thread-35-1-1.html

2.研究会话邀请流程

3.视频通话通话流程

4.整理Sipdroid的架构

5.提取有用信息,比如视频和声音的编解码库,协议的封装和设计模式等

Sipdroid流程详细分析

1.SipDroid模块简单分析

UI层,负责界面显示模块和界面时间传递

Engine层:软件核心处理层,启动各种服务,初始化软件参数。处理各种UI时间,维持配置文件

信息,保存全局属性变量。其中包括UserAgenProfile(用户配置文件属性),UserAgent(用户事件

处理),SipdroidEngine(核心处理,所有的UI的事件,参数设计,服务的启动都由此文件调度),RegisterAgent(注册服务处理)

Sip层:提供所有sip消息模型,所有sip消息的处理流程,包括发送,接收,封装,解码等等网络传输:提供音频,视频信息的处理,此模块会对音频视频进行即时编码解码,同时发送数据。同时还提供JSTN NAT透传技术。

2.SipDroid流程分析:

拨号:拨号请求由UI通过Receiver广播信息交给SipDroidEngine处理,在SipDroidEngine做一些验证,然后交给UserAgine处理。其中call(String target_url, boolean send_anonymous)为判断是否匿名

拨号再此方法中,会创建一个ExtendedCall对象,以提供给sip协议栈使用,然后调用ExtendedCall

的call方法处理,之后由ExtendedCall的call,调用ExtendedInviteDialog来发送invite请求。由InviteDialog执行具体的invite调用InviteTransactionClient的request方法,最后由InviteTransactionClient发送request请求,在InviteTransactionClient中会通过SipProvider,来发送

消息,并打开对端口监听,来收sip消息。发送消息主要有(UDP传输 UdpTransport TCP传输TcpTransport)

来电:在启动程序的时候会通过SipDroidEngine会根据配置,创建一个Provider,这里用到udp传输,创建一个Udpprovider,udpprover是一个Thread,创建的时候就会启动线程,开始监听收到的信息,当受到信息后,通过onServiceTerminated方法,把对象传出到UdpTransport,由UdpTransport处理,再通过onTransportTerminated方法传出到SipProvider对象,最后将在SipProvider的processReceivedMessage方法中处理收到的消息,最后从listeners(map)中取出相应的listener,最后通过相应的listener中的OnReceiveMessage()来处理具体Sip请求

Sipdroid会话邀请流程 (一)

Sipdroid会话流程图:

图片比较直观,但是表述却不够详细,下面咱们详细分析下流程:

会话邀请所涉及到得类:

SipdroidEngine(call) - UserAgent(call) -ExtendedCall(call) - InviteDialog(invite)

左边的代表涉及到的类,右边代表涉及到的核心方法,从左到右进行观察,左边的类都有一个右边类型的参数作为自己的成员函数,就是SipdroidEngine有一个成员函数ua ,这个ua是UserAgent类型的。。。。

InviteDialog中的invite函数所做的事情也是非常的简单,生成会话邀请的message然后通过SipProvider发送出去就行了,那么发送完毕后,怎么实现对发送结果的监听呢?

其实自己猜测一下也猜测到服务器返回数据会什么类型的?

1、等待对方应答中

2.对方已经应答,进入双边通话模式中,同时手机这边开始声音和视频的采集.(声音和视频的采集,这个是核心中的核心哦,马上就要弄到这块了,兴奋啊)

3. 超时,对方无应答.

在二的分析中,咱们用倒推的方法来分析下客户端在接受到服务器的返回后,传递到了哪里去进行会话邀请的结果了,客户端是怎么实现的.

Sipdroid会话邀请流程(二)

这里咱们要弄明白的是,Sipdroid会话邀请的结果是在哪里进行监听的,以及这种效果是怎么实现的?

发送消息统一是由Sipdroid的sendMessage来进行发送的,然后返回的数据咱们可以在Sipdroid 的OnReceiveMessage中来进行获取。

在Sipdroid注册结果的监听:

https://www.360docs.net/doc/f45039883.html,/thread-44-1-1.html这篇文章中,咱们分析了下,首先会话的类型有很多种,注册会话,邀请会话,通话会话等等,因此在OnReceiveMessage中会对当前进行的会话进行判断,根据当前的会话ID,判断出当前具体的会话类型,然后具体的会话类型通过调用自己的成员函数(会话接口对象的接口方法)将数据会话接口对象.

那么分析就简单了,倒推下:

1.邀请会话类是谁?

2.邀请会话类得会话接口是哪个,邀请会话类会在获取到返回的数据后将获得到得数据传递给会话接口的接口方法中

3.分析会话接口中的方法是怎么处理邀请回话类返回的数据的?

只要咱们这样分析,咱们就可以倒推出最终数据是怎么处理的,处理数据的核心其实还是更新UI 界面和进行逻辑判断,所以最终的处理必须是距离UI界面近的类。

会话邀请类是:

InviteTransactionClient invite_tc = new InviteTransactionClient(sip_provider, invite_req, this);

nvite_tc.request();

SipPorvider在获取到数据后是怎么传给会话邀请类得?

每个具体的会话类都会继承一个抽象的会话类,这个抽象的会话类继承了SipProviderListener,在SipProvider获得message后,在OnReceiverMessage中,会通过执行SipProviderListener的接口方法将数据传给是实现了此接口的类,在会话邀请类中也就会传递给具体的会话类InviteTransaction.

每一个具体类在初始化的时候,是必须有SipProvider作为参数的,然后在通过SipProvider的

addSipProviderListener(id,SipProvierListner)指定会话类型和会话接口,还有SipProvider是唯一和独立的,因此一个会话中,只可能存在一种会话类型.

InviteTransactionClient在onReceiveMessage会将数据传递给

transaction_listener,transaction_listener其实就是InviteDialog,InviteDialog实现了TransactionClientListener,并在InviteTransactionClinet的时候传递进去了,所以数据都是按照这样的逻辑往上传的,剩下的分析就非常简单了,大家自己分析吧。

所以最终的数据还是会回到UserAgent里面CallListenerAdapter接口方法里面。

这个方法还挺巧的,谁发送的请求最后就由谁最终来处理这个返回的结果,然后呢,SipdroidEngined就是这些核心类用来交互的一个地儿,然后咱们在启动的Actiivity里面就只管调用SipdroidEngine提供的方法来控制指令就成了,牛X的框架啊。

RegisterAgent最先发送注册请求,最后的注册结果也得RegisterAgent来处理。

UserAgent最先发出会话邀请请求,最后的邀请结果也得UserAget来处理.

Sipdroid中像服务器注册是如何实现的(一)

关于Sipdroid是如何实现像服务器注册的?

首先告诉大家代码块在哪里,注册的核心在RegisterAgent类中的函数register(int time)中,注册的核

心是:

1.//Create message re (modified by mandrajg)

2.Message req = MessageFactory.createRegisterRequest(sip_provider,

3.target, target, new NameAddress(user_profile.contact_url), qvalue, icsi);

4.

5.req.setExpiresHeader(new ExpiresHeader(String.valueOf(expire_time)));

注册稍微简单一点,因为只是需要客户端和服务器进行绑定就行了,注册在SIP协议中称作登记服务,再向服务提供商进行注册的时候,服务提供商都会给我们一个URL,这个URL在Sipdroid中被封装

在SipURL中的,这个URL写的格式如下:

sip:20847236@https://www.360docs.net/doc/f45039883.html,,用户名+主机名。

登陆服务过程中的具体流程如下:

程序启动便自动进行初始化,在初始化的过程中,每隔一段的时间,客户端这边就要发送Register信

息给服务器,Register中的信息包含了服务提供商分配给当前用户的SIP URI(通俗点说,SIP URI 就

是客户端的标识,这个标识是必须是唯一的)

服务器通过获得当前用户的URI便可以绑定这个用户,然后写到一个数据库中,在SIP协议中,这个

叫做定位服务(location server)

发送的请求的格式如下:

F1 REGISTER Bob -> Registrar

REGISTER sip:https://www.360docs.net/doc/f45039883.html, SIP/2.0

Via: SIP/2.0/UDP https://www.360docs.net/doc/f45039883.html,:5060;branch=z9hG4bKnashds7

Max-Forwards: 70

To: Bob

From: Bob ;tag=456248

Call-ID: 843817637684230@998sdasdh09

CSeq: 1826 REGISTER

Contact:

Expires: 7200

Content-Length: 0

注册会在2小时后超时。注册服务器回应一个200OK,返回的信息包含如下: F2 200 OK Registar -> Bob

SIP/2.0 200 OK

Via: SIP/2.0/UDP https://www.360docs.net/doc/f45039883.html,:5060;branch=z9hG4bKnashds7

;received=192.0.2.4

To: Bob ;tag=2493k59kd

From: Bob ;tag=456248

Call-ID: 843817637684230@998sdasdh09

CSeq: 1826 REGISTER

Contact:

Expires: 7200

Content-Length: 0

这里面涉及到很多参数,但是在RFC3261中都有详细的介绍。

第二篇将给大家介绍下,发送的注册消息体里面的每个参数所代表的意义.

Sipdroid 注册(二)-Sipdroid注册SIP消息分析

发送注册的message的结构体如下:

F1 REGISTER Bob -> Registrar

REGISTER sip:https://www.360docs.net/doc/f45039883.html, SIP/2.0

Via: SIP/2.0/UDP https://www.360docs.net/doc/f45039883.html,:5060;branch=z9hG4bKnashds7

Max-Forwards: 70

To: Bob

From: Bob ;tag=456248

Call-ID: 843817637684230@998sdasdh09

CSeq: 1826 REGISTER

Contact:

Expires: 7200

Content-Length: 0

下面咱们分析下,这里面的数据? 里面的数据在RFC3261协议中已经有很清楚的说明了,只是比较的分散.

上面的数据称为:SIP消息,SIP协议是基于文本的协议,使用UTF-8字符集。

SIP消息的大致组成是按照下面的格式进行的.

一般消息= 起始行

*消息包头

CRLF //crlf也就是回车换行的意思

[消息正文]

起始行= 请求行/状态行

需要注意的是,每一行结束后都要加回车换行\r\n,并且回车换行只允许加到末尾,其余的位置不允许出现回车换行

请求分析:

Request-Line= Method SP Request-URI SP SIP-VERSION CRLF

//SP代表空格

这个是请求行的格式,Method定义了6中方法,REGISTER用于登记联系信息,INVITE, ACK, CANCEL用于建立会话,BYE用于结束会话

Request-URI就是注册商分配给你的唯一的标识符,服务器需要这个数据来于你进行绑定,

SIP-VERSION:请求和应答消息都包含的当前所有的SIP版本,一般写SIP/2.0就行了

后面的那些参数: TO FROM Cseq Call-ID这些字段在SIP消息中被称为头域,这些头域包含了路由用的核心信息,比如消息地址,事物的唯一标志等

To

RFC3261中的详细解释在8.1.1.2章节

To头域是第一个并且也是最先指定请求的逻辑接受地,或者是这个请求的用户或者资源的address-of-record(这个语句解释的有点抽象).

To 头域的写法SP 表示空格)

姓名(可用可不用) SP SIP URI

example:

To: 523851253

From

详细的说明文档请参加RFC3261 8.1.1.3章节

Fromm头域包含了请求发起者的逻辑标志,可以是用户的address-of-record.

From头域的主要作用是用来实现对请求的检查,同时让服务器选择一个规则对请求进行处理,同样From也可以包含一个请求人的姓名,值得注意的是,From还必须包含一个TAG,这个tag是UAC(用户代理客户端,Sipdroid中其实也就是sipdroid程序给产生的)自动给你产生的,tag的详细描述在RFC3261 19.3章节.

From格式:

From: 姓名(可有可无) SP ; tag=a48s CTLF(回车换行)

RFC3261协议中对于Call-ID写的已经是非常非常详细了,直接贴出来给大家看:

Call-ID是一个在一系列消息中,区分一组消息的唯一标志。在对话中的任一UA的所有请求和所有应答的Call-ID必须一致。在UA的每次注册中,都应该是一样的。在会话外的时候,UAC发起一个新的请求,这个Call-ID头域必须由UAC产生一个全局(在时间和空间上都是)唯一的Call-ID, 除非是请求头的方法(method)指明了别的产生方式。所有的SIP UA都必须保证自己产生的Call-ID不会和其他UA产生的Call-ID重复。注意,如果是请求的重新尝试,则重新尝试的请求不被当作一个新的请求,所以不需要新的Call-ID(重新尝试的请求例如:认证冲突等等)。(见8.1.3.5)

我们强烈建议用密码乱序随机串(RFC 1750[12])来产生Call-ID。实现中,可以用类似”localid@host”这样的格式产生。Call-ID是大小写敏感的,并且通过简单字节/字节的来进行比较。

采用密码乱序随机串可以降低会话被窃听的机会,并且降低Call-ID重复的冲突。不规定或者要求使用用户界面来选择输入Call-ID头域的值。参见20.8节

Cseq

组成格式:一系列的顺序号码+方法

Cseq 头域是用来区分和做位事务的顺序使用的。他由一个方法(method)和一系列的顺序号码组成。方法(method)必须和请求的方法一致。对于对话外的非REGISTER请求来说,顺序号码可以是任意的。这个顺序号码必须可以由32位的无符号整数表达,必须小于2^31。只要遵循了上述指导方针,客户端可以用任意的方法来产生这个Cseq头域。12.2.1.1节讲述了在对话中如何创建Cseq

例子:

Cseq: 4711 INVITE

Sipdroid注册(三)-注册返回结果的监听

看Sipdroid的源码,发送注册message后,是如何实现对注册返回结果进行监听的?

Sipdroid的数据发送流程是这样的:

https://www.360docs.net/doc/f45039883.html,/thread-5-1-1.html

先直接给大家说下结果:

对注册结果的监听是通过RegisterAgent来进行监听的,因为RegisterAgent实现了TransactionClientListener接口,通过

/**

* When the TransactionClient goes into the "Completed" state receiving a

* 2xx response

*/

public void onTransSuccessResponse(TransactionClient tc, Message resp);

函数即可实现对返回结果的监听。

那么按照Sipdroid的数据发送流程,最终获取到得结果是会传递到SipProvider中的onReceiverMessage()接口方法中,那么如何来实现将OnReceiverMessage中获取到的结果传递到RegisterAgent中呢?

这里其实还真的挺佩服涉及Sipdroid架构的哥们的,大量的运用回调和接口来进行数据的转发.

注册的流程是这样的:

RegisterAgent--register(int time)

register函数主要干两件事情,一是生成注册消息的message,另一个就是发送这个message,message可以看https://www.360docs.net/doc/f45039883.html,/thread-35-1-1.html这篇文章

message发送成功后,那么就得发送,发送是通过:

//TransactionClient t

t = new TransactionClient(sip_provider, req, this, 30000);

t.request();

来完成的,request函数中有两行代码非常重要:

sip_provider.addSipProviderListener(transaction_id, this);

connection_id = sip_provider.sendMessage(request);

sip_provider发送消息后,通过onReceiveMessage来接受消息,接受消息后就会进行一件事情的判断,通过判断发送message的transaction_id来判断此前发送的请求是用来注册,还是用来进行会要的邀

请呢?transaction_id其实就是一个标识,标识会话类型的,会话类型包括:注册,通话请求等等,然

后在onReceiveMessage中进行判断。

SipProvider中有如下的一个参数:

/** List of provider listeners */

Hashtable listeners = null;

listeners里面含有的对应关系为会话标识符----会话接口,比如注册过程就是注册会话标识符对应注册

会话接口,所以只要是会话类型那么必然要实现会话接口SiproviderListener,会话有很多种,比如注

册会话,通话会话,视频会话,那么Sipdroid的开发者就提取了他们的共同点,封装成了一个抽象的

会话类型:

public abstract class Transaction implements SipProviderListener, TimerListener;

具体的会话都必须继承Transaction,Transaction默认的已经继承了SipProviderListener。

所以呢,具体的数据最终会返回到具体的会话类中,注册的会话类是:TransactionClient,在TransactionClient中通过OnReceiverMessage就可以获得服务器返回的注册应答的数据,那么如果TransactionClient如何将应答的数据返回给RegisterAgent呢?

在TransactionClient初始化的时候

t = new TransactionClient(sip_provider, req, RegisterAgent.this, 30000);

RegisterAgent将自己作为TransactionClientListener传进了TransactionClient中,那么咱们就只需要

在TransactionClient的onReceiverMessage中通过调用TransactionListener的onTransProvisionalResponse(this, msg),数据就返回到了RegisterAgent呢。

理解了其实就非常的简单,大的脉络基本就不会偏差,以后所有的会话基采取的这样的一套模式,每

个会话都会继承Transaction,每隔具体的会话都有一个具体的会话接口的或者会话客户端的,大家沿

着这样的方向研究,框架基本就会弄的非常明白了,剩下的就是细节了.

Sipdroid数据发送流程

public class SipProvider implements Configurable, TransportListener

实现TransportListener的目的是什么?简单,为了实现回调,SipProvider实现了TransportListener,记住-这个很重要.

数据发送对应的流程对应如下,再这里假设message就是已经生成好了的(实际上的message涉及到很多的协议,搞了很长时候,这会还在犯晕)

SipdroidProvider----UdpTransport-UdpProvider-UdpSocket

UdpSocket拥有我们最直接的两个方法:send receive,方法原型如下:

/** Receives a datagram packet from this socket. */

public void receive(UdpPacket pkt) throws java.io.IOException {

DatagramPacket dgram = pkt.getDatagramPacket();

socket.receive(dgram);

pkt.setDatagramPacket(dgram);

}

/** Sends an UDP packet from this socket. */

public void send(UdpPacket pkt) throws java.io.IOException {

socket.send(pkt.getDatagramPacket());

}

UdpProvider拥有UdpSocket, 依托UdpSocket来实现数据的收发,但是receive是阻塞的。因此UdpProvider必然要继承thread来让自己成为线程:

/** The main thread */

public void run() {

byte[] buf = new byte[BUFFER_SIZE];

//UdpPacket是DataProgramPacket更深层次的一封装

UdpPacket packet = new UdpPacket(buf, buf.length);

Exception error = null;

long expire = 0;

if (alive_time > 0)

expire = System.currentTimeMillis() + alive_time;

try {

// socket.setSoTimeout(socket_timeout); modified

// loop

while (!stop) {

try {

socket.receive(packet);

} catch (InterruptedIOException ie) {

if (alive_time > 0 && System.currentTimeMillis() > expire)

halt();

continue;

}

if (packet.getLength() >= minimum_length) {

If (listener != null)

listener.onReceivedPacket(this, packet);///严重注意这里

if (alive_time > 0)

expire = System.currentTimeMillis() + alive_time;

}

packet = new UdpPacket(buf, buf.length);

}

} catch (Exception e) {

error = e;

stop = true;

}

is_running = false;

if (listener != null)

listener.onServiceTerminated(this, error);

listener = null;

}

UdpProvider中有一个UdpProviderListener,在UdpProvider进行初始化的时候便指定了

Siproid ---- UdpTransport – UdpProvider - UdpSocket(仔细看这个初始化的流程图,我给出它们的构造函数)

public UdpProvider(UdpSocket socket, UdpProviderListener listener) //UdpProvider构造函数

public UdpTransport(int port, TransportListener listener)//UdpTransProt构造函数,UdpTransport实现了UdpProviderListener

udp = new UdpTransport(host_port, host_ipaddr, Sipdroid.this);

所以流程是怎么样的呢?

客户端接收到服务器返回的数据后,首先是在UdpProvider的run里面的,上面的红色字体注意

没,UdpProvider会用UdpProviderListener进行回调,UdpProviderListener是谁呢,是UdpTransport,因为UdpTransport实现了UdpProviderListener接口,并在自己的构造函数中将自己作为参数传递给了UdpProvider.

好了,数据已经到了UdpTransport手里了,看下面的UdpTransport是如何实现的?

/** When a new UDP datagram is received. */

public void onReceivedPacket(UdpProvider udp, UdpPacket packet) {

Message msg = new Message(packet);

msg.setRemoteAddress(packet.getIpAddress().toString());

msg.setRemotePort(packet.getPort());

msg.setTransport(PROTO_UDP);

if (listener != null)

listener.onReceivedMessage(this, msg);

}

UdpTransport同样只需要调用onReceivedMessage就可以将数据传回给SipProvider呢, 这样SipProvider便获得了从服务器返回的信息, 然后程序在获得信息后要做的就是对Message解析, 并进行适配, 确定手机客户端这边怎么来响应。

通俗点说是上级将接口传递给下级,下级在获得数据后便通过该接口将数据返回给上级.

Sipdroid中的视频通话是如何实现的?

Sipdroid中的视频原理是怎么样的?

涉及到视频传输的类是org.sipdroid.sipua.ui中的

VideoCamera,VideoCameraNew,VideoCameraNew2,VideoPrewView。

首先呢,双边的视频观看,走的还是数据报包,有数据报包的ip和端口就行了。

那么发送视视频数据的方式和观看视频数据的方式是如何实现的呢?

发送视频数据就非常的巧妙了,在Android拍传的那篇文章中,采用的是获取预览帧的数据并进行发

送来实现的,

详细文档见:

https://www.360docs.net/doc/f45039883.html,/thread-7-1-1.html

这种方式的弊端就是预览帧的编码特别的耗费时间,流畅性差,而且对编码器的效率的依耐性比较高。

但是Sipdroid可以直接的从MediaRecord里面已经生成好的视频数据中提取出H264/H263的数据,

这些数据已经经过了相应的编码,具体的怎么实现提取H263/H264的,我会在后面说,非常牛X哦.

然后作为观看段呢?

怎么观看呢,其实观看就2行代码,

mVideoFrame.setVideoURI(Uri.parse("rtsp://"+Receiver.engine(mContext).getRemoteAddr()+"/"+

Receiver.engine(mContext).getRemoteVideo()+"/sipdroid"));

通过内置的videoview来通过RTSP来进行播放,那么也就是说服务器会将传递的RTP的视频数据流

封装成RTSP的流传递给手机的videoview来实现观看,同样也不需要解码库,真是涉及独到啊。

所以Sipdroid开源代码里只有声音的编码库,没有视频的编码库.

Sipdroid中涉及到的音频编解码库?

今天整理下Sipdroid中涉及到的音频和视频编解码库,首先呢咱们必须明白,Sipdroid中提供的可选的音频编码选项,和手机内置支持的音频编码方式。

Sipdroid中提供的音频编码格式一共有6种:

在org.sipdroid.codecs包中的Codecs类中:

Codecs的成员函数包含了所有的code类型,它们都继承于Codec

private static final Vector codecs = new Vector() {{

add(new G722());

// add(new SILK24()); save space (until a common library for all bitrates gets available?)

// add(new SILK16());

// add(new SILK8());

add(new alaw());//PCMA

add(new ulaw());//PCMU

add(new Speex());

add(new GSM());

add(new BV16());

}};

采集声音是由org.sipdroid.media包中的JAudioLauncher来完成的,JAudioLauncher继承于Thread,在run方法中会在通话状态下,不断的采集声音并进行声音的编码并发送,发送采用的协议是RTP协

议。

Sipdroid的开源项目中提供了4种音频格式的编码和解码库,这四种声音格式分别是G722,SPEEX, GSM,BV16,可惜的是,我的HTC2.2手机对这四种格式都不支持,一般采用的是PCMU也就是G711U 格式的声音源,关于PCMU的详细介绍,请参加文档:

https://www.360docs.net/doc/f45039883.html,/view/3409786.htm

语音通话过程中是通过UserAgent中的launchMediaAppication来进行最先发出的:

声音的编码格式应该是在设置的时候就进行指定和匹配好了,因为在launchMediaApplication中Codecs.Map c = Codecs.getCodec(local_sdp);

是直接获取的,Map里面就包含了Codecs,这个Codecs指定了具体的编码类,在我的HTC2.2中就是PCMU,在Sipdroid中对应的声音编解码格式就是org.sipdroid.codecs包中的ulaw类了,当然大家测试的时候也可以再设置的时候固定死音频的编码格式,这样好分析.

Sipdroid涉及到的协议之RFC3261

Sipdroid是不断的向服务器发送Message来实现和服务之间的通信的,发送统一是由

org.zoolu.sip.provider.SipProvider提供的sendMessage来实现的,因此在SipdroidEngine中的startEngine初始化整个项目中的核心类的时候,这些类的构造函数中都SipProvider这个参数. (设计的几个核心类包括:UserAgent RegisterAgent KeepAliveSip UserAgentProfile,这几个类中都拥有SipProvide参数,它们都指向的是同一SipProvider对象)

下面来扯下淡:

既然数据都是由SipProvider来进发送和接受的,那么很明显的,跟服务直接进行对接的任务理所当然的就应当由SipProvider来实现,UserAgent RegisterAgent。。。。。。所有的目的就只可能有一个了,对要发送的message来进行封装

(仅代表个人观点,没研究证实)

接受服务器返回的消息是通过SipdroidProviderListener中的onReceiveMessage()来实现的,添加SipdroidProviderListener只能通过SipProvider的addSipdroidListener()这个函数,而SipProvider在初始化的时候又作为参数传给了Sipdroid项目所设计的绝大多数核心类,因此这些核心类就能够直接实现SipProviderListener接口然后直接调用sipProvider.add(this)来实现自己对服务器返回数据的监控.

具体的怎么个实现法,目前还不是很清楚。

但是客户端在像服务器发送消息的时候,必然要设计一个东东-RFC3261,也就是服务器和客户端之间的通信协议,如果要实现发送我们自己定义的message,那么我们就得对照着RFC3261中的参数来看Sipdroid中的源码,这样才弄的明白,它里面包含的那么多的参数是什么意思。

Class Message supports all methods and header definened in RFC3261, plus。(Sipdroid源码中的message类的说明)

相关主题
相关文档
最新文档