1. 程式人生 > >Android 開發 voip/sip 程式

Android 開發 voip/sip 程式

       首先說明一下相關概念,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 工程。如果不知道具體方法,可以百度。

建立好工程後開始配置買manifest檔案

為了使用SIP,需要新增以下許可權到你的manifest檔案:

§     android.permission.USE_SIP

§     android.permission.INTERNET

為了確保你的應用程式能夠安裝到支援SIP的裝置上,你需要新增以下內容到你應用程式的manifest檔案裡:

§     <uses-sdkandroid:minSdkVersion="9"/>.這個設定表明你的應用程式需要Android2.3或者更高版本的平臺。詳情請參考API Levels<uses-sdk>元素相關的文件。

為了控制你的應用程式被那些不支援SIP的裝置過濾掉(比如:在Google Play),你需要新增以下內容到你應用程式的manifest檔案裡:

§    <uses-featureandroid:name="android.hardware.sip.voip" />.這個設定聲明瞭你的應用程式用到了SIP API。這個宣告還應該包含一個android:required屬性來表明你是否想讓你的應用程式被那些不提供SIP支援的裝置過濾掉。其他<uses-feature>宣告你也可能需要,具體取決於你的實現,詳情請參考<uses- feature>元素相關的文件。

如果你的應用程式設計用來接受呼叫,那麼你還必須在應用程式的manifest檔案裡定義一個接收器(BroadcastReceiver的子類):

§    <receiverandroid:name=".IncomingCallReceiver"android:label="CallReceiver"/>

綜上,manifest檔案就出來具體如下

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

         package="com.example.android.sip">

    <applicationandroid:icon="@drawable/icon" android:label="SipDemo">

      <activityandroid:name=".WalkieTalkieActivity"

          android:configChanges="orientation|keyboardHidden">

            <intent-filter>

                <actionandroid:name="android.intent.action.MAIN"/>

                <categoryandroid:name="android.intent.category.LAUNCHER"/>

            </intent-filter>

        </activity>

        <activityandroid:name=".SipSettings"android:label="set_preferences"/>

        <receiverandroid:name=".IncomingCallReceiver" android:label="CallReceiver"/>

    </application>

    <uses-sdkandroid:minSdkVersion="9" />

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

    <uses-permissionandroid:name="android.permission.INTERNET" />

    <uses-permissionandroid:name="android.permission.VIBRATE" />

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

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

    <uses-permissionandroid:name="android.permission.RECORD_AUDIO" />

    <uses-featureandroid:name="android.hardware.sip.voip"android:required="true" />

    <uses-featureandroid:name="android.hardware.wifi"android:required="true" />

    <uses-featureandroid:name="android.hardware.microphone"android:required="true" />

</manifest>


配置檔案配置好以後 。就可以開始程式編寫了。首先得得到

一個SipManager物件,這個物件作用如下;

要想使用SIP API,你的應用程式需要建立一個SipManager物件,這個SipManager物件在你的應用程式裡負責以下內容:

1.發起SIP會話

2.發起和接受呼叫

3.在SIP provider裡進行註冊和登出

4.驗證會話的連通性

要提醒的是得到SipManage 物件並不是使用new 而是使用英文文件中的如下函式

使用以上函式就可得到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 的物件,該類的建構函式如下:

 

Constructor.(通過使用者名稱和伺服器地址構造)

三個都可以使用,但是常用的是第三個,(一般情況sip賬戶還有密碼,這時候還得使用

這函式設定密碼,其實builder 還有一些函式(包括設定埠號等資訊系),根據具體情況來使用。)。

得到Builder 物件後使用其

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使用例子。

還有如果各位想轉載的話請註明原帖地址(讓別人有問題時可以找到這裡,當然若您有信心解決讀者的問題,不註明也可以的)。

有什麼問題的話可以在評論處留言。我看見後會回覆的。