Pro Android学习笔记(一二五):Telephony API(7):SIP Phone(下)

文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处http://blog.csdn.net/flowingflying以及作者@恺风Wei。

发起呼叫

发起呼叫,要确保客户端正常发送RTP包,需要增加两个新的权限:

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> 

发起呼叫的代码片段如下:

public SipAudioCall myCall = null;
public void onOutgoingCall(View v){
    if(myCall != null){
        myCall.close();
    }
 
    
    try{  //设置状态监听器
       SipAudioCall.Listener listener = new SipAudioCall.Listener(){
            @Override //通话建立(对方接通),即收到SIP INVITE的200OK消息,并回复ACK时触发
            public void onCallEstablished(SipAudioCall call) { 
                call.startAudio();  //在sip信令接通后,进行语音的处理
                call.setSpeakerMode(true);
 
                debug("onCallEstablished"); 
            }
            @Override //通话结束,指捕获通话建立后对方挂机,如果本方主动挂机,是不会触发此状态。如果对方拒听,可以在onError(SipAudaioCall call, int errorCode, String errorMessage)中获取,通过errorCode:-7和errorMessage:Temporarily Unavailable (480)。
            public void onCallEnded(SipAudioCall call) { 
                debug("onCallEnded");
            }             
        }; 

        String called = "[email protected]:54712"; //我们用Xlite作为SIP的另一个UA,无需通过软交换,直接填入该终端的地址。
        myCall = sipManager.makeAudioCall(mySipProfile.getUriString(), called, listener, 30);
    }catch(Exception e){
        debug("onOutgoingCall ERROR: "+ e.toString());
        e.printStackTrace();
    }

}

android.net.sip还提供了myCall.toggleMute()实现静音和非静音的切换,通过isMute()来检查当前是否是静音。静音时无RTP发送。SipDemo就是通过toogleMute()实现对讲的功能。

主动挂机的代码片段如下,包括通话后挂机(发送BYTE),以及建立呼叫的过程中终止(发送CANCEL)。

public void endCall(){
    if(myCall == null)
        return;

    try{
        myCall.endCall();  //停止通话           
    }catch(Exception e){
        debug("onEndCall ERROR: "+ e.toString());
        e.printStackTrace();           
    }

   myCall.close(); //关闭object,不能再用,释放object
    myCall = null;       
}

Pro Android学习笔记(一二五):Telephony API(7):SIP Phone(下)_第1张图片

我要补充的是,SDP协商为PCMU,抓包看到双方都有RTP了,但是有问题,不能听到对方说啥,XLite听到的是噪音,因此应有什么东西欠缺,具体如何处理Audio还是很有问题,这可能也与测试机型有关,所以这个sip包,我觉得不实用。

作为被叫

我们仍用XLite来模拟,将domain设置为我们的小例子的sip地址(要带端口号),不采用register方式,这样在没有软交换的情况下,XLite的拨出电话都会打到我们的小例子。

在上一次笔记中,自动register的方式如下,我们查看了sipManager的说明,没有发现其他方法可以携带PendingIntent,因此作为被叫,必须采用自动register的方式,在收到呼叫时,触发一个广播intent。

Intent i = new Intent(); 
i.setAction("cn.wei.flowingflying.mysipphone.INCOMING_CALL");
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA); 
sipManager.open(mySipProfile, pi, null);

我们需要在AndroidManifest.xml中声明接收器,也可以在代码中进行注册。根据小例子的特点,在代码中进行注册更为合适。

IncomingCallReceiver receiver = new IncomingCallReceiver(this);
…… 进行注册,例如在onCreate()中……
IntentFilter filter = new IntentFilter();
filter.addAction("cn.wei.flowingflying.mysipphone.INCOMING_CALL");
this.registerReceiver(receiver, filter);
……进行注销,例如在onDestroy()中….
this.unregisterReceiver(receiver);

接收器代码如下:

public class IncomingCallReceiver extends BroadcastReceiver{ 
    private MainActivity activity = null;  //主Activity的对象    
    private static boolean isEnd = false;
    public IncomingCallReceiver(MainActivity activity){
        this.activity = activity;
    }

    @Override
    public void onReceive(Context context, Intent intent) { 
        activity.debug("--------receiver--------"); 
        try{ 
            //设置监听器,准备进行状态检测,和呼出一样,能触发的对方的状态,而非本方
            SipAudioCall.Listener listener = new SipAudioCall.Listener(){ 
               @Override //实际不会触发到onRinging,这是对方Ringing(180)而非本方
                public void onRinging(SipAudioCall call, SipProfile caller) { 
                    activity.debug("onRing..............");
                }

                @Override //呼叫建立可触发,是在answerCall()(发送200OK)后收到ACK时触发
                public void onCallEstablished(SipAudioCall call) { 
                    activity.debug("onCallEstablished..............");
                }

                @Override //对方挂机时触发,包括接通和未接通的挂机
                public void onCallEnded(SipAudioCall call) { 
                    activity.debug("onCallEnded..............");
                    isEnd = true;
                    call.close(); 
                }

                @Override
                public void onError(SipAudioCall call, int errorCode,  String errorMessage) { 
                    activity.debug("onError : " + errorMessage);
                }                 
            };
            // 通过takeAudioCall()获得呼叫的session,并设置监测器 
            SipAudioCall incomingCall = activity.sipManager.takeAudioCall(intent, listener);
            Thread.sleep(2000); //模拟2秒后接通
            if(isEnd){ //对方可能已经在未接通前主动挂架,故要进行检查
                incomingCall.answerCall(30);  //接通,发送200OK,并有30秒时间等待ACK的应当
               incomingCall.startAudio();
                incomingCall.setSpeakerMode(true);

                activity.myCall = incomingCall;  //使得activity的UI的挂机可以操作
           }
        }catch(Exception e){
            activity.debug("INCOMING CALL ERROR : " + e.toString());
            e.printStackTrace();
        }         
    }

}

Pro Android学习笔记(一二五):Telephony API(7):SIP Phone(下)_第2张图片

对于被叫,andriod.net.sip不支持INVITE不带sdp方式,会报“seesion description missing in incoming call intent”的错误。

总的来讲android.net.sip高度封装,完成要给sip phone很容易,但是很多无法调整,在实际应用中会受到限制,基本上还是自己处理的为好。

相关链接:我的Android开发相关文章

你可能感兴趣的:(Pro Android学习笔记(一二五):Telephony API(7):SIP Phone(下))