首先说明一下相关概念,voip 和Sip ,voip的意思是网络电话,会话发起协议(SIP)是建立VOIP连接的IETF标准。SIP是一种应用层协议,用于和一个或多个参与者创建、修改和终止会话。SIP的结构与HTTP(客户-服务器协议)相似。客户机发出请求,并发送给服务器,服务器处理这些请求后给客户机发送一个响应)。
简单点就是voip是网络电话,而sip则是网络电话使用的协议。别的信息就请各位读者自己百度了。至于Android voip程序也就是写一个程序用于实现网络电话功能。
注意:在本篇博客中voip和 sip是一致的,笔者是不分的(原因是在android 2.3以后提供的api中是用sip表示voip相关接口的)
其实关于这个程序还得说明这里的Android 的版本。因为Android版本不同,开发方式也可能不同。voip程序,如果是运行在Android 2.3 以上系统,使用的属于Android 基本变成(简单调用编程api就可以),但是如果用户群体还包括Android 2.3 以前的系统。那就得换方案了,原因如下,因为Android 系统是在2.3以后才支持sip网络电话的,2.3以前就意味着没有对应的编程接口。只能自己实现了。
既然要自己实现,就必须先知道要实现哪些东西(或者说2.3以后和2,3之前到底在sip方面究竟是增加了哪些东西),进过笔者分析2.3和2.2代码的知2.3主要做的是在框架层实现了sip协议还有与sip相关的呼叫管理服务,简单点就是说2.3以后的系统提供了一个支持会话发起协议(SIP)的API,通过它就可以让应用轻松无需管理会话和传输层的沟通就可设置传出和传入的语音通话,或直接音频记录或播放。如果自己实现相关借口还得实现音频编解码。
现在总结一下,在android 2.3 以后的系统上开发voip程序要做的只是调用sip相关api就可以了。但是在Android 2.3 以前的系统上开发voip程序至少得完成如下几件事:
一.实现sip协议栈(在程序中把sip数据包封装好,发到网络上),或者实现别的完成同样功能的协议。
二.实现一个呼叫管理服务功能(例如 来电界面,拨号界面响铃界面等等,试具体功能需求而定)。
三.实现音频编解码(由于Android 的多媒体api中没有提供音频编解码的api,所以得自己实现,但是音频编解码是一个很庞大的事,并且现在有很多开源的音视频编解码库,常见的(笔者接触过的)有BroadVoice (音频编码的),ffmpeg(音视频编解码的),但是这些编解码库大多是以c语言写的。而Android 应用程序开发主要是java。所以在实现编解码音频时就得使用ndk开发(或者jni技术))。相信大家都知道了,开发voip程序在不同版本android上是不同的。而本篇博客主要是在2.3以后版本系统上开发voip 应用至于在2.3以前的开发的情况会在以后的博客中专门说明。
Android2.3 以后开发程序大致步骤如下
1.获取SipManager对象。
2.创建SipProfile对象(但是要先得到SipProfile.Builder 对象 ,并且在获取 Builder 对象时要传入sip账户信息),还得绑定监听器确定是否成功绑定;
3.有了前两个以后就可以拨号了(注意:另一个号也要是sip账号)。
4.使用广播接受器响应Sip呼叫广播(具体是响铃,唤醒屏幕)及设置过滤器。
既然要开发2.3以后的voip程序那就必须知道android系统提供的api相关类有哪些。具体如下:
SipAudioCall |
通过SIP处理网络音频电话 |
SipAudioCall.Listener |
关于SIP电话的事件监听器,比如接受到一个电话(on ringing)或者呼出一个电话(on calling)的时候 |
SipErrorCode |
定义在SIP活动中返回的错误代码 |
SipManager |
为SIP任务提供APIs,比如初始化一个SIP连接。提供相关SIP服务的访问。 |
SipProfile |
定义了SIP的相关属性,包含SIP账户、域名和服务器信息 |
SipProfile.Builder |
创建SipProfile的帮助类 |
SipSession |
代表一个SIP会话,跟SIP对话框或者一个没有对话框的独立事务相关联 |
SipSession.Listener |
关于SIP会话的事件监听器,比如注册一个会话(on registering)或者呼出一个电话(on calling)的时候 |
SipSession.State |
定义SIP会话的声明,比如“注册”、“呼出电话”、“打入电话” |
SipRegistrationListener |
一个关于SIP注册事件监听器的接口 |
至于 更详细的api相关信息 可以打开这个网页(android sdk api 开发文档英文版) http://docs.huihoo.com/android/2.3/reference/android/net/sip/package-summary.html
熟悉了api以后。就可以开始写程序了。首先呢是创建android 工程。如果不知道具体方法,可以百度。
创建好工程后开始配置买manifest文件
为了使用SIP,需要添加以下权限到你的manifest文件:
§ android.permission.USE_SIP
§ android.permission.INTERNET
为了确保你的应用程序能够安装到支持SIP的设备上,你需要添加以下内容到你应用程序的manifest文件里:
§
为了控制你的应用程序被那些不支持SIP的设备过滤掉(比如:在Google Play),你需要添加以下内容到你应用程序的manifest文件里:
§
如果你的应用程序设计用来接受呼叫,那么你还必须在应用程序的manifest文件里定义一个接收器(BroadcastReceiver的子类):
§
综上,manifest文件就出来具体如下
package="com.example.android.sip"> android:configChanges="orientation|keyboardHidden">
配置文件配置好以后 。就可以开始程序编写了。首先得得到
一个SipManager对象,这个对象作用如下;
要想使用SIP API,你的应用程序需要创建一个SipManager对象,这个SipManager对象在你的应用程序里负责以下内容:
1.发起SIP会话
2.发起和接受呼叫
3.在SIP provider里进行注册和注销
4.验证会话的连通性
要提醒的是得到SipManage 对象并不是使用new 而是使用英文文档中的如下函数
static SipManager |
newInstance(Context context) Creates a manager instance. |
使用以上函数就可得到SipManage对象。然后还得在Sip服务器上注册(一个典型的Android SIP应用中包含一个或多个用户,他们中的每个人都有一个SIP账户),
在Android SIP应用中,每一个SIP账户代表一个SipProfile对象。
一个SipProfile对象定义了一个SIP的概要文件,包括SIP账户、域名和服务器信息。跟正在这个设备上运行应用的SIP账户相关联的概要文件被称之为本地配置文件。与会话相连接的概要文件被称之为对应配置文件。当你的SIP应用通过本地SipProfile登录到SIP服务器的时候,这就有效的注册当前设备为基站来发送SIP呼叫到你想呼叫的SIP地址。所以呢,接下来要做的就是创建一个SipProfile,以及如何把刚创建的SipProfile注册到SIP服务器上,并且跟踪注册事件。要的到SipProfile对象,使用的是SipProfile.Builder这个类的函数,也就是说得先创建Builder 的对象,该类的构造函数如下:
SipProfile.Builder(SipProfile profile) Creates a builder based on the given profile.(拷贝构造) |
|
||
|
|
||
|
SipProfile.Builder(String username, StringserverDomain)
Constructor.(通过用户名和服务器地址构造)
三个都可以使用,但是常用的是第三个,(一般情况sip账户还有密码,这时候还得使用
SipProfile.Builder |
setPassword(String password) Sets the password of the SIP account |
这函数设置密码,其实builder 还有一些函数(包括设置端口号等信息系),根据具体情况来使用。)。
得到Builder 对象后使用其
SipProfile |
build() Builds and returns the SIP profile object. |
就可以得到SipProfile对象。接着就得开始处理被呼叫时的反应了。用于呼出电话和/或接收通用的SIP电话。呼叫器可以通过mSipManager.makeAudioCall来呼出后续电话。这段摘录同样设置了一个android.SipDemo.INCOMING_CALL行动,这个行动会被一个intent过滤器来使用,当前设备接收到一个呼叫(见Setting up an intent filter to receive calls)。
Intent intent = newIntent();
intent.setAction("android.SipDemo.INCOMING_CALL");
PendingIntent pendingIntent=PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA);
mSipManager.open(mSipProfile,pendingIntent,null);
还得绑定监听器用于检测sip用户的注册结果及错误信息
mSipManager.setRegistrationListener(mSipProfile.getUriString(),new SipRegistrationListener() {
public void onRegistering(StringlocalProfileUri) {
updateStatus("Registering with SIPServer...");
}
public void onRegistrationDone(StringlocalProfileUri, long expiryTime) {
updateStatus("Ready");
}
public void onRegistrationFailed(StringlocalProfileUri, int errorCode,
String errorMessage) {
updateStatus("Registrationfailed. Please checksettings.");
}
最后还得释放掉(关闭)profile及在服务器上注销该账户 。
mSipManager.close(mSipProfile.getUriString());
就可以了。
到这里的话 程序里就会存在一个正常的记录用户信息的Profile对象(若这个对象绑定的监听器提示注册失败,就说明你的信息错误(可能是用户名,密码,服务器地址或者端口号等等,这时只能在核对信息了)及一个SipManager对象。)这时就可以拨打开始语音电话部分了。
要想拨打一个语音电话,要建立一个SipAudioCall.Listener监听器。大部分客户与SIP堆栈的交互都是通过监听器来发生的。在这一小段你将会看到SipAudioCall.Listener监听器是如何在呼叫制定之后建立事务的:
SipAudioCall.Listener listener= newSipAudioCall.Listener() {
@Override
publicvoidonCallEstablished(SipAudioCall call) {
call.startAudio();
call.setSpeakerMode(true);
call.toggleMute();
}
@Override
publicvoidonCallEnded(SipAudioCall call) {
// Dosomething.
}
};
一旦你创建了这个SipAudioCall.Listener监听器,你就可以拨打电话了,SipManager对象里的makeAudioCall方法接受以下参数:
一个本地SIP配置文件(呼叫方)
一个相对应的SIP配置文件(被呼叫方)
一个用来监听从SipAudioCall发出的呼叫事件的SipAudioCall.Listener,这个参数可以为null,但是如上所说,一旦呼叫电话制定,这个监听器将被用来创建事务
超时的值,以秒为单位
例如:
call=mSipManager.makeAudioCall(mSipProfile.getUriString(), sipAddress,listener,30);
这样拨号过程中的信息就可以在listener中得到了。
现在拨号已没问题了。
接下来就要处理被呼叫的情况了。首先得知道android系统在有呼叫过程发生时是通过广播的形式发送有来电的消息,所以为了接收呼叫,你的SIP应用必须实现BroadcastReceiver的子类。当Android系统接收到一个呼叫的时候,他会处理这个SIP呼叫,然后广播一个来电intent(这个intent由系统来定义),
如下就是代码:
public class IncomingCallReceiverextends BroadcastReceiver {
/**
* Processes the incoming call, answers it,and hands it over to the
* WalkieTalkieActivity.
* @param context The context under whichthe receiver is running.
* @param intent The intent being received.
*/
@Override
public void onReceive(Context context,Intent intent) {
SipAudioCall incomingCall = null;
try {
SipAudioCall.Listener listener =new SipAudioCall.Listener() {
@Override
public voidonRinging(SipAudioCall call, SipProfile caller) {
try {
call.answerCall(30);
} catch (Exception e) {
e.printStackTrace();
}
}
};
WalkieTalkieActivity wtActivity =(WalkieTalkieActivity) context;
incomingCall =wtActivity.manager.takeAudioCall(intent, listener);
incomingCall.answerCall(30);
incomingCall.startAudio();
incomingCall.setSpeakerMode(true);
if(incomingCall.isMuted()) {
incomingCall.toggleMute();
}
wtActivity.call = incomingCall;
wtActivity.updateStatus(incomingCall);
} catch (Exception e) {
if (incomingCall != null) {
incomingCall.close();
}
}
}
}
这样呢当这个广播接收器收到广播时,就会唤醒手机以及响铃。但是只有这个还不够,还要为广播监听器进行广播过滤,(因为android 系统不仅发送来电的广播,还有很多别的广播(例如启动完成以后,电量变化等等 ))。
设置过滤器代码如下:
Intent intent = newIntent();
intent.setAction("android.SipDemo.INCOMING_CALL"); //sip电话呼叫
PendingIntent pendingIntent=PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA);
mSipManager.open(mSipProfile,pendingIntent,null); //打开SipManager。
这时候 就可以接受呼叫了。
这样一个Android voip程序就好了。
特别说明:本篇博客来源于android sdk 19 sample代码中的SipDemo的例子,这是google官方提供的sdk使用例子。
笔者也将那个例子上传到csdn http://download.csdn.net/detail/huang_rong12/9504286 这是免积分的,这个工程导入eclipse 后就可以运行的。
还有如果各位想转载的话请注明原帖地址(让别人有问题时可以找到这里,当然若您有信心解决读者的问题,不注明也可以的)。
有什么问题的话可以在评论处留言。我看见后会回复的。